Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c56b41bf47 | ||
|
|
484c7a98a8 | ||
|
|
fdf21c71bb | ||
|
|
f03453f7c5 | ||
|
|
9ebdb107fa | ||
|
|
057d5df6da | ||
|
|
188a226165 | ||
|
|
9aba824be0 | ||
|
|
a958be45dd | ||
|
|
7fd0375283 | ||
|
|
dc2879e2ff | ||
|
|
7fa75b8090 | ||
|
|
5e58aaf199 | ||
|
|
54231e2d74 | ||
|
|
0042835842 | ||
|
|
a53d48a6e3 | ||
|
|
d3ffc9eed5 | ||
|
|
dcfa809b9e | ||
|
|
fa31c2ef11 | ||
|
|
7c28938ba6 | ||
|
|
0a426b84e3 | ||
|
|
45f5e485d3 | ||
|
|
853f8d2e84 | ||
|
|
d1784355d0 | ||
|
|
f045d272c1 | ||
|
|
faa6528400 | ||
|
|
be33ecc798 | ||
|
|
37f3550831 | ||
|
|
e2d454da1e | ||
|
|
7af2467713 | ||
|
|
d8e203264d | ||
|
|
4a0cefd240 | ||
|
|
e5aef7ee5b | ||
|
|
9f2ac024cb | ||
|
|
92a8cb507a | ||
|
|
ea87dcb52a | ||
|
|
467a66565d | ||
|
|
a52b477b0c | ||
|
|
dbd0bfed10 | ||
|
|
ee25d3ed9a | ||
|
|
d662a60f6a | ||
|
|
c0a80182e6 | ||
|
|
4360a1154c | ||
|
|
1cb6066aea | ||
|
|
e85d7b5d0c | ||
|
|
143f660f37 | ||
|
|
4107ac093b | ||
|
|
c90752b4f2 | ||
|
|
f97ccf249f | ||
|
|
2d3e942f93 | ||
|
|
d965674ff5 | ||
|
|
a174336baf | ||
|
|
746e858f76 | ||
|
|
48702ce266 | ||
|
|
8ba354eda8 | ||
|
|
74b18cf71a | ||
|
|
65d3b858b3 | ||
|
|
1e8fd9589b | ||
|
|
c331c8dde5 | ||
|
|
21a47e2d43 | ||
|
|
8cbe6d7199 | ||
|
|
58481c9b17 | ||
|
|
1b5256d699 | ||
|
|
1e64f930d9 | ||
|
|
9630851f1f | ||
|
|
46878f009a | ||
|
|
ef77826042 | ||
|
|
344f82dc8f | ||
|
|
bc362b9778 | ||
|
|
f17b2cb06d | ||
|
|
1d5878e2f5 | ||
|
|
42cfba5ccc | ||
|
|
9e178cf4a1 | ||
|
|
74cac5815c | ||
|
|
b47055d814 | ||
|
|
1a760a579a | ||
|
|
f6b5635715 | ||
|
|
1a7d0d75a8 | ||
|
|
e41a9351c2 | ||
|
|
7a1ffa1ea9 | ||
|
|
db34468bc4 | ||
|
|
f5d7ea9a47 | ||
|
|
dc8c0df0a4 | ||
|
|
b3d3071316 | ||
|
|
88e352457f | ||
|
|
f1bb6af757 | ||
|
|
97a1cf0162 | ||
|
|
1821ff8cc7 | ||
|
|
dd397987ca | ||
|
|
9b2b320255 | ||
|
|
655a89289a | ||
|
|
911cf8a19c | ||
|
|
7d9aaeac55 | ||
|
|
6546ee531f | ||
|
|
9ea0e214b6 | ||
|
|
0ffe8ba972 | ||
|
|
41eb89ed35 | ||
|
|
9ae087eca1 | ||
|
|
08d8dcd53f | ||
|
|
1a279298fd | ||
|
|
479eba940e | ||
|
|
4aa64a7b5d | ||
|
|
459d097239 | ||
|
|
188616d1ce | ||
|
|
9ef1fdb795 | ||
|
|
e241d13edc | ||
|
|
df7d791ba1 | ||
|
|
b0db7ad4b3 | ||
|
|
a29731cb3c | ||
|
|
9b187797f8 | ||
|
|
56ba30e7f4 | ||
|
|
f776099c98 | ||
|
|
2825ad833f | ||
|
|
795915fddc | ||
|
|
1ff0fe0208 | ||
|
|
6d03493bfb | ||
|
|
080c8cdaa6 | ||
|
|
6db1aeea3f | ||
|
|
a6214a47b5 | ||
|
|
2da1644f8e | ||
|
|
e15764eb2b | ||
|
|
08a6daab00 | ||
|
|
ef03b87357 | ||
|
|
0f339f603b | ||
|
|
49796a5c33 | ||
|
|
51cc64f031 | ||
|
|
42b4345485 | ||
|
|
20d77d1cf3 | ||
|
|
9dd6fc8832 | ||
|
|
3a5f6b331d | ||
|
|
d09dbb9422 | ||
|
|
81afd71d84 | ||
|
|
da21c57cca | ||
|
|
d6fafee64c | ||
|
|
0ed884a5ab | ||
|
|
1822ed6aa2 | ||
|
|
eabfb6a695 | ||
|
|
58d0a3c688 | ||
|
|
8b3d4ba9aa | ||
|
|
8ff94037eb | ||
|
|
065f667a79 | ||
|
|
f61b69f6bd | ||
|
|
9ec85c324c | ||
|
|
5ab506e9cd | ||
|
|
e51d7de309 | ||
|
|
28307b0681 | ||
|
|
5af68ccfd0 | ||
|
|
4002f80ede | ||
|
|
65915a9784 | ||
|
|
7259397e02 | ||
|
|
281b4e10b0 | ||
|
|
b17a0b1eea | ||
|
|
d7e2c87830 | ||
|
|
22b1bfa777 | ||
|
|
4ea9c6c28d | ||
|
|
2cb5cddb91 | ||
|
|
cc0dd649ea | ||
|
|
49e24804f7 | ||
|
|
7958ce236f | ||
|
|
f3c55f0981 | ||
|
|
dc9df4af09 | ||
|
|
add6f99189 | ||
|
|
37c6b5d46d | ||
|
|
0ff7adf73b | ||
|
|
3ffc130889 | ||
|
|
69d75292ee | ||
|
|
d6539348ea | ||
|
|
4d00be1ab5 | ||
|
|
505b01cdcb | ||
|
|
e8b210913c | ||
|
|
2e10e997b0 | ||
|
|
530975d9de | ||
|
|
f560a9e603 | ||
|
|
d20e937a79 | ||
|
|
b7a99047ed | ||
|
|
98eb65c99f | ||
|
|
d1f346b28c | ||
|
|
39bf6ebaa5 | ||
|
|
9011df884e | ||
|
|
92490e1a75 | ||
|
|
8ee6d6d860 | ||
|
|
543dfa8509 | ||
|
|
a8bc7a071e | ||
|
|
02071c1289 | ||
|
|
ce80e12451 | ||
|
|
a04df917d4 | ||
|
|
0a7d8f931b | ||
|
|
77105029d3 | ||
|
|
5fe0b0c58b | ||
|
|
f77f7b091d | ||
|
|
4f9111b8a0 | ||
|
|
94cda9a09b | ||
|
|
29afa7f3b1 | ||
|
|
45283593ce | ||
|
|
00d91353a4 | ||
|
|
7944338516 | ||
|
|
77854a3cf0 | ||
|
|
d29ec26beb | ||
|
|
d64810e234 | ||
|
|
f3d2c9e5b9 | ||
|
|
63ac084a17 | ||
|
|
29f7c24a00 | ||
|
|
58c298fed8 | ||
|
|
ee825f76c7 | ||
|
|
dfb33efd45 | ||
|
|
dd7ebc54a2 | ||
|
|
8f99d8d9ca | ||
|
|
c411a0c792 | ||
|
|
bc8513778b | ||
|
|
501dc37e94 | ||
|
|
c58b5fb0e6 | ||
|
|
f89f2b8ad0 | ||
|
|
5e3e85641d | ||
|
|
4a5f71e7f2 | ||
|
|
92d7503528 | ||
|
|
773f3db976 | ||
|
|
dd5b489e8f | ||
|
|
7bda1cc1cc | ||
|
|
58e88812b1 | ||
|
|
db80631901 | ||
|
|
593944266d | ||
|
|
b8883fc8ae | ||
|
|
efac4a6077 | ||
|
|
cb22d21f70 | ||
|
|
f6b17d9541 | ||
|
|
7a1298e015 | ||
|
|
0a81521e6e | ||
|
|
8771281954 | ||
|
|
b50381d386 | ||
|
|
91345cf9d2 | ||
|
|
19b3ba6615 | ||
|
|
6b68029b6d | ||
|
|
15c8a8bf28 | ||
|
|
2b2fe02315 | ||
|
|
1f4c91a344 | ||
|
|
92f8f74d8f | ||
|
|
ad2869ea6f | ||
|
|
979506d5be | ||
|
|
bc6e792855 | ||
|
|
5df90f27c8 | ||
|
|
d571642454 | ||
|
|
0695ddc33e | ||
|
|
c97e7675fd | ||
|
|
526805680a | ||
|
|
03e3549895 | ||
|
|
ebbc48771b | ||
|
|
2d6fb5c15c |
No files matched your search
@@ -24,5 +24,7 @@ project.lock.json
|
||||
src/server/sparkd/orderman/perf_analysis/data
|
||||
src/server/sparkd/orderman/perf_analysis/logs/
|
||||
src/server/sparkd/orderman/perf_analysis/trace_data/
|
||||
.fake
|
||||
*.fsx.lock
|
||||
src/lib/orderman/perf_analysis/logs/latency_data/
|
||||
trace_data/
|
||||
Vendored
+1
-1
@@ -198,7 +198,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": ["test", "[md]"],
|
||||
"args": ["test","Co2RelayTests"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
1.3.5
|
||||
- Change in market data wire format. Old shell will get only direct market data instead of merged which may not match what the servers is driving from.
|
||||
|
||||
1.3.4
|
||||
- Changed default logging config for Windows. If Logging config is overridden for a system it should be adjusted to override only linux.
|
||||
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
1.3.5
|
||||
- Speed Enhanments
|
||||
- Further speed enhancements
|
||||
- Net Change pricing
|
||||
- The ability to price synthetics based off of net change
|
||||
- Separation of implied and direct market data
|
||||
- Viewing within a market view will now show 3 columns
|
||||
- Direct
|
||||
- Implied
|
||||
- Merged
|
||||
- Ability to configure the market data size in a ladder as the following
|
||||
- Direct
|
||||
- Implied
|
||||
- Merged
|
||||
- Synthetic creation option to choose Merged or Direct
|
||||
- The ability to choose Merged or Direct per leg when creating a synthetic
|
||||
- Volume at price
|
||||
- Within a ladder you can now see a volume at price graph
|
||||
- This is configurable from a ladder's settings as well
|
||||
- - Volume at price
|
||||
- Within a ladder you can now see a volume at price graph
|
||||
- This is configurable from a ladder's settings as well
|
||||
- Customized settings in all windows
|
||||
- All windows now contain a settings parge where grid settings (font color, size, etc) are applied
|
||||
- These settings can be saved on a per-window basis or as a Default for newly-launched windows
|
||||
|
||||
1.3.4
|
||||
- Significantly reduced client memory use
|
||||
- Improved OrderBook performance
|
||||
|
||||
+59
-55
@@ -4,7 +4,7 @@ VisualStudioVersion = 15.0.27130.2024
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
|
||||
EndProject
|
||||
@@ -12,11 +12,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sparkcmd", "src\client\spar
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{9AD1A867-93A9-301D-BF77-0113E18E9E39}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3} = {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C} = {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{ECD78CC5-138F-4809-A764-2358C2AC2E82}"
|
||||
@@ -57,22 +57,22 @@ Global
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.ActiveCfg = Release|x64
|
||||
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.Build.0 = Release|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x64.Build.0 = Debug|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x64.ActiveCfg = Release|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x64.Build.0 = Release|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x86.ActiveCfg = Release|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x64.Build.0 = Debug|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x64.ActiveCfg = Release|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x64.Build.0 = Release|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x86.ActiveCfg = Release|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
|
||||
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.Build.0 = Debug|x64
|
||||
@@ -93,26 +93,30 @@ Global
|
||||
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x86.ActiveCfg = Release|x64
|
||||
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x86.Build.0 = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.Build.0 = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.Build.0 = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.ActiveCfg = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.Build.0 = Release|x64
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.Build.0 = Debug|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.Build.0 = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
|
||||
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.Build.0 = Debug|x64
|
||||
@@ -133,22 +137,22 @@ Global
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.ActiveCfg = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.Build.0 = Release|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x64.Build.0 = Debug|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x64.ActiveCfg = Release|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x64.Build.0 = Release|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x86.ActiveCfg = Release|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x64.Build.0 = Debug|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x64.ActiveCfg = Release|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x64.Build.0 = Release|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x86.ActiveCfg = Release|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.Build.0 = Debug|x64
|
||||
|
||||
@@ -19,14 +19,14 @@ namespace mbts.spark {
|
||||
IReadOnlyDictionary<uint, string>
|
||||
_account_map = new Dictionary<uint, string>(),
|
||||
_user_map = new Dictionary<uint, string>();
|
||||
readonly BehaviorSubject<AdminModelWrapper> _wrapper_subject = new BehaviorSubject<AdminModelWrapper>(null);
|
||||
readonly BehaviorSubject<AdminModelWrapper> _wrapper_subject = new BehaviorSubject<AdminModelWrapper>(new AdminModelWrapper());
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
AdminModel(AdminService service, [Import("username")]string user_name) {
|
||||
_user_name = user_name;
|
||||
_dispose = service.subscribei_admin_data().Select(update => {
|
||||
_wrapper_subject.OnNext(new AdminModelWrapper(update.all.FirstOrDefault(), _user_name));
|
||||
_wrapper_subject.OnNext(new AdminModelWrapper(update.all.TryFirst(), _user_name));
|
||||
IEnumerable<BTSUserImmutable> active_users =
|
||||
from admin_data in update.all
|
||||
from bts_user in admin_data.Users
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace mbts.spark
|
||||
public AdminModelWrapper(AdminDataImmutable? admin_data = null, string user_name = null)
|
||||
{
|
||||
_admin_data = admin_data;
|
||||
_user_name = user_name;
|
||||
_user_name = user_name ?? string.Empty;
|
||||
_allowed_accounts = search_admin(u => u.Name == _user_name).Select(x => x.Item1).ToList();
|
||||
foreach(var val in _allowed_accounts)
|
||||
_allowed_account_dict[val.Name] = val.Name;
|
||||
@@ -30,7 +30,7 @@ namespace mbts.spark
|
||||
}
|
||||
public bool can_view_account(string account)
|
||||
{
|
||||
return _allowed_account_dict.ContainsKey(account);
|
||||
return _allowed_account_dict.ContainsKey(account) || _user_name.ToLower() == "bts";
|
||||
}
|
||||
|
||||
IEnumerable<Tuple<BTSAccountImmutable, TradingPermissionUnitImmutable>> search_admin(Func<BTSUserImmutable, bool> predicate)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using bts.api.admin;
|
||||
using bts.api.core;
|
||||
using bts.api.md.auxiliary;
|
||||
using bts.api.orderman;
|
||||
using bts.api.persistence;
|
||||
using bts.api.position;
|
||||
@@ -40,5 +41,8 @@ namespace Spark {
|
||||
|
||||
[Export]
|
||||
public PositionService position_service => context.locate<PositionService>();
|
||||
|
||||
[Export]
|
||||
public MarketDataAuxiliaryService md_auxiliary_service => context.locate<MarketDataAuxiliaryService>();
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace mbts.ui {
|
||||
|
||||
static readonly string base_uri = "pack://application:,,,/Images/Icons/";
|
||||
private static ResourceDictionary _resources = (ResourceDictionary)Application.LoadComponent(new Uri("/Images/Vector/Icons.xaml", UriKind.RelativeOrAbsolute));
|
||||
|
||||
|
||||
private static Uri uri(string filename) {
|
||||
return new Uri(base_uri + filename);
|
||||
}
|
||||
@@ -94,12 +94,12 @@ namespace mbts.ui {
|
||||
public static ImageSource CancelBuys { get { return load_resource_and_freeze("CancelBuys"); } }
|
||||
public static ImageSource CancelSells { get { return load_resource_and_freeze("CancelSells"); } }
|
||||
public static ImageSource CancelSelected { get { return load_resource_and_freeze("CancelSelected"); } }
|
||||
|
||||
|
||||
public static ImageSource LadderCancelOrders { get { return load_resource_and_freeze("LadderCancelOrders"); } }
|
||||
public static ImageSource LadderCancelBuys { get { return load_resource_and_freeze("LadderCancelBuys"); } }
|
||||
public static ImageSource LadderCancelSells { get { return load_resource_and_freeze("LadderCancelSells"); } }
|
||||
|
||||
public static ImageSource Synthetics { get { return load_resource_and_freeze("Synthetics"); } }
|
||||
public static ImageSource Synthetics { get { return load_resource_and_freeze("Synthetics"); } }
|
||||
public static ImageSource AddSynthetic { get { return load_resource_and_freeze("AddSynthetic"); } }
|
||||
public static ImageSource RemoveSynthetic { get { return load_resource_and_freeze("RemoveSynthetic"); } }
|
||||
public static ImageSource ModifySynthetic { get { return load_resource_and_freeze("ModifySynthetic"); } }
|
||||
@@ -126,7 +126,10 @@ namespace mbts.ui {
|
||||
public static ImageSource Wrench { get { return load_resource_and_freeze("Wrench"); } }
|
||||
public static ImageSource RightArrow { get { return (load_resource_and_freeze("RightArrow")); } }
|
||||
public static ImageSource DownArrow { get { return (load_resource_and_freeze("DownArrow")); } }
|
||||
public static ImageSource Support => load_resource_and_freeze("SupportIcon");
|
||||
public static ImageSource Factory { get { return (load_resource_and_freeze("Factory")); } }
|
||||
public static ImageSource DotsVertical { get { return (load_resource_and_freeze("DotsVertical")); } }
|
||||
public static ImageSource HandPointingOverDocument => load_resource_and_freeze("HandPointingOverDocument");
|
||||
public static ImageSource Support => load_resource_and_freeze("SupportIcon");␍
|
||||
|
||||
public static ImageSource create_icon(string geometric_path_key) =>
|
||||
create_icon(geometric_path_key, Brushes.Black);
|
||||
|
||||
@@ -813,8 +813,48 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="Factory" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing
|
||||
Brush="Black"
|
||||
Geometry="M21.832001,24.424L21.832001,27.912998 26.218018,27.912998 26.218018,24.424z M13.757019,24.424L13.757019,27.912998 18.143005,27.912998 18.143005,24.424z M5.7820129,24.424L5.7820129,27.912998 10.167999,27.912998 10.167999,24.424z M1.8950195,6.3799964L3.8880005,6.3799964 4.1870117,17.745001 8.1750183,17.745001 8.1750183,13.956 16.050018,17.745001 16.149994,17.745001 16.149994,13.956 24.024994,17.745001 24.125,17.745001 24.125,13.956 32,17.745001 31.901001,17.745001 31.901001,31.103001 0,31.103001 0,17.745001 1.5950012,17.745001z M8.5740051,1.3959956C8.5740051,1.3959958 9.1719971,3.2899927 6.6800232,3.5889963 6.6800232,3.5889963 5.085022,3.6879953 4.2870178,4.4859993 4.2870178,4.4859993 3.9880066,4.7849952 3.8880005,4.4859993 3.8880005,4.4859993 3.9880066,3.0899958 6.480011,2.6919931 6.5800171,2.7909998 8.3740234,2.6919931 8.5740051,1.3959956z M5.5830078,0C5.5830078,-1.4470834E-07 6.8789978,1.4949948 4.5859985,2.6919931 4.5859985,2.6919931 3.1900024,3.2899927 2.6920166,4.3859931 2.6920166,4.3859931 2.5920105,4.7849952 2.2929993,4.4859993 2.2929993,4.4859993 1.8950195,3.1899942 4.0880127,1.8939967 4.1870117,1.9939953 5.882019,1.2959973 5.5830078,0z"
|
||||
/>
|
||||
</DrawingGroup.Children>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<!-- Pin
|
||||
<DrawingImage x:Key="DotsVertical" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing
|
||||
Brush="Black"
|
||||
Geometry="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"
|
||||
/>
|
||||
</DrawingGroup.Children>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
|
||||
<DrawingImage x:Key="HandPointingOverDocument" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing
|
||||
Brush="Black"
|
||||
Geometry="M17.740002,11.999007C17.254002,11.999007,16.859001,12.394008,16.859001,12.879008L16.859001,24.458791 16.860127,24.479386C16.861003,24.607261 16.813003,24.735511 16.716002,24.834012 16.667502,24.883012 16.611378,24.91995 16.551394,24.944762L16.521562,24.955436 16.507633,24.960536 16.506465,24.960837 16.50574,24.961097 16.49194,24.964574 16.459728,24.972863C16.42719,24.979527,16.393502,24.983027,16.359002,24.983027L16.316763,24.980893 16.315716,24.980851 16.307903,24.980445C16.223895,24.971904,16.146014,24.942538,16.07951,24.897597L16.067752,24.887895 16.047276,24.873078 16.015234,24.844558 16.005502,24.836528 16.001236,24.831357 13.737002,22.600011C13.267003,22.134011 12.502003,22.135011 12.031003,22.600011 11.805003,22.82301 11.680003,23.120011 11.680003,23.436011 11.680003,23.751012 11.805003,24.048011 12.031003,24.272012L16.842003,29.031013C19.311003,31.189013 21.546002,31.119014 22.656003,30.886014 23.939002,30.614013 25.015002,29.846013 25.464002,28.880013 25.857001,28.036012 26.301002,27.080012 26.263001,26.195012 26.262002,26.178012 26.268003,26.074012 26.269002,26.056012L26.268003,20.974011C26.268003,20.55601 25.922002,20.215011 25.496001,20.215011 25.335502,20.215011 25.239001,20.246511 25.179877,20.283135L25.144,20.309762 25.144,21.215013C25.144,21.491012 24.919999,21.715013 24.644,21.715013 24.367999,21.715013 24.144,21.491012 24.144,21.215013L24.144,19.278017C24.144,18.793018 23.749,18.399019 23.263,18.399019 22.808312,18.399019 22.432727,18.745308 22.387555,19.18822L22.383004,19.277944 22.383004,19.716007C22.383004,19.992006 22.159003,20.216004 21.883002,20.216004 21.607003,20.216004 21.383002,19.992006 21.383002,19.716007L21.383002,19.278054 21.383002,18.504013C21.383002,18.018016 20.988002,17.623017 20.502002,17.623017 20.016002,17.623017 19.621001,18.018016 19.621001,18.504013L19.621001,20.24402C19.621001,20.52102 19.397001,20.74402 19.121001,20.74402 18.844001,20.74402 18.621001,20.52102 18.621001,20.24402L18.621001,18.504035 18.621001,12.879008C18.621001,12.394008,18.226001,11.999007,17.740002,11.999007z M17.740002,10.999005C18.777001,10.999005,19.621001,11.843007,19.621001,12.879008L19.621001,16.842839 19.687315,16.808824C19.933955,16.689783 20.210345,16.623022 20.502002,16.623022 21.247346,16.623022 21.892987,17.059031 22.197202,17.689331L22.211675,17.721288 22.212884,17.720384C22.513,17.517568 22.874499,17.399021 23.263,17.399021 24.267594,17.399021 25.091062,18.190155 25.141547,19.181478L25.143222,19.247425 25.174827,19.241074C25.276639,19.223799 25.383876,19.21501 25.496001,19.21501 26.473002,19.21501 27.268003,20.00401 27.268003,20.974011L27.268003,26.087012C27.268003,26.098012 27.262002,26.194013 27.262002,26.205012 27.311002,27.281013 26.790002,28.401013 26.371001,29.302012 25.783002,30.566012 24.472001,31.523013 22.863002,31.864013 22.513001,31.938014 22.067002,31.999013 21.547002,31.999013 20.149003,31.999013 18.216002,31.561013 16.161002,29.763013L11.328003,24.983012C10.910003,24.570012 10.680003,24.020011 10.680003,23.436011 10.680003,22.85101 10.910003,22.302011 11.328003,21.88901 12.187003,21.040011 13.583003,21.041011 14.440002,21.88901L15.859002,23.287391 15.859002,12.879008C15.859002,11.843007,16.703002,10.999005,17.740002,10.999005z M6.8659983,4.0500388L19.083997,4.0500388C20.668996,4.0500388,21.957997,5.3460296,21.957997,6.9390478L21.957997,14.653042C21.957997,14.93005 21.734998,15.153042 21.457997,15.153042 21.181996,15.153042 20.957997,14.93005 20.957997,14.653042L20.957997,6.9390478C20.957997,5.8980318,20.116998,5.0500394,19.083997,5.0500394L6.8659983,5.0500394C5.8319979,5.0500394,4.9909983,5.8980318,4.9909984,6.9390478L4.9909984,26.116061C4.9909983,27.157046,5.8319979,28.00504,6.8659983,28.00504L11.696996,28.00504C11.972997,28.00504 12.196996,28.229039 12.196996,28.50504 12.196996,28.781041 11.972997,29.00504 11.696996,29.00504L6.8659983,29.00504C5.2809954,29.00504,3.9909983,27.70905,3.9909983,26.116061L3.9909983,6.9390478C3.9909983,5.3460296,5.2809954,4.0500388,6.8659983,4.0500388z M6.4360008,0L17.467001,0C17.743001,0 17.967001,0.22300029 17.967001,0.50000024 17.967001,0.77600098 17.743001,1.0000012 17.467001,1.0000012L6.4360008,1.0000012C3.4380004,1.0000012,1.0000002,3.4210042,1.0000002,6.3970079L1.0000002,20.617026C1.0000002,20.893025 0.77600026,21.117026 0.50000048,21.117026 0.22400022,21.117026 2.3841858E-07,20.893025 0,20.617026L0,6.3970079C2.3841858E-07,2.8700035,2.8870004,0,6.4360008,0z"
|
||||
/>
|
||||
</DrawingGroup.Children>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
|
||||
<!-- Pin
|
||||
<Path Data="F1M3,11L3,7 0,7 0,6 1,6 1,1 1,0 6,0 6,1 6,6 7,6 7,7 4,7 4,11 3,11z M2,1L2,6 4,6 4,1 2,1z" />
|
||||
Unpin
|
||||
<Path Data="F1M0,3L4,3 4,0 5,0 5,1 10,1 11,1 11,6 10,6 5,6 5,7 4,7 4,4 0,4 0,3z M10,2L5,2 5,4 10,4 10,2z" />
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using bts.api.core;
|
||||
using bts.api.md.auxiliary;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Reactive.Concurrency;
|
||||
|
||||
namespace mbts.spark {
|
||||
[Export]
|
||||
class MarketDataAuxiliaryModel : IDisposable {
|
||||
readonly MarketDataAuxiliaryService _service;
|
||||
readonly TableUpdateDemuxer<VolumeAtPriceLevelImmutable, int, uint> _security_orders_demuxer;
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
MarketDataAuxiliaryModel(MarketDataAuxiliaryService service) {
|
||||
_service = service;
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
volume_at_price = service.subscribei_volume_at_price().Naggle(interval, scheduler).Table().Cache();
|
||||
_security_orders_demuxer = new TableUpdateDemuxer<VolumeAtPriceLevelImmutable, int, uint>(o => o.RecordId, v => v.SecurityId);
|
||||
_dispose = volume_at_price.AddRemoveOnly().Subscribe(_security_orders_demuxer);
|
||||
}
|
||||
|
||||
public IObservable<TableUpdate<VolumeAtPriceLevelImmutable, int>> volume_at_price { get; }
|
||||
|
||||
public IObservable<ITableUpdateTransform<VolumeAtPriceLevelImmutable, int>> security_volume_at_price(uint security) =>
|
||||
_security_orders_demuxer.subscribe(security);
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using bts.spark;
|
||||
using mbts;
|
||||
using mbts.co2;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using System.Collections.Generic;
|
||||
@@ -17,9 +18,9 @@ namespace bts.shell
|
||||
class MarketShellModule : IShellModule
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public MarketShellModule(IBus bus, MDConsumer consumer, OrdersModel orders_model, [Import("username")]string user_name) {
|
||||
_tsf = new TimeAndSalesWindowFactory(this, bus, consumer);
|
||||
_mvf = new MarketViewWindowFactory(this, bus, orders_model);
|
||||
public MarketShellModule(IBus bus, MDConsumer consumer, UserSettingsHub settings_hub, OrdersModel orders_model, [Import("username")]string user_name) {
|
||||
_tsf = new TimeAndSalesWindowFactory(this, bus, consumer, settings_hub);
|
||||
_mvf = new MarketViewWindowFactory(this, bus, settings_hub, orders_model);
|
||||
}
|
||||
|
||||
TimeAndSalesWindowFactory _tsf;
|
||||
|
||||
@@ -5,11 +5,9 @@ using mbts.collections;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using bts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -18,7 +16,6 @@ using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class MarketViewVM {
|
||||
public class Row : SecurityMDRow, IDisposable, IEquatable<Row>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
|
||||
@@ -29,7 +26,6 @@ namespace mbts.spark {
|
||||
readonly ObservableFilterCollection<OrderRecordImmutable> _working_orders, _working_bids, _working_asks;
|
||||
readonly string _security_text, _security_key_text;
|
||||
readonly Property<double> _bid_price, _ask_price, _last_price, _day_change, _settlement, _session_high, _session_low;
|
||||
readonly Property<uint> _bid_size, _ask_size;
|
||||
readonly CellProperty<int> _position;
|
||||
readonly CellProperty<uint> _last_size;
|
||||
readonly CellProperty<ulong> _volume;
|
||||
@@ -38,6 +34,9 @@ namespace mbts.spark {
|
||||
readonly CellProperty<double> _working_best_bid, _working_best_ask;
|
||||
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;
|
||||
string _bid_merged_size_text, _ask_merged_size_text;
|
||||
ulong _bbo_ref, _last_ref;
|
||||
|
||||
public Row(IBus bus, SecurityMD smd, OrdersModel orders_model) : base(smd) {
|
||||
@@ -49,8 +48,6 @@ namespace mbts.spark {
|
||||
_position = new CellProperty<int>(0);
|
||||
_bid_price = backing(nameof(bid_price), double.NaN);
|
||||
_ask_price = backing(nameof(ask_price), double.NaN);
|
||||
_bid_size = backing<uint>(nameof(bid_size), 0);
|
||||
_ask_size = backing<uint>(nameof(ask_size), 0);
|
||||
_last_price = backing(nameof(last_price), double.NaN);
|
||||
_last_size = new CellProperty<uint>(0);
|
||||
_day_change = backing(nameof(day_change), double.NaN);
|
||||
@@ -73,10 +70,14 @@ namespace mbts.spark {
|
||||
//
|
||||
default_command = new DelegateCommand(_show_ladder);
|
||||
bid_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Bid));
|
||||
bid_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidSize));
|
||||
bid_merged_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidMergedSize));
|
||||
bid_direct_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidDirectSize));
|
||||
bid_implied_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidImpliedSize));
|
||||
working_bids_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingBids));
|
||||
ask_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Ask));
|
||||
ask_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskSize));
|
||||
ask_merged_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskMergedSize));
|
||||
ask_direct_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskDirectSize));
|
||||
ask_implied_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskImpliedSize));
|
||||
working_asks_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingAsks));
|
||||
_animation.AnimationsChanged += on_animations_changed;
|
||||
void on_animations_changed() => RaisePropertyChanged(nameof(animations));
|
||||
@@ -99,9 +100,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_size => _bid_size.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 double ask_price => _ask_price.Value;
|
||||
public uint ask_size => _ask_size.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 ulong volume => _volume.value;
|
||||
public double settlement => _settlement.Value;
|
||||
public double day_change => _day_change.Value;
|
||||
@@ -115,10 +122,14 @@ namespace mbts.spark {
|
||||
public double working_best_ask => _working_best_ask.value;
|
||||
public IList<ui.spark.CellAnimation> animations => _animation.get_animations().ToList();
|
||||
public ICommand bid_price_click { get; }
|
||||
public ICommand bid_size_click { get; }
|
||||
public ICommand bid_merged_size_click { get; }
|
||||
public ICommand bid_direct_size_click { get; }
|
||||
public ICommand bid_implied_size_click { get; }
|
||||
public ICommand working_bids_click { get; }
|
||||
public ICommand ask_price_click { get; }
|
||||
public ICommand ask_size_click { get; }
|
||||
public ICommand ask_merged_size_click { get; }
|
||||
public ICommand ask_direct_size_click { get; }
|
||||
public ICommand ask_implied_size_click { get; }
|
||||
public ICommand working_asks_click { get; }
|
||||
public ICommand default_command { get; }
|
||||
|
||||
@@ -133,9 +144,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);
|
||||
_bid_size.set(b.size);
|
||||
refresh_size(ref b, ref _bid_direct_size, ref _bid_merged_size, ref _bid_merged_size_text, nameof(bid_merged_size_text));
|
||||
_ask_price.set(a.price);
|
||||
_ask_size.set(a.size);
|
||||
refresh_size(ref a, ref _ask_direct_size, ref _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);
|
||||
}
|
||||
@@ -150,6 +161,16 @@ namespace mbts.spark {
|
||||
_day_change.set(last_price - settlement);
|
||||
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;
|
||||
text = null;
|
||||
dirty = true;
|
||||
RaisePropertyChanged(text_prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(Row other) =>
|
||||
@@ -187,17 +208,33 @@ namespace mbts.spark {
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, 0u));
|
||||
break;
|
||||
case MarketViewView.Columns.BidSize:
|
||||
case MarketViewView.Columns.BidMergedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_merged_size));
|
||||
break;
|
||||
case MarketViewView.Columns.BidDirectSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_direct_size));
|
||||
break;
|
||||
case MarketViewView.Columns.BidImpliedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_implied_size));
|
||||
break;
|
||||
case MarketViewView.Columns.Ask:
|
||||
order.Side = Side.Buy;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, 0u));
|
||||
break;
|
||||
case MarketViewView.Columns.AskSize:
|
||||
case MarketViewView.Columns.AskMergedSize:
|
||||
order.Side = Side.Buy;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_size));
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_merged_size));
|
||||
break;
|
||||
case MarketViewView.Columns.AskDirectSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_direct_size));
|
||||
break;
|
||||
case MarketViewView.Columns.AskImpliedSize:
|
||||
order.Side = Side.Sell;
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_implied_size));
|
||||
break;
|
||||
case MarketViewView.Columns.Last:
|
||||
_bus.post(SparkTags.ShowOrderTicketTag, (order, last_price, 0u));
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using bts.api;
|
||||
using mbts.persist;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows.Data;
|
||||
@@ -17,65 +20,79 @@ namespace mbts.spark {
|
||||
partial class MarketViewVM : SecurityMDGrid<MarketViewVM.Row, SecurityMD> {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
|
||||
public MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state)
|
||||
: this(bus, orders_model, state, state.SelectManyInSeries(s => s.security_criteria.subscribe_md(bus))) { }
|
||||
|
||||
public MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state, IWindowSettingsManager<GridSettings> settings_manager )
|
||||
: this(bus, orders_model, state, state.SelectManyInSeries(s => s.security_criteria.subscribe_md(bus)), settings_manager) { }
|
||||
|
||||
// securities_connectable should NOT be connected already.
|
||||
MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state, IConnectableObservable<IEnumerable<SecurityMD>> securities_connectable)
|
||||
MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state, IConnectableObservable<IEnumerable<SecurityMD>> securities_connectable, IWindowSettingsManager<GridSettings> settings_manager)
|
||||
: base(RXCollection.Create(securities_connectable, SecurityDataHelper.EqualityComparer.MDComparer), null) {
|
||||
_bus = bus;
|
||||
_orders_model = orders_model;
|
||||
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
|
||||
_settings_manager = settings_manager;
|
||||
// Columns
|
||||
const string size_col_header = "Qty", working_col_header = "Working";
|
||||
const string merged_size_col_header = "Qty", direct_size_col_header = "Direct", implied_size_col_header = "Implied", working_col_header = "Working";
|
||||
const double working_orders_col_width = 105;
|
||||
IMultiValueConverter price_converter = new ui.spark.converters.TickDisplayConverter();
|
||||
add_col(Columns.Security, "Security", r => r.security.ToString(), nameof(Row.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), 64, volume_filter, DataGridFilterKnownTemplates.ULongMinMax);
|
||||
add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), DefaultColumnWidths.Volume, volume_filter, DataGridFilterKnownTemplates.ULongMinMax);
|
||||
add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
|
||||
add_col(Columns.LastSize, size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax);
|
||||
add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true);
|
||||
add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid);
|
||||
add_hl_size_col(Columns.BidSize, size_col_header, nameof(Row.bid_size), nameof(Row.bid_size_click), bid_size_filter, BTSColors.MarketData.bid);
|
||||
add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid_brush);
|
||||
add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask_brush);
|
||||
add_hl_size_col(Columns.AskSize, size_col_header, nameof(Row.ask_size), nameof(Row.ask_size_click), ask_size_filter, BTSColors.MarketData.ask);
|
||||
add_hl_working_orders_col(Columns.WorkingAsks, working_col_header, nameof(Row.working_asks_text), nameof(Row.working_best_ask), nameof(Row.working_asks_click), working_ask_volume_filter, BTSColors.MarketData.ask);
|
||||
add_col(Columns.Settlement, "Settlement", r => r.format_price(r.settlement), nameof(Row.settlement), DefaultColumnWidths.Price, settlement_filter, DataGridFilterKnownTemplates.DoubleMinMax);
|
||||
add_col(Columns.DayChange, "Change", r => r.format_price(r.day_change), nameof(Row.day_change), DefaultColumnWidths.Price, day_change_filter, DataGridFilterKnownTemplates.DoubleMinMax, true);
|
||||
add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax);
|
||||
add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax);
|
||||
add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid, System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.BidImpliedSize, implied_size_col_header, r => r.bid_implied_size.ToString(), nameof(Row.bid_implied_size), DefaultColumnWidths.ImpliedDirectMergedSize, bid_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.BidDirectSize, direct_size_col_header, r => r.bid_direct_size.ToString(), nameof(Row.bid_direct_size), DefaultColumnWidths.ImpliedDirectMergedSize, bid_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_merged_size_col(Columns.BidMergedSize, merged_size_col_header, nameof(Row.bid_merged_size), nameof(Row.bid_merged_size_text), nameof(Row.bid_merged_size_click), bid_merged_size_filter, BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid_brush, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask_brush, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_merged_size_col(Columns.AskMergedSize, merged_size_col_header, nameof(Row.ask_merged_size), nameof(Row.ask_merged_size_text), nameof(Row.ask_merged_size_click), ask_merged_size_filter, BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.AskDirectSize, direct_size_col_header, r => r.ask_direct_size.ToString(), nameof(Row.ask_direct_size), DefaultColumnWidths.ImpliedDirectMergedSize, ask_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.AskImpliedSize, implied_size_col_header, r => r.ask_implied_size.ToString(), nameof(Row.ask_implied_size), DefaultColumnWidths.ImpliedDirectMergedSize, ask_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_working_orders_col(Columns.WorkingAsks, working_col_header, nameof(Row.working_asks_text), nameof(Row.working_best_ask), nameof(Row.working_asks_click), working_ask_volume_filter, BTSColors.MarketData.ask, System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.Settlement, "Settlement", r => r.format_price(r.settlement), nameof(Row.settlement), DefaultColumnWidths.Settlement, settlement_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.DayChange, "Change", r => r.format_price(r.day_change), nameof(Row.day_change), DefaultColumnWidths.Price, day_change_filter, DataGridFilterKnownTemplates.DoubleMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.SecurityKey, "Security Key", r => r.security_key.ToString(), nameof(Row.security_key), DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
//
|
||||
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false) =>
|
||||
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false, Color? font_color = null, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
|
||||
// it's necessary to have this id populated for grid settings
|
||||
id = bts.shell.MarketViewWindowFactory.col_id(col_key),
|
||||
Header = header,
|
||||
sort_member_path = prop,
|
||||
Width = width,
|
||||
padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding
|
||||
padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding,
|
||||
font_color_override = font_color,
|
||||
horizontal_alignment = h_alignment
|
||||
});
|
||||
|
||||
void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background) =>
|
||||
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price, foreground, background, DefaultSelectionHighlight);
|
||||
void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.MViewPrice, foreground, background, DefaultSelectionHighlight, h_alignment);
|
||||
|
||||
void add_hl_size_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground) =>
|
||||
add_hl_col(col_key, header, new Binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, foreground, null, null);
|
||||
void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.ImpliedDirectMergedSize, foreground, null, null, h_alignment);
|
||||
|
||||
void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground) =>
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null);
|
||||
void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null, h_alignment);
|
||||
|
||||
void add_hl_col(
|
||||
Columns col_key, string header, BindingBase binding, string prop, string command_prop,
|
||||
IDataGridColumnFilter filter, string filter_template, double width, Color foreground, Brush background, Brush background_overlay) =>
|
||||
IDataGridColumnFilter filter, string filter_template, double width, Color foreground, Brush background, Brush background_overlay, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
columns.Add(new HyperlinkElementColumn<Row, Columns>(
|
||||
col_key, binding, new Binding(command_prop), filter, filter_template) {
|
||||
// it's necessary to have this id populated for grid settings
|
||||
id = bts.shell.MarketViewWindowFactory.col_id(col_key),
|
||||
Header = header,
|
||||
sort_member_path = prop,
|
||||
font_color_override = foreground,
|
||||
background = background,
|
||||
background_overlay = background_overlay,
|
||||
Width = DefaultColumnWidths.Price
|
||||
Width = DefaultColumnWidths.Price,
|
||||
horizontal_alignment = h_alignment
|
||||
});
|
||||
|
||||
BindingBase price_binding(string prop) {
|
||||
@@ -85,17 +102,41 @@ namespace mbts.spark {
|
||||
return mb;
|
||||
}
|
||||
//
|
||||
// make font-color immutable for Side, Size, (the font-color rule is hard-coded: MarketData.bid for Buy orders, MarketData.ask for Sells)
|
||||
var col_settings_flags = new Dictionary<string, GridColumnSettingsFeatureFlags> {
|
||||
{ Columns.Bid.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.Ask.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.BidImpliedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.BidDirectSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.BidMergedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.AskImpliedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.AskDirectSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.AskMergedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.WorkingAsks.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.WorkingBids.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }
|
||||
};
|
||||
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, col_settings_flags, row_vms);
|
||||
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
|
||||
grid_settings_manager.load_from(settings_manager.current_settings());
|
||||
//
|
||||
_resources.AddRange(
|
||||
state.OnDispatcher().Subscribe(s => {
|
||||
using (rows_view.DeferRefresh()) {
|
||||
rows_view.SetSortingCriteria(s.sorting_criteria);
|
||||
var filters = s.filters;
|
||||
if (filters != null) {
|
||||
if (s.grid != null) {
|
||||
// this means we are not using defaults,
|
||||
// Note: settings_manager in turn notifies grid_settings_manager of this update
|
||||
_settings_manager.instance_settings = s.grid;
|
||||
}
|
||||
if (s.filters is FilterStates filters) {
|
||||
security_filter.load_state(filters.security);
|
||||
bid_price_filter.load_state(filters.bid_price);
|
||||
bid_size_filter.load_state(filters.bid_size);
|
||||
bid_merged_size_filter.load_state(filters.bid_merged_size);
|
||||
bid_direct_size_filter.load_state(filters.bid_direct_size);
|
||||
bid_implied_size_filter.load_state(filters.bid_implied_size);
|
||||
ask_price_filter.load_state(filters.ask_price);
|
||||
ask_size_filter.load_state(filters.ask_size);
|
||||
ask_merged_size_filter.load_state(filters.ask_merged_size);
|
||||
ask_direct_size_filter.load_state(filters.ask_direct_size);
|
||||
ask_implied_size_filter.load_state(filters.ask_implied_size);
|
||||
last_price_filter.load_state(filters.last_price);
|
||||
last_size_filter.load_state(filters.last_size);
|
||||
day_change_filter.load_state(filters.day_change);
|
||||
@@ -108,23 +149,40 @@ namespace mbts.spark {
|
||||
volume_filter.load_state(filters.volume);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
rows_view.set_sort_descriptions(s.sorting_criterion, columns);
|
||||
}
|
||||
}), DataGridFilterHelper.hook_filters(rows_view,
|
||||
security_filter, bid_price_filter, bid_size_filter, ask_price_filter, ask_size_filter, last_price_filter, last_size_filter, day_change_filter, settlement_filter,
|
||||
}), DataGridFilterHelper.hook_filters(rows_view, security_filter,
|
||||
bid_price_filter, bid_merged_size_filter, bid_direct_size_filter, bid_implied_size_filter,
|
||||
ask_price_filter, ask_merged_size_filter, ask_direct_size_filter, ask_implied_size_filter,
|
||||
last_price_filter, last_size_filter, day_change_filter, settlement_filter,
|
||||
session_low_filter, session_high_filter, position_filter, working_bid_volume_filter, working_ask_volume_filter, volume_filter, security_key_filter),
|
||||
securities_connectable.Connect(),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
WorkLoop.Start("MarketView", _refresh)
|
||||
);
|
||||
}
|
||||
|
||||
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
|
||||
|
||||
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
|
||||
|
||||
public GridSettings get_grid_save_state() {
|
||||
if (_settings_manager.instance_settings == null)
|
||||
return null;
|
||||
else
|
||||
return grid_settings_manager.create();
|
||||
}
|
||||
|
||||
#region Filters
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_price_filter { get; } = new DoubleMinMax<Row>(r => r.bid_price, nameof(Row.bid_price));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_size, nameof(Row.bid_size));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_merged_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_merged_size, nameof(Row.bid_merged_size));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_direct_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_direct_size, nameof(Row.bid_direct_size));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_implied_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_implied_size, nameof(Row.bid_implied_size));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> ask_price_filter { get; } = new DoubleMinMax<Row>(r => r.ask_price, nameof(Row.ask_price));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_size_filter { get; } = new UIntMinMax<Row>(r => r.ask_size, nameof(Row.ask_size));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_merged_size_filter { get; } = new UIntMinMax<Row>(r => r.ask_merged_size, nameof(Row.ask_merged_size));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_direct_size_filter { get; } = new UIntMinMax<Row>(r => r.ask_direct_size, nameof(Row.ask_direct_size));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_implied_size_filter { get; } = new UIntMinMax<Row>(r => r.ask_implied_size, nameof(Row.ask_implied_size));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> last_price_filter { get; } = new DoubleMinMax<Row>(r => r.last_price, nameof(Row.last_price));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> last_size_filter { get; } = new UIntMinMax<Row>(r => r.last_size, nameof(Row.last_size));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> day_change_filter { get; } = new DoubleMinMax<Row>(r => r.day_change, nameof(Row.day_change));
|
||||
@@ -141,9 +199,13 @@ namespace mbts.spark {
|
||||
return new FilterStates {
|
||||
security = security_filter.get_state(),
|
||||
bid_price = bid_price_filter.get_state(),
|
||||
bid_size = bid_size_filter.get_state(),
|
||||
bid_merged_size = bid_merged_size_filter.get_state(),
|
||||
bid_direct_size = bid_direct_size_filter.get_state(),
|
||||
bid_implied_size = bid_implied_size_filter.get_state(),
|
||||
ask_price = ask_price_filter.get_state(),
|
||||
ask_size = ask_size_filter.get_state(),
|
||||
ask_merged_size = ask_merged_size_filter.get_state(),
|
||||
ask_direct_size = ask_direct_size_filter.get_state(),
|
||||
ask_implied_size = ask_implied_size_filter.get_state(),
|
||||
last_price = last_price_filter.get_state(),
|
||||
last_size = last_size_filter.get_state(),
|
||||
day_change = day_change_filter.get_state(),
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row}"
|
||||
Rows="{Binding rows_view}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
|
||||
>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for MarketSheetsView.xaml
|
||||
/// </summary>
|
||||
partial class MarketViewView : UserControl {
|
||||
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidSize, Ask, AskSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey }
|
||||
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidMergedSize, BidDirectSize, BidImpliedSize, Ask, AskMergedSize, AskDirectSize, AskImpliedSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey }
|
||||
|
||||
public MarketViewView() {
|
||||
InitializeComponent();
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -16,16 +19,18 @@ namespace mbts.spark {
|
||||
const string _window_type = "MarketView", _thread_id = "MarketView", _window_type_display_name = "Market View";
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly AllowOnce _init_guard = new AllowOnce("MarketView");
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
MarketViewVM _vm;
|
||||
MarketViewView _view;
|
||||
MarketViewWindowFactory.StartupData _state;
|
||||
IReadOnlyList<SparkCommand> _commands;
|
||||
IDisposable _subscriptions;
|
||||
|
||||
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state)
|
||||
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, WindowSettingsManager<GridSettings> settings_manager)
|
||||
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
|
||||
_orders_model = orders_model;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
Icon.Freeze();
|
||||
}
|
||||
|
||||
@@ -34,15 +39,21 @@ namespace mbts.spark {
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override MarketViewWindowFactory.StartupData SaveState => _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states());
|
||||
public override double MinWidth => 1035 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
|
||||
public override MarketViewWindowFactory.StartupData SaveState =>
|
||||
_state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.get_grid_save_state(), _vm.rows_view.get_sorting_criteria(_vm.columns));
|
||||
|
||||
public override double MinWidth => MarketViewWindowFactory.StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public string thread_id => _thread_id;
|
||||
|
||||
public void thread_init() {
|
||||
_init_guard.check_access();
|
||||
Log.notnull(_state);
|
||||
var state_src = Observable.FromEvent<Action<MarketViewWindowFactory.StartupData>, MarketViewWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
|
||||
_vm = new MarketViewVM(_bus, _orders_model, state_src);
|
||||
_vm = new MarketViewVM(_bus, _orders_model, state_src, _settings_manager);
|
||||
_view = new MarketViewView { DataContext = _vm };
|
||||
var update_securities_cmd = SparkCommands.change_securities_callback(_window_type_display_name);
|
||||
_commands = new List<SparkCommand> {
|
||||
@@ -55,10 +66,28 @@ namespace mbts.spark {
|
||||
),
|
||||
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
|
||||
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs)),
|
||||
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria))
|
||||
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
|
||||
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
|
||||
SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
|
||||
_settings_manager,
|
||||
us => us.market_view_grid,
|
||||
UserSettings.FileExtensions.GRID,
|
||||
s => _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.with(_vm.columns.Select( c => c.create())),
|
||||
this.observe_title()
|
||||
)
|
||||
);
|
||||
// TODO: bind to main window's Title
|
||||
var dialog = new SimpleGridSettingsDialog { Title = "Market View Grid Settings", DataContext = vm };
|
||||
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
|
||||
return dialog;
|
||||
}))
|
||||
};
|
||||
_subscriptions = _bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(c =>
|
||||
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states())));
|
||||
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.get_filter_states(), _vm.grid_settings_manager.create())));
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
@@ -67,4 +96,4 @@ namespace mbts.spark {
|
||||
Commands.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,47 @@
|
||||
using bts.api;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.MarketViewView;
|
||||
|
||||
namespace bts.shell {
|
||||
class MarketViewWindowFactory : ShellWindowFactory<MarketViewWindowFactory.StartupData> {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
|
||||
public MarketViewWindowFactory(MarketShellModule module, IBus bus, OrdersModel orders_model) : base(module, "MarketView") {
|
||||
|
||||
public MarketViewWindowFactory(MarketShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) : base(module, "MarketView") {
|
||||
_bus = bus;
|
||||
_orders_model = orders_model;
|
||||
_settings_hub = settings_hub;
|
||||
_dispose = bus.subscribe(SparkTags.ShowMarketViewTag).OnDispatcher().Subscribe(_show);
|
||||
}
|
||||
|
||||
public static string col_id(Columns col) => col.ToString();
|
||||
|
||||
public override Task<IShellWindow> load(StartupData state) =>
|
||||
Task.FromResult(_create(state));
|
||||
|
||||
void _show(SecurityCriteria criteria) =>
|
||||
_bus.post(SparkTags.ShowWindow, _create(new StartupData(criteria, new List<SortingCriteria> { new SortingCriteria(nameof(MarketViewVM.Row.security), true) }, null)));
|
||||
_bus.post(SparkTags.ShowWindow, _create(StartupData.Default(criteria)));
|
||||
|
||||
IShellWindow _create(StartupData state) => new MarketViewWindow(this, _bus, _orders_model, state);
|
||||
IShellWindow _create(StartupData state) {
|
||||
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
|
||||
state.grid,
|
||||
get: us => us.market_view_grid,
|
||||
set: (us, gs) => us.with(provided_market_view_gridsettings: gs));
|
||||
return new MarketViewWindow(this, _bus, _orders_model, state, settings_manager);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class StartupData {
|
||||
@@ -34,20 +50,89 @@ namespace bts.shell {
|
||||
[ProtoMember(1)]
|
||||
public readonly SecurityCriteria security_criteria;
|
||||
[ProtoMember(2)]
|
||||
public readonly List<SortingCriteria> sorting_criteria;
|
||||
List<SortingCriteria> _sorting_criteria;
|
||||
[ProtoMember(3)]
|
||||
public readonly FilterStates filters;
|
||||
[ProtoMember(4)]
|
||||
public GridSettings grid;
|
||||
|
||||
StartupData() { }
|
||||
public StartupData() { }
|
||||
|
||||
public StartupData(SecurityCriteria security_criteria)
|
||||
: this (security_criteria, new List<SortingCriteria>(), null) { }
|
||||
: this (security_criteria, new FilterStates()) { }
|
||||
|
||||
public StartupData(SecurityCriteria security_criteria, List<SortingCriteria> sorting_criteria, FilterStates filters) {
|
||||
public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List<SortingCriteria> sort = null) {
|
||||
this.security_criteria = security_criteria;
|
||||
this.sorting_criteria = sorting_criteria;
|
||||
this.filters = filters;
|
||||
this.grid = grid;
|
||||
_sorting_criteria = sort;
|
||||
}
|
||||
|
||||
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
|
||||
|
||||
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList());
|
||||
|
||||
public static StartupData Default(SecurityCriteria security_criteria) =>
|
||||
new StartupData(security_criteria, new FilterStates(), null);
|
||||
|
||||
static class DefaultColumns {
|
||||
|
||||
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings volume => new GridColumnSettings(col_id(Columns.Volume), DefaultColumnHeaders.Volume, width: DefaultColumnWidths.Volume);
|
||||
public static GridColumnSettings last => new GridColumnSettings(col_id(Columns.Last), DefaultColumnHeaders.Last, width: DefaultColumnWidths.Price);
|
||||
public static GridColumnSettings last_size => new GridColumnSettings(col_id(Columns.LastSize), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings position => new GridColumnSettings(col_id(Columns.Position), DefaultColumnHeaders.Pos, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings working_bids => new GridColumnSettings(col_id(Columns.WorkingBids), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Working, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings bid_implied_size => new GridColumnSettings(col_id(Columns.BidImpliedSize), DefaultColumnHeaders.ImpliedSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings bid_direct_size => new GridColumnSettings(col_id(Columns.BidDirectSize), DefaultColumnHeaders.DirectSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings bid_merged_size => new GridColumnSettings(col_id(Columns.BidMergedSize), DefaultColumnHeaders.MergedSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings ask_merged_size => new GridColumnSettings(col_id(Columns.AskMergedSize), DefaultColumnHeaders.MergedSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings ask_direct_size => new GridColumnSettings(col_id(Columns.AskDirectSize), DefaultColumnHeaders.DirectSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings ask_implied_size => new GridColumnSettings(col_id(Columns.AskImpliedSize), DefaultColumnHeaders.ImpliedSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings working_asks => new GridColumnSettings(col_id(Columns.WorkingAsks), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Working, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings settlement => new GridColumnSettings(col_id(Columns.Settlement), DefaultColumnHeaders.Settlement, width: DefaultColumnWidths.Settlement, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
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 IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(security.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return security;
|
||||
yield return volume;
|
||||
yield return last;
|
||||
yield return last_size;
|
||||
yield return position;
|
||||
yield return working_bids;
|
||||
yield return bid_implied_size;
|
||||
yield return bid_direct_size;
|
||||
yield return bid_merged_size;
|
||||
yield return bid;
|
||||
yield return ask;
|
||||
yield return ask_merged_size;
|
||||
yield return ask_direct_size;
|
||||
yield return ask_implied_size;
|
||||
yield return working_asks;
|
||||
yield return settlement;
|
||||
yield return day_change;
|
||||
yield return session_low;
|
||||
yield return session_high;
|
||||
yield return security_key;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
@@ -57,11 +142,11 @@ namespace bts.shell {
|
||||
[ProtoMember(2)]
|
||||
public DoubleMinMaxState bid_price;
|
||||
[ProtoMember(3)]
|
||||
public UIntMinMaxState bid_size;
|
||||
public UIntMinMaxState bid_merged_size;
|
||||
[ProtoMember(4)]
|
||||
public DoubleMinMaxState ask_price;
|
||||
[ProtoMember(5)]
|
||||
public UIntMinMaxState ask_size;
|
||||
public UIntMinMaxState ask_merged_size;
|
||||
[ProtoMember(6)]
|
||||
public DoubleMinMaxState last_price;
|
||||
[ProtoMember(7)]
|
||||
@@ -84,6 +169,14 @@ namespace bts.shell {
|
||||
public ULongMinMaxState volume;
|
||||
[ProtoMember(16)]
|
||||
public string security_key;
|
||||
[ProtoMember(17)]
|
||||
public UIntMinMaxState bid_direct_size;
|
||||
[ProtoMember(18)]
|
||||
public UIntMinMaxState bid_implied_size;
|
||||
[ProtoMember(19)]
|
||||
public UIntMinMaxState ask_direct_size;
|
||||
[ProtoMember(20)]
|
||||
public UIntMinMaxState ask_implied_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,14 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
class TimeAndSalesModel : IDisposable {
|
||||
internal readonly SecurityCriteria security_criteria;
|
||||
|
||||
static readonly TimeSpan _poll_interval = TimeSpan.FromSeconds(0.4);
|
||||
Lazy<IObservable<IReadOnlyList<TradeInfo>>> _trades;
|
||||
IDisposable _dispose;
|
||||
|
||||
public TimeAndSalesModel(IBus bus, MDConsumer consumer, SecurityCriteria security_criteria) {
|
||||
this.security_criteria = security_criteria;
|
||||
var curr_channels = new Dictionary<ChannelKey, ChannelConsumer>();
|
||||
_trades = new Lazy<IObservable<IReadOnlyList<TradeInfo>>>(() => Observable.Interval(_poll_interval).Select(_ => {
|
||||
var copy = curr_channels;
|
||||
|
||||
@@ -3,7 +3,6 @@ using bts.co2;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
@@ -15,17 +14,25 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using static bts.shell.TimeAndSalesWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using mbts.persist;
|
||||
using mbts.ui.settings;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
class TimeAndSalesVM : SecurityGrid<TimeAndSalesVM.Row, TradeInfo> {
|
||||
readonly IBus _bus;
|
||||
readonly TimeAndSalesModel _model;
|
||||
IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
|
||||
public TimeAndSalesVM(IBus bus, TimeAndSalesModel model, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
|
||||
: base(RXCollection.CreateBuffer(model.trades, 1000), sorting_criteria) {
|
||||
|
||||
public TimeAndSalesVM(IBus bus, IWindowSettingsManager<GridSettings> settings_manager, TimeAndSalesModel model, StartupData startup_data)
|
||||
: base(RXCollection.CreateBuffer(model.trades, 1000), Enumerable.Empty<SortingCriteria>()) {
|
||||
_bus = bus;
|
||||
_model = model;
|
||||
_settings_manager = settings_manager;
|
||||
show_ladder = DelegateCommandParamObserver.Create(current_row_observable, r => _show_ladder_async(r).ignore(), r => r != null);
|
||||
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
|
||||
if (filters != null) {
|
||||
if (startup_data.filters is FilterStates filters) {
|
||||
time_filter.load_state(filters.time);
|
||||
security_filter.load_state(filters.security);
|
||||
price_filter.load_state(filters.price);
|
||||
@@ -35,33 +42,49 @@ namespace mbts.spark {
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
// Columns
|
||||
add_column(TimeAndSalesView.Columns.Security, DefaultColumnHeaders.Security, DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.Time, DefaultColumnHeaders.Time, DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter);
|
||||
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
|
||||
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
|
||||
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DIYGrid.DefaultColumnWidths.Aggressor, aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
|
||||
add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
add_column(TimeAndSalesView.Columns.Security, DefaultColumnHeaders.Security, DefaultColumnWidths.Security, nameof(Row.security), security_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.Time, DefaultColumnHeaders.Time, DefaultColumnWidths.Time, nameof(Row.time), time_filter, DataGridFilterKnownTemplates.TimeFilter);
|
||||
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(
|
||||
r => r[col_key]
|
||||
col_key
|
||||
, (r) => _get_aggressor_brush(r)
|
||||
, filter
|
||||
, filter_template
|
||||
)
|
||||
{ Header = header, Width = width, horizontal_alignment = h_alignment }
|
||||
{ Header = header, Width = width, sort_member_path = sort_member_path, horizontal_alignment = h_alignment }
|
||||
);
|
||||
|
||||
// be sure to load GridSettings AFTER the columns collection has been populated, the grid settings manager looks columns by id to apply the appropriate settings,
|
||||
// if the collection is not populated, the column settings will not be applied
|
||||
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, row_vms);
|
||||
grid_settings_manager.load_from(settings_manager.current_settings());
|
||||
_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, trade_key_filter, security_key_filter),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager)
|
||||
);
|
||||
rows_view.set_sort_descriptions(startup_data.sorting_criterion, columns);
|
||||
}
|
||||
|
||||
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
|
||||
public DelegateCommandParamObserver<Row> show_ladder { get; }
|
||||
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
|
||||
|
||||
public GridSettings get_grid_save_state() {
|
||||
if (_settings_manager.instance_settings == null)
|
||||
return null;
|
||||
else
|
||||
return grid_settings_manager.create();
|
||||
}
|
||||
|
||||
public StartupData create() =>
|
||||
new StartupData(_model.security_criteria, _get_filter_states(), get_grid_save_state(), rows_view.get_sorting_criteria(columns));
|
||||
|
||||
private Brush _get_aggressor_brush(Row r) {
|
||||
BTSColor c;
|
||||
@@ -82,7 +105,7 @@ namespace mbts.spark {
|
||||
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));
|
||||
|
||||
public FilterStates get_filter_states() =>
|
||||
FilterStates _get_filter_states() =>
|
||||
new FilterStates {
|
||||
time = time_filter.get_state(),
|
||||
security = security_filter.get_state(),
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Grid>
|
||||
<c:DIYGrid Columns="{Binding columns}" Rows="{Binding rows_view}" CurrentRow="{Binding current_row}">
|
||||
<c:DIYGrid Columns="{Binding columns}"
|
||||
Rows="{Binding rows_view}" CurrentRow="{Binding current_row}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
|
||||
>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
|
||||
@@ -3,8 +3,11 @@ using bts.api;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.co2;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -20,6 +23,7 @@ namespace mbts.spark {
|
||||
const string _window_type = "TimeAndSales", _window_type_display_name = "Time And Sales";
|
||||
readonly MDConsumer _consumer;
|
||||
readonly TimeAndSalesWindowFactory _factory;
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
SingleValuePump<TimeAndSalesVM> _vm;
|
||||
TimeAndSalesModel _model;
|
||||
StartupData _state;
|
||||
@@ -27,29 +31,25 @@ namespace mbts.spark {
|
||||
SparkCommand[] _commands;
|
||||
IDisposable _subscriptions;
|
||||
|
||||
public TimeAndSalesWindow(TimeAndSalesWindowFactory factory, IBus bus, MDConsumer consumer, StartupData state, ImageSource icon)
|
||||
public TimeAndSalesWindow(TimeAndSalesWindowFactory factory, IBus bus, MDConsumer consumer, StartupData state, ImageSource icon, IWindowSettingsManager<GridSettings> settings_manager)
|
||||
: base("Time And Sales", bus, factory, SparkCommands.ShowTimeAndSales.icon) {
|
||||
Log.assert(state != null);
|
||||
_consumer = consumer;
|
||||
_factory = factory;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
Icon.Freeze();
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override UIElement Content => _view;
|
||||
public override double MinWidth => DefaultColumnWidths.Security
|
||||
+ DefaultColumnWidths.Time
|
||||
+ DefaultColumnWidths.Price
|
||||
+ DefaultColumnWidths.Size
|
||||
+ DefaultColumnWidths.Aggressor
|
||||
+ DefaultColumnWidths.TradeKey
|
||||
+ DefaultColumnWidths.SecurityKey
|
||||
public override double MinWidth => StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH;
|
||||
|
||||
public override StartupData SaveState => _state = new StartupData(_state.security_criteria, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public override StartupData SaveState => _state = _vm.value.create();
|
||||
|
||||
public override void Dispose() =>
|
||||
_dispose_resources();
|
||||
@@ -66,7 +66,25 @@ namespace mbts.spark {
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
|
||||
SparkCommands.ShowFills.complete(selected_secs_source, always_true, get_fills_startup_data),
|
||||
SparkCommands.ShowMarketView.complete(selected_secs_source, always_true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
|
||||
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria))
|
||||
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
|
||||
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
|
||||
SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
|
||||
_settings_manager,
|
||||
s => s.time_and_sales_grid,
|
||||
UserSettings.FileExtensions.GRID,
|
||||
s => _vm.value.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.with(_vm.value.columns.Select( c => c.create())),
|
||||
this.observe_title()
|
||||
)
|
||||
);
|
||||
// TODO: bind to main window's Title
|
||||
var dialog = new SimpleGridSettingsDialog { Title = "Time and Sales Grid Settings", DataContext = vm };
|
||||
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
|
||||
return dialog;
|
||||
}))
|
||||
};
|
||||
_subscriptions = Disposable.Create(
|
||||
_bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(update_criteria),
|
||||
@@ -79,7 +97,7 @@ namespace mbts.spark {
|
||||
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
|
||||
});
|
||||
void update_criteria(SecurityCriteria c) {
|
||||
_state = new StartupData(c, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
|
||||
_state = _vm.value.create().with(c);
|
||||
_initialize();
|
||||
}
|
||||
}
|
||||
@@ -98,7 +116,7 @@ namespace mbts.spark {
|
||||
Disposable.dispose(ref _model);
|
||||
_model = new TimeAndSalesModel(_bus, _consumer, _state.security_criteria);
|
||||
// the previous _vm.value is automatically disposed when _vm.value is set - no need to dispose it here
|
||||
_vm.value = new TimeAndSalesVM(_bus, _model, _state.sorting_criteria, _state.filters);
|
||||
_vm.value = new TimeAndSalesVM(_bus, _settings_manager, _model, _state);
|
||||
if (_view == null)
|
||||
_view = new TimeAndSalesView();
|
||||
_view.DataContext = _vm.value;
|
||||
|
||||
@@ -2,24 +2,31 @@
|
||||
using bts.spark;
|
||||
using mbts;
|
||||
using mbts.co2;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.TimeAndSalesView;
|
||||
|
||||
namespace bts.shell {
|
||||
class TimeAndSalesWindowFactory : ShellWindowFactory<TimeAndSalesWindowFactory.StartupData> {
|
||||
readonly IBus _bus;
|
||||
readonly MDConsumer _consumer;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
|
||||
|
||||
public TimeAndSalesWindowFactory(IShellModule module, IBus bus, MDConsumer consumer) : base(module, "TimeAndSales") {
|
||||
public TimeAndSalesWindowFactory(IShellModule module, IBus bus, MDConsumer consumer, UserSettingsHub settings_hub) : base(module, "TimeAndSales") {
|
||||
_bus = bus;
|
||||
_consumer = consumer;
|
||||
_settings_hub = settings_hub;
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
|
||||
@@ -27,8 +34,11 @@ namespace bts.shell {
|
||||
);
|
||||
}
|
||||
|
||||
public static string col_id(Columns col) => col.ToString();
|
||||
|
||||
public override Task<IShellWindow> load(StartupData state) =>
|
||||
Task.FromResult<IShellWindow>(new TimeAndSalesWindow(this, _bus, _consumer, state, SparkCommands.PickTimeAndSales.icon));
|
||||
Task.FromResult<IShellWindow>(new TimeAndSalesWindow(this, _bus, _consumer, state, SparkCommands.PickTimeAndSales.icon,
|
||||
new WindowSettingsManager<GridSettings>(_settings_hub, state.grid, s => s.time_and_sales_grid, (s, g) => s.with(time_and_sales:g))));
|
||||
|
||||
public async void show(SecurityCriteria criteria) {
|
||||
var w = await load(new StartupData(criteria));
|
||||
@@ -40,19 +50,60 @@ namespace bts.shell {
|
||||
[ProtoMember(1)]
|
||||
public readonly SecurityCriteria security_criteria;
|
||||
[ProtoMember(2)]
|
||||
public readonly List<SortingCriteria> sorting_criteria;
|
||||
List<SortingCriteria> _sorting_criterion;
|
||||
[ProtoMember(3)]
|
||||
public FilterStates filters;
|
||||
[ProtoMember(4)]
|
||||
public GridSettings grid;
|
||||
|
||||
StartupData() { }//for ptorobuf
|
||||
public StartupData() { }
|
||||
|
||||
public StartupData(SecurityCriteria security_criteria)
|
||||
: this (security_criteria, new List<SortingCriteria>(), null) { }
|
||||
public StartupData(SecurityCriteria security_criteria) :
|
||||
this(security_criteria, new FilterStates(), null, null) { }
|
||||
|
||||
public StartupData(SecurityCriteria security_criteria, List<SortingCriteria> sorting_criteria, FilterStates filters) {
|
||||
public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List<SortingCriteria> sorting_criterion = null) {
|
||||
this.security_criteria = security_criteria;
|
||||
this.sorting_criteria = sorting_criteria;
|
||||
this.filters = filters;
|
||||
this.grid = grid;
|
||||
_sorting_criterion = sorting_criterion;
|
||||
}
|
||||
|
||||
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
|
||||
|
||||
public List<SortingCriteria> sorting_criterion => _sorting_criterion ?? (_sorting_criterion = DefaultColumns.default_sorting_criterion.ToList());
|
||||
|
||||
public static StartupData Default(SecurityCriteria security_criteria) =>
|
||||
new StartupData(security_criteria, new FilterStates(), null, DefaultColumns.default_sorting_criterion.ToList());
|
||||
|
||||
public StartupData with(SecurityCriteria security_criteria) =>
|
||||
new StartupData(security_criteria, filters, grid);
|
||||
|
||||
public static class DefaultColumns {
|
||||
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings time => new GridColumnSettings(col_id(Columns.Time), DefaultColumnHeaders.Time, width: DefaultColumnWidths.Time);
|
||||
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 IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(time.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return security;
|
||||
yield return time;
|
||||
yield return price;
|
||||
yield return size;
|
||||
yield return aggressor;
|
||||
yield return trade_key;
|
||||
yield return security_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,27 +46,22 @@
|
||||
Offset="{StaticResource RowHeight}"
|
||||
RowHeaderWidth="{StaticResource RowHeaderWidth}"
|
||||
/>
|
||||
<usp:DataGridParameterCellTemplateConverter x:Key="ParameterCellTemplateConverter">
|
||||
<DataTemplate x:Key="SimpleValueTextTemplate">
|
||||
<TextBlock Text="{Binding value}"/>
|
||||
</DataTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter
|
||||
x:Key="ParameterCellTemplateConverter"
|
||||
DoubleTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
IntTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
UIntTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
MarketDataTypeTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
MarketDataCalculationTemplate="{StaticResource SimpleValueTextTemplate}"
|
||||
>
|
||||
<usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox HorizontalAlignment="Left" IsChecked="{Binding value}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding value}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.IntTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding value}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.IntTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.UIntTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding value}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.UIntTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -117,6 +112,16 @@
|
||||
<c:TickUpDownUInt Value="{Binding value}" IsEnabled="{Binding enabled}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.UIntTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.MarketDataTypeTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:LegSpreaderParams.Types.MarketDataType}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.MarketDataTypeTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.MarketDataCalculationTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:LegSpreaderParams.Types.LegMarketDataCalculation}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.MarketDataCalculationTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
|
||||
<DataTemplate>
|
||||
<Grid IsEnabled="{Binding enabled}">
|
||||
|
||||
@@ -302,6 +302,8 @@ namespace mbts.ui.spark {
|
||||
public DataTemplate IntTemplate { get; set; }
|
||||
public DataTemplate UIntTemplate { get; set; }
|
||||
public DataTemplate QuantityMeasureTemplate { get; set; }
|
||||
public DataTemplate MarketDataTypeTemplate { get; set; }
|
||||
public DataTemplate MarketDataCalculationTemplate { get; set; }
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
Type t = (Type)value;
|
||||
@@ -310,6 +312,8 @@ namespace mbts.ui.spark {
|
||||
else if (t == typeof(int)) return IntTemplate;
|
||||
else if (t == typeof(uint)) return UIntTemplate;
|
||||
else if (t == typeof(QuantityMeasure)) return QuantityMeasureTemplate;
|
||||
else if (t == typeof(LegSpreaderParams.Types.MarketDataType)) return MarketDataTypeTemplate;
|
||||
else if (t == typeof(LegSpreaderParams.Types.LegMarketDataCalculation)) return MarketDataCalculationTemplate;
|
||||
else return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using bts.api;
|
||||
using bts.spark;
|
||||
using mbts;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
@@ -18,8 +19,8 @@ namespace bts.shell
|
||||
LatencyReportWindowFactory _lrwf;
|
||||
|
||||
[ImportingConstructor]
|
||||
public SpreaderShellModule(IBus bus, OrdersModel orders_model) {
|
||||
_smwf = new SyntheticManagementWindowFactory(this, bus, orders_model);
|
||||
public SpreaderShellModule(IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) {
|
||||
_smwf = new SyntheticManagementWindowFactory(this, bus, settings_hub, orders_model);
|
||||
_lrwf = new LatencyReportWindowFactory(this, bus);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<UserControl x:Class="mbts.ui.spark.SyntheticManagementView" x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls">
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.SyntheticManagementView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
Background="{StaticResource CommonBackground}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
@@ -10,71 +14,31 @@
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<c:DataGridEx
|
||||
x:Name="grid" BorderThickness="0" IsReadOnly="True" CanUserSortColumns="True"
|
||||
GridLinesVisibility="None" ItemsSource="{Binding rows_view}"
|
||||
CurrentItem="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
>
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_ladder}"/>
|
||||
<Setter Property="u:AttachedCommands.Enter" Value="{Binding show_ladder}"/>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.Columns>
|
||||
<!--<DataGridTextColumn
|
||||
Header="Create Time" Binding="{Binding create_time}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.create_time_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.create_time_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource TimeFilterTemplate}"
|
||||
/>-->
|
||||
<DataGridTextColumn
|
||||
Header="Name" Binding="{Binding name}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.name_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.name_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.name_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn
|
||||
Header="Description" Binding="{Binding description}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.description_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.description_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.description_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn
|
||||
Header="# Legs" Binding="{Binding n_legs}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.n_legs_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.n_legs_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.n_legs_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"
|
||||
/>
|
||||
<DataGridTemplateColumn
|
||||
Header="# Working Orders"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.n_working_orders_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.n_working_orders_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.n_working_orders_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"
|
||||
<AdornerDecorator>
|
||||
<Grid>
|
||||
<c:DIYGrid
|
||||
x:Name="_grid"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row}"
|
||||
Rows="{Binding rows_view}"
|
||||
>
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<Hyperlink Command="{Binding show_order_book}">
|
||||
<TextBlock Text="{Binding n_working_orders}"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn
|
||||
Header="Security Key" Binding="{Binding security_key}" Width="*"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.security_key_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_key_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_key_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
|
||||
/>
|
||||
</DataGrid.Columns>
|
||||
</c:DataGridEx>
|
||||
</Grid>
|
||||
<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.RowTemplate>
|
||||
<DataTemplate>
|
||||
<usp:SyntheticManagementRowView/>
|
||||
</DataTemplate>
|
||||
</c:DIYGrid.RowTemplate>
|
||||
</c:DIYGrid>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -1,12 +1,17 @@
|
||||
using mbts.spark.filters;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
/// <summary>
|
||||
/// Interaction logic for SyntheticManagementView.xaml
|
||||
/// </summary>
|
||||
internal partial class SyntheticManagementView : UserControl {
|
||||
partial class SyntheticManagementView : UserControl {
|
||||
|
||||
public enum Columns { Name, Description, n_legs, n_working_orders, SecurityKey}
|
||||
public SyntheticManagementView() =>
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
class SyntheticManagementRowView : DIYGrid.HighPerformanceRow<SyntheticManagementDialogVM.Row, SyntheticManagementView.Columns> { }
|
||||
}
|
||||
@@ -2,8 +2,11 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -13,27 +16,33 @@ using System.Windows;
|
||||
using static bts.shell.SyntheticManagementWindowFactory;
|
||||
|
||||
namespace mbts.spark {
|
||||
class SyntheticManagementWindow : SparkWindow<StartupData> {
|
||||
class SyntheticManagementWindow : SparkWindow<StartupData>, ISlowWindow {
|
||||
const string _window_type = "SyntheticManagement";
|
||||
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
|
||||
StartupData _state;
|
||||
OrdersModel _orders_model;
|
||||
SyntheticManagementView _view;
|
||||
IDisposable _subscriptions;
|
||||
IReadOnlyList<SparkCommand> _commands;
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
|
||||
public static SyntheticManagementWindow create(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state) {
|
||||
var window = new SyntheticManagementWindow(factory, bus, orders_model);
|
||||
window.initialize(state);
|
||||
return window;
|
||||
}
|
||||
|
||||
SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model)
|
||||
public SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, WindowSettingsManager<GridSettings> settings_manager)
|
||||
: base("Synthetic Management", bus, factory, SparkCommands.ShowSyntheticManagement.icon) {
|
||||
_orders_model = orders_model;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
Icon.Freeze();
|
||||
}
|
||||
|
||||
public void thread_init() {
|
||||
Log.notnull(_state);
|
||||
|
||||
var vm_connectable_builder = new ConnectableSeriesBuilder<SyntheticManagementDialogVM>(_vm.observable);
|
||||
var current_row_source = vm_connectable_builder.create(vm => vm.current_row_observable);
|
||||
var current_row_connectable_builder = new ConnectableSeriesBuilder<SyntheticManagementDialogVM.Row>(current_row_source);
|
||||
Commands = new SparkCommand[] {
|
||||
|
||||
_commands = new SparkCommand[] {
|
||||
SparkCommand.create("Add", Icons.AddSynthetic, SparkTags.ShowSyntheticCreateTag).complete(
|
||||
() => new SecurityCriteria(), () => true
|
||||
), SparkCommand.create("Remove", Icons.RemoveSynthetic, SparkTags.SyntheticDeleteTag).complete(
|
||||
@@ -51,7 +60,24 @@ namespace mbts.spark {
|
||||
filters = new OrderBookWindowFactory.FilterStates {
|
||||
security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security))
|
||||
}
|
||||
})
|
||||
}
|
||||
), SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
|
||||
_settings_manager,
|
||||
us => us.synthetic_management_grid,
|
||||
UserSettings.FileExtensions.GRID,
|
||||
s => _vm.value.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.with(_vm.value.columns.Select( c => c.create())),
|
||||
this.observe_title()
|
||||
)
|
||||
);
|
||||
// TODO: bind to main window's Title
|
||||
var dialog = new SimpleGridSettingsDialog { Title = "Synthetic Management Grid Settings", DataContext = vm };
|
||||
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
|
||||
return dialog;
|
||||
}))
|
||||
};
|
||||
|
||||
_subscriptions = Disposable.Create(
|
||||
@@ -69,21 +95,21 @@ namespace mbts.spark {
|
||||
uint select_security_id(RowModificationParams? rmp) => rmp.HasValue ? rmp.Value.security.Id : 0;
|
||||
(SecurityImmutable, ExecutionStrategyImmutable) select_row_strategy_tuple(RowStrategy? rs) => rs.HasValue ? (rs.Value.security, rs.Value.strategy) : default;
|
||||
bool can_execute(RowModificationParams? rmp) => rmp.HasValue && !rmp.Value.has_working_orders;
|
||||
}
|
||||
|
||||
public void initialize(StartupData state) {
|
||||
Log.notnull(state);
|
||||
_state = state;
|
||||
_vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, state.sorting_criteria, state.filters ?? new FilterStates());
|
||||
|
||||
_vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, _settings_manager, _state.sorting_criteria, _state.filters ?? new FilterStates());
|
||||
if (_view == null) _view = new SyntheticManagementView { DataContext = _vm.value };
|
||||
else _view.DataContext = _vm.value;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands { get; }
|
||||
public override StartupData SaveState => _state = new StartupData(_vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
|
||||
public override double MinWidth => 700;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override StartupData SaveState => _state = _vm.value.create();
|
||||
public override double MinWidth =>
|
||||
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public override void Dispose() {
|
||||
Disposable.dispose(ref _vm);
|
||||
|
||||
@@ -1,30 +1,46 @@
|
||||
using bts.api;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.SyntheticManagementView;
|
||||
|
||||
namespace bts.shell {
|
||||
class SyntheticManagementWindowFactory : ShellWindowFactory<SyntheticManagementWindowFactory.StartupData> {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
|
||||
public SyntheticManagementWindowFactory(SpreaderShellModule module, IBus bus, OrdersModel orders_model) : base(module, "SyntheticManagement") {
|
||||
public SyntheticManagementWindowFactory(SpreaderShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) : base(module, "SyntheticManagement") {
|
||||
_bus = bus;
|
||||
_orders_model = orders_model;
|
||||
_settings_hub = settings_hub;
|
||||
_dispose = bus.subscribe(SparkTags.ShowSyntheticManagementTag).OnDispatcher().Subscribe(show);
|
||||
}
|
||||
|
||||
public static string col_id(Columns col) => col.ToString();
|
||||
|
||||
public override Task<IShellWindow> load(StartupData state) =>
|
||||
Task.FromResult<IShellWindow>(SyntheticManagementWindow.create(this, _bus, _orders_model, state));
|
||||
Task.FromResult<IShellWindow>(_create(state));
|
||||
|
||||
public async void show(SparkTags.Nothing nothing) =>
|
||||
_bus.post(SparkTags.ShowWindow, await load(new StartupData()));
|
||||
|
||||
IShellWindow _create(StartupData state) {
|
||||
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
|
||||
state.grid,
|
||||
get: us => us.synthetic_management_grid,
|
||||
set: (us, gs) => us.with(provided_synthetic_management_gridsettings: gs));
|
||||
return new SyntheticManagementWindow(this, _bus, _orders_model, state, settings_manager);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class StartupData {
|
||||
public const int Version = 1;
|
||||
@@ -33,11 +49,41 @@ namespace bts.shell {
|
||||
public readonly List<SortingCriteria> sorting_criteria;
|
||||
[ProtoMember(2)]
|
||||
public readonly FilterStates filters;
|
||||
[ProtoMember(3)]
|
||||
public readonly GridSettings grid;
|
||||
|
||||
public StartupData() : this(new List<SortingCriteria>(), null) { }
|
||||
public StartupData(List<SortingCriteria> sorting_criteria, FilterStates filters) {
|
||||
public StartupData() : this(new List<SortingCriteria>(), null, null) { }
|
||||
public StartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, GridSettings grid = default) {
|
||||
this.sorting_criteria = sorting_criteria;
|
||||
this.filters = filters;
|
||||
this.grid = grid;
|
||||
}
|
||||
|
||||
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings name => new GridColumnSettings(col_id(Columns.Name), DefaultColumnHeaders.Name, width: DefaultColumnWidths.Security);
|
||||
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 IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(name.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return name;
|
||||
yield return description;
|
||||
yield return n_legs;
|
||||
yield return n_working_orders;
|
||||
yield return security_key;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using mbts.ui.spark;
|
||||
using LegMarketDataCalculation = bts.spark.LegSpreaderParams.Types.LegMarketDataCalculation;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class MarketDataCalculationModel : RowModelBase<LegMarketDataCalculation> {
|
||||
public override string display_name { get; } = "Market Data Calculation";
|
||||
public override string help_text { get; }
|
||||
public override string man_page { get; } = string.Empty;
|
||||
|
||||
public override LegMarketDataCalculation get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.LegMdCalc;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, LegMarketDataCalculation value) =>
|
||||
leg_tup.leg_params.LegMdCalc = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using mbts.ui.spark;
|
||||
using MarketDataType = bts.spark.LegSpreaderParams.Types.MarketDataType;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class MarketDataTypeModel : RowModelBase<MarketDataType> {
|
||||
public override string display_name { get; } = "Market Data Type";
|
||||
public override string help_text { get; }
|
||||
public override string man_page { get; } = string.Empty;
|
||||
|
||||
public override MarketDataType get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.MarketDataType;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, MarketDataType value) =>
|
||||
leg_tup.leg_params.MarketDataType = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,6 +203,8 @@ namespace mbts.spark {
|
||||
new IsQuoteModel(),
|
||||
new HedgeRatioModel(),
|
||||
new QuoteHiddenModel(),
|
||||
new MarketDataTypeModel(),
|
||||
//new MarketDataCalculationModel(),
|
||||
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
|
||||
quote_throttle_inside_ticks,
|
||||
quote_throttle_outside_ticks,
|
||||
|
||||
@@ -1,22 +1,31 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using static bts.shell.SyntheticManagementWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.SyntheticManagementView;
|
||||
|
||||
namespace mbts.spark {
|
||||
class SyntheticManagementDialogVM : SecurityMDGrid<SyntheticManagementDialogVM.Row, (ExecutionStrategyImmutable? strategy, SecurityMD md)> {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
|
||||
public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model) : this(bus, orders_model, null, null) { }
|
||||
public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IReadOnlyCollection<SortingCriteria> sorting, FilterStates filters)
|
||||
|
||||
public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IWindowSettingsManager<GridSettings> settings_manager) : this(bus, orders_model, settings_manager, null, null) { }
|
||||
public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IWindowSettingsManager<GridSettings> settings_manager, IReadOnlyCollection<SortingCriteria> sorting, FilterStates filters)
|
||||
: base(orders_model.spreader.strategy_tuple.Select(map => map.Values), sorting) {
|
||||
_bus = bus;
|
||||
_orders_model = orders_model;
|
||||
@@ -28,7 +37,41 @@ namespace mbts.spark {
|
||||
n_working_orders_filter.load_state(filters.n_working_orders);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
_dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter);
|
||||
_settings_manager = settings_manager;
|
||||
// columns
|
||||
add_col(Columns.Name, DefaultColumnHeaders.Name, r => r.name, nameof(Row.name), name_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Security);
|
||||
add_col(Columns.Description, DefaultColumnHeaders.Description, r => r.description, nameof(Row.description), description_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Description);
|
||||
add_col(Columns.n_legs, DefaultColumnHeaders.N_Legs, r => r.n_legs.ToString(), nameof(Row.n_legs), n_legs_filter, DataGridFilterKnownTemplates.IntMinMax, DefaultColumnWidths.N_Legs);
|
||||
columns.Add(new HyperlinkElementColumn<Row, Columns>(
|
||||
Columns.n_working_orders,
|
||||
new Binding(nameof(Row.n_working_orders)),
|
||||
new Binding(nameof(Row.show_order_book)),
|
||||
n_working_orders_filter,
|
||||
DataGridFilterKnownTemplates.UIntMinMax) {
|
||||
id = col_id(Columns.n_working_orders),
|
||||
Header = DefaultColumnHeaders.N_Working_Orders,
|
||||
Width = DefaultColumnWidths.N_Working_Orders
|
||||
}
|
||||
);
|
||||
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
|
||||
|
||||
//
|
||||
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, IDataGridColumnFilter filter, string filter_template, double width) =>
|
||||
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
|
||||
id = col_id(col_key),
|
||||
Header = header,
|
||||
sort_member_path = prop,
|
||||
Width = width
|
||||
});
|
||||
//
|
||||
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, row_vms);
|
||||
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
|
||||
grid_settings_manager.load_from(settings_manager.current_settings());
|
||||
|
||||
_resources.AddRange(
|
||||
DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager)
|
||||
);
|
||||
}
|
||||
|
||||
protected override Row row_factory((ExecutionStrategyImmutable? strategy, SecurityMD md) row_model) =>
|
||||
@@ -36,6 +79,20 @@ namespace mbts.spark {
|
||||
|
||||
protected override void on_row_removed(Row row) => row.Dispose();
|
||||
|
||||
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
|
||||
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
|
||||
|
||||
public GridSettings get_grid_save_state() {
|
||||
if (_settings_manager.instance_settings == null)
|
||||
return null;
|
||||
else
|
||||
return grid_settings_manager.create();
|
||||
}
|
||||
|
||||
public StartupData create() =>
|
||||
new StartupData(rows_view.get_sorting_criteria(columns), _get_filter_states(), get_grid_save_state());
|
||||
|
||||
|
||||
#region Filters
|
||||
public IDataGridColumnPersistentFilter<Row, TimeFilterTuple> create_time_filter { get; } = new TimeFilterVM<Row>(r => r.create_time, nameof(Row.create_time));
|
||||
public IDataGridColumnPersistentFilter<Row, string> name_filter { get; } = new RegexFilterVM<Row>(r => r.name, nameof(Row.name));
|
||||
@@ -44,7 +101,7 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> n_working_orders_filter { get; } = new UIntMinMax<Row>(r => r.n_working_orders, nameof(Row.n_legs));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
|
||||
public FilterStates get_filter_states() =>
|
||||
FilterStates _get_filter_states() =>
|
||||
new FilterStates {
|
||||
description = description_filter.get_state(),
|
||||
create_time = create_time_filter.get_state(),
|
||||
@@ -55,7 +112,7 @@ namespace mbts.spark {
|
||||
};
|
||||
#endregion
|
||||
|
||||
public class Row : SecurityMDRow, IEquatable<Row> {
|
||||
public class Row : SecurityMDRow, IEquatable<Row>, IRowVM, IActionableRowVM {
|
||||
public readonly ExecutionStrategyImmutable? strategy;
|
||||
|
||||
readonly IBus _bus;
|
||||
@@ -85,6 +142,17 @@ namespace mbts.spark {
|
||||
public uint n_working_orders => _n_working_orders.Value;
|
||||
public ICommand show_ladder { get; }
|
||||
public ICommand show_order_book { get; }
|
||||
public ICommand default_command => show_ladder;
|
||||
|
||||
|
||||
public ObservableCollection<RowAnimation> row_animations => new ObservableCollection<RowAnimation>();
|
||||
|
||||
public IReadOnlyList<IGridCellStore> cell_stores => new IGridCellStore[0];
|
||||
|
||||
|
||||
public event Action invalidated;
|
||||
|
||||
public void raise_invalidated() => invalidated?.Invoke();
|
||||
|
||||
public bool Equals(Row other) =>
|
||||
other != null &&
|
||||
@@ -96,6 +164,8 @@ namespace mbts.spark {
|
||||
public override int GetHashCode() =>
|
||||
security_key.GetHashCode();
|
||||
|
||||
|
||||
|
||||
void _show_ladder() =>
|
||||
_bus.post(SparkTags.ShowLadderTag, md);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.spark.filters;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
@@ -13,6 +14,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows.Data;
|
||||
using static bts.api.orderman.Fill.Types;
|
||||
using static bts.shell.FillsWindowFactory;
|
||||
@@ -23,13 +25,16 @@ using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
namespace mbts.spark {
|
||||
class FillsVM : SecurityGrid<FillsVM.Row, FillTuple> {
|
||||
readonly OrdersModel _model;
|
||||
|
||||
public FillsVM(IBus bus, OrdersModel model, StartupData state)
|
||||
: this(bus, model, _get_observable(bus, model, state), state.sorting_criteria, state.filters) { }
|
||||
IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
|
||||
FillsVM(IBus bus, OrdersModel model, IObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
|
||||
: base(RXCollection.CreateIncrementalUpdate(fills_source), sorting_criteria) {
|
||||
|
||||
public FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IWindowSettingsManager<GridSettings> settings_manager)
|
||||
: this(bus, model, state_obs, _get_observable(bus, model, state_obs), settings_manager) { }
|
||||
|
||||
FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IConnectableObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> fills_source, IWindowSettingsManager<GridSettings> settings_manager)
|
||||
: base(RXCollection.CreateIncrementalUpdate(fills_source), null) {
|
||||
_model = model;
|
||||
_settings_manager = settings_manager;
|
||||
account_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_accounts().Select(d => d.Values), r => r.account, nameof(Row.account));
|
||||
user_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_users().Select(d => d.Values), r => r.user, nameof(Row.user));
|
||||
security_filter = DiscreteValuesFilterVM.Create<Row, SecurityImmutable, SecurityCriteria>(
|
||||
@@ -44,30 +49,6 @@ namespace mbts.spark {
|
||||
SecurityDataHelper.EqualityComparer.IDComparer,
|
||||
Comparer<SecurityImmutable>.Default
|
||||
);
|
||||
if (filters != null) {
|
||||
fill_time_filter.load_state(filters.fill_time);
|
||||
time_in_force_filter.load_state(filters.time_in_force);
|
||||
order_type_filter.load_state(filters.order_type);
|
||||
account_filter.load_state(filters.account);
|
||||
user_filter.load_state(filters.user);
|
||||
exchange_filter.load_state(filters.exchange);
|
||||
security_filter.load_state(filters.security);
|
||||
side_filter.load_state(filters.side);
|
||||
price_filter.load_state(filters.price);
|
||||
size_filter.load_state(filters.size);
|
||||
fill_kind_filter.load_state(filters.fill_kind);
|
||||
fill_key_filter.load_state(filters.fill_key);
|
||||
order_key_filter.load_state(filters.order_key);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
} else {
|
||||
fill_time_filter.load_state(
|
||||
new TimeFilterTuple(ui.controls.TimeFilterMode.CMEOpen,
|
||||
TimeTextTuple.FromDateTime(DateTime.Today),
|
||||
TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1))
|
||||
)
|
||||
);
|
||||
fill_kind_filter.load_state(EnumHelper.get_values_except(FillKind.Sod).ToArray());
|
||||
}
|
||||
// columns
|
||||
add_col(Columns.Security, DefaultColumnHeaders.Security, r => r.security.ToString(), nameof(Row.security), security_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Security);
|
||||
add_col(Columns.FillTime, DefaultColumnHeaders.FillTime, r => r.fill_time.ToString("M/d h:mm:ss tt"), nameof(Row.fill_time), fill_time_filter, DataGridFilterKnownTemplates.TimeFilter, DefaultColumnWidths.Time);
|
||||
@@ -75,30 +56,83 @@ namespace mbts.spark {
|
||||
add_col(Columns.Account, DefaultColumnHeaders.Account, r => r.account, nameof(Row.account), account_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Account);
|
||||
add_col(Columns.Exchange, DefaultColumnHeaders.Exchange, r => r.exchange.ToString(), nameof(Row.exchange), exchange_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Exchange);
|
||||
add_col(Columns.TimeInForce, DefaultColumnHeaders.TimeInForce, r => r.time_in_force.ToString(), nameof(Row.time_in_force), time_in_force_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.TimeInForce);
|
||||
columns.Add(new OrderTypeColumn<Columns>(Columns.OrderType, new Binding(nameof(Row.order_type)), new Binding(nameof(Row.order_metadata)), order_type_filter) { sort_member_path = nameof(Row.order_type) });
|
||||
columns.Add(new SideColumn<Row, Columns>(Columns.Side, r => r.side, side_filter) { sort_member_path = nameof(Row.side) });
|
||||
columns.Add(new OrderTypeColumn<Columns>(Columns.OrderType, new Binding(nameof(Row.order_type)), new Binding(nameof(Row.order_metadata)), order_type_filter) { id = col_id(Columns.OrderType), sort_member_path = nameof(Row.order_type) });
|
||||
columns.Add(new SideColumn<Row, Columns>(Columns.Side, r => r.side, side_filter) { id = col_id(Columns.Side), sort_member_path = nameof(Row.side) });
|
||||
add_col(Columns.Size, DefaultColumnHeaders.Size, r => r.size.ToString(), nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.Size);
|
||||
add_col(Columns.Price, DefaultColumnHeaders.Price, r => r.format_price(r.price), nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price);
|
||||
add_col(Columns.FillKind, "Fill Kind", r => r.fill_kind.ToString(), nameof(Row.fill_kind), fill_kind_filter, DataGridFilterKnownTemplates.DiscreteValues, 70);
|
||||
add_col(Columns.FillKind, DefaultColumnHeaders.FillKind, r => r.fill_kind.ToString(), nameof(Row.fill_kind), fill_kind_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.FillKind);
|
||||
add_col(Columns.FillKey, DefaultColumnHeaders.FillKey, r => r.fill_key.ToString(), nameof(Row.fill_key), fill_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.FillKey);
|
||||
add_col(Columns.OrderKey, DefaultColumnHeaders.OrderKey, r => r.order_key.ToString(), nameof(Row.order_key), order_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.OrderKey);
|
||||
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
|
||||
//
|
||||
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, IDataGridColumnFilter filter, string filter_template, double width) =>
|
||||
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
|
||||
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
|
||||
id = col_id(col_key),
|
||||
Header = header,
|
||||
sort_member_path = prop,
|
||||
Width = width
|
||||
});
|
||||
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, row_vms);
|
||||
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
|
||||
grid_settings_manager.load_from(settings_manager.current_settings());
|
||||
|
||||
//
|
||||
_resources.Add(DataGridFilterHelper.hook_filters(rows_view,
|
||||
fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter,
|
||||
security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter
|
||||
));
|
||||
_resources.AddRange(
|
||||
state_obs.OnDispatcher().Subscribe(s =>
|
||||
{
|
||||
using (rows_view.DeferRefresh()) {
|
||||
if (s.grid != null) {
|
||||
// this means we are not using defaults,
|
||||
// Note: settings_manager in turn notifies grid_settings_manager of this update
|
||||
_settings_manager.instance_settings = s.grid;
|
||||
}
|
||||
if (s.filters is FilterStates filters) {
|
||||
fill_time_filter.load_state(filters.fill_time);
|
||||
time_in_force_filter.load_state(filters.time_in_force);
|
||||
order_type_filter.load_state(filters.order_type);
|
||||
account_filter.load_state(filters.account);
|
||||
user_filter.load_state(filters.user);
|
||||
exchange_filter.load_state(filters.exchange);
|
||||
security_filter.load_state(filters.security);
|
||||
side_filter.load_state(filters.side);
|
||||
price_filter.load_state(filters.price);
|
||||
size_filter.load_state(filters.size);
|
||||
fill_kind_filter.load_state(filters.fill_kind);
|
||||
fill_key_filter.load_state(filters.fill_key);
|
||||
order_key_filter.load_state(filters.order_key);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
} else {
|
||||
fill_time_filter.load_state(
|
||||
new TimeFilterTuple(ui.controls.TimeFilterMode.CMEOpen,
|
||||
TimeTextTuple.FromDateTime(DateTime.Today),
|
||||
TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1))
|
||||
)
|
||||
);
|
||||
fill_kind_filter.load_state(EnumHelper.get_values_except(FillKind.Sod).ToArray());
|
||||
}
|
||||
rows_view.set_sort_descriptions(s.sorting_criterion, columns);
|
||||
}
|
||||
}),
|
||||
DataGridFilterHelper.hook_filters(rows_view,
|
||||
fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter,
|
||||
security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter
|
||||
),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
fills_source.Connect()
|
||||
);
|
||||
}
|
||||
|
||||
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
|
||||
|
||||
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
|
||||
|
||||
public GridSettings get_grid_save_state() {
|
||||
if (_settings_manager.instance_settings == null)
|
||||
return null;
|
||||
else
|
||||
return grid_settings_manager.create();
|
||||
}
|
||||
|
||||
#region Filters
|
||||
public IDataGridColumnPersistentFilter<Row, TimeFilterTuple> fill_time_filter { get; } = new TimeFilterVM<Row>(r => r.fill_time, nameof(Row.fill_time));
|
||||
public IDataGridColumnPersistentFilter<Row, TimeInForce[]> time_in_force_filter { get; } = DiscreteValuesFilterVM.Create<Row, TimeInForce>(EnumHelper.get_values<TimeInForce>().ToLArray(), r => r.time_in_force, nameof(Row.time_in_force));
|
||||
@@ -144,6 +178,9 @@ namespace mbts.spark {
|
||||
} else return model.on_fill_tuples;
|
||||
}
|
||||
|
||||
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 {
|
||||
readonly OrdersModel _model;
|
||||
readonly FillImmutable _fill;
|
||||
|
||||
@@ -2,9 +2,16 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using StartupData = bts.shell.FillsWindowFactory.StartupData;
|
||||
|
||||
@@ -13,29 +20,76 @@ namespace mbts.spark {
|
||||
const string _window_type = "Fills", _thread_id = "Fills";
|
||||
readonly AllowOnce _init_guard = new AllowOnce("Fills");
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
FillsVM _vm;
|
||||
FillsView _view;
|
||||
StartupData _state;
|
||||
IReadOnlyList<SparkCommand> _commands;
|
||||
IDisposable _subscriptions;
|
||||
|
||||
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state)
|
||||
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, WindowSettingsManager<GridSettings> settings_manager)
|
||||
: base("Fills", bus, factory, SparkCommands.ShowFills.icon) {
|
||||
_orders_model = orders_model;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
}
|
||||
|
||||
event Action<FillsWindowFactory.StartupData> _state_changed;
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.GetSortingCriteria(), _vm.get_filters());
|
||||
public override double MinWidth => 1050 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
|
||||
public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.get_sorting_criteria(_vm.columns), _vm.get_filters(), _vm.get_grid_save_state());
|
||||
public override double MinWidth =>
|
||||
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public string thread_id => _thread_id;
|
||||
|
||||
public void thread_init() {
|
||||
_init_guard.check_access();
|
||||
Log.notnull(_state);
|
||||
_vm = new FillsVM(_bus, _orders_model, _state);
|
||||
var state_src = Observable.FromEvent<Action<FillsWindowFactory.StartupData>, FillsWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
|
||||
_vm = new FillsVM(_bus, _orders_model, state_src, _settings_manager);
|
||||
_view = new FillsView { DataContext = _vm };
|
||||
|
||||
_commands = new List<SparkCommand> {
|
||||
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
|
||||
SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
|
||||
_settings_manager,
|
||||
us => us.fills_grid,
|
||||
UserSettings.FileExtensions.GRID,
|
||||
s => _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.with(_vm.columns.Select( c => c.create())),
|
||||
this.observe_title()
|
||||
)
|
||||
);
|
||||
// TODO: bind to main window's Title
|
||||
var dialog = new SimpleGridSettingsDialog { Title = $"{_window_type} Grid Settings", DataContext = vm };
|
||||
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
|
||||
return dialog;
|
||||
}))
|
||||
};
|
||||
_subscriptions = Disposable.Create(() =>
|
||||
_state_changed?.Invoke(
|
||||
_state = new StartupData(_state.orders,
|
||||
_vm.rows_view.get_sorting_criteria(_vm.columns),
|
||||
_vm.get_filters(),
|
||||
_vm.grid_settings_manager.create()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public override void Dispose() => Disposable.dispose(ref _vm);
|
||||
public override void Dispose() {
|
||||
Disposable.dispose(ref _vm);
|
||||
Commands.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,118 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.FillsView;
|
||||
|
||||
namespace bts.shell {
|
||||
class FillsWindowFactory : ShellWindowFactory<FillsWindowFactory.StartupData> {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
|
||||
public FillsWindowFactory(TradingShellModule module, IBus bus, OrdersModel orders_model)
|
||||
public FillsWindowFactory(TradingShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model)
|
||||
: base(module, "Fills") {
|
||||
_bus = bus;
|
||||
_orders_model = orders_model;
|
||||
_settings_hub = settings_hub;
|
||||
_dispose = bus.subscribe(SparkTags.ShowFillsTag).OnDispatcher().Subscribe(show);
|
||||
}
|
||||
|
||||
public static string col_id(Columns col) => col.ToString();
|
||||
|
||||
public override Task<IShellWindow> load(StartupData state) =>
|
||||
Task.FromResult<IShellWindow>(new FillsWindow(this, _bus, _orders_model, state));
|
||||
Task.FromResult(_create(state));
|
||||
|
||||
public async void show(StartupData state) =>
|
||||
_bus.post(SparkTags.ShowWindow, await load(state));
|
||||
|
||||
IShellWindow _create(StartupData state) {
|
||||
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
|
||||
state.grid,
|
||||
get: us => us.fills_grid,
|
||||
set: (us, gs) => us.with(provided_fills_gridsettings: gs));
|
||||
return new FillsWindow(this, _bus, _orders_model, state, settings_manager);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class StartupData {
|
||||
public const int Version = 1;
|
||||
public StartupData() { }
|
||||
public StartupData(uint[] orders) : this(orders, null, null) { }
|
||||
public StartupData(FilterStates filters) : this(null, null, filters) { }
|
||||
public StartupData(uint[] orders, List<SortingCriteria> sorting_criteria, FilterStates filters) {
|
||||
this.orders = orders;
|
||||
this.sorting_criteria = sorting_criteria;
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
[ProtoMember(1)]
|
||||
public uint[] orders;
|
||||
[ProtoMember(2)]
|
||||
public List<SortingCriteria> sorting_criteria;
|
||||
List<SortingCriteria> _sorting_criteria;
|
||||
[ProtoMember(3)]
|
||||
public FilterStates filters;
|
||||
[ProtoMember(4)]
|
||||
public GridSettings grid;
|
||||
|
||||
public StartupData() { }
|
||||
public StartupData(uint[] orders) : this(orders, null, null) { }
|
||||
public StartupData(FilterStates filters) : this(null, null, filters) { }
|
||||
public StartupData(uint[] orders, List<SortingCriteria> sorting_criteria, FilterStates filters, GridSettings grid = default) {
|
||||
this.orders = orders;
|
||||
this._sorting_criteria = sorting_criteria;
|
||||
this.filters = filters;
|
||||
this.grid = grid;
|
||||
}
|
||||
|
||||
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
|
||||
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList());
|
||||
public static StartupData Default() =>
|
||||
new StartupData(null, null, new FilterStates(), null);
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings fill_time => new GridColumnSettings(col_id(Columns.FillTime), DefaultColumnHeaders.FillTime, width: DefaultColumnWidths.Time);
|
||||
public static GridColumnSettings user => new GridColumnSettings(col_id(Columns.User), DefaultColumnHeaders.User, width: DefaultColumnWidths.User);
|
||||
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange);
|
||||
public static GridColumnSettings time_in_force => new GridColumnSettings(col_id(Columns.TimeInForce), DefaultColumnHeaders.TimeInForce, width: DefaultColumnWidths.TimeInForce);
|
||||
public static GridColumnSettings order_type => new GridColumnSettings(col_id(Columns.OrderType), DefaultColumnHeaders.OrderType, width: DefaultColumnWidths.OrderType);
|
||||
public static GridColumnSettings side => new GridColumnSettings(col_id(Columns.Side), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Side);
|
||||
public static GridColumnSettings quantity => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind);
|
||||
public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey);
|
||||
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 IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(fill_time.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return security;
|
||||
yield return fill_time;
|
||||
yield return user;
|
||||
yield return account;
|
||||
yield return exchange;
|
||||
yield return time_in_force;
|
||||
yield return order_type;
|
||||
yield return side;
|
||||
yield return quantity;
|
||||
yield return price;
|
||||
yield return fill_kind;
|
||||
yield return fill_key;
|
||||
yield return order_key;
|
||||
yield return security_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -4,6 +4,7 @@ using mbts.config;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.spark;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -22,15 +23,17 @@ namespace bts.shell {
|
||||
readonly IWindowManager _wm;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly MarketDataAuxiliaryModel _md_auxiliary_model;
|
||||
readonly int _max_ladders_open;
|
||||
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
|
||||
|
||||
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model)
|
||||
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model, MarketDataAuxiliaryModel md_auxiliary_model)
|
||||
: base(owner, "SparkLadder") {
|
||||
_bus = bus;
|
||||
_wm = wm;
|
||||
_settings_hub = settings_hub;
|
||||
_orders_model = orders_model;
|
||||
_md_auxiliary_model = md_auxiliary_model;
|
||||
var knobs = Configuration.get<bts.config.LadderKnobs>();
|
||||
_max_ladders_open = (int)(knobs.max_open_ladders);
|
||||
_security_timeout = TimeSpan.FromSeconds(Math.Max(15, knobs.security_timeout_secs));
|
||||
@@ -56,7 +59,7 @@ namespace bts.shell {
|
||||
IShellWindow create_ladder_window(SecurityMD md, LadderSettings state_settings) {
|
||||
var id = Interlocked.Increment(ref _ladder_count) % Environment.ProcessorCount;
|
||||
var view = new Lazy<UIElement>(() => {
|
||||
var model = new LadderModel(md, _orders_model);
|
||||
var model = new LadderModel(md, _orders_model, _md_auxiliary_model);
|
||||
var settings_manager = new WindowSettingsManager<LadderSettings>(
|
||||
_settings_hub, state_settings,
|
||||
us => sanitize_ladder_settings(us.ladder),
|
||||
@@ -85,6 +88,12 @@ namespace bts.shell {
|
||||
s.session_high_line = LadderSettings.Default.session_high_line;
|
||||
if (s.session_range_fill == null)
|
||||
s.session_range_fill = LadderSettings.Default.session_range_fill;
|
||||
if (s.volume_column == null)
|
||||
s.volume_column = LadderSettings.Default.volume_column;
|
||||
if (s.size_display == null)
|
||||
s.size_display = MarketSizeDisplay.get_default();
|
||||
if (s.size_display.normal == null)
|
||||
s.size_display = s.size_display.with(MarketSizeDisplay.get_default().normal);
|
||||
s.row_height = LadderRender.measure_row_height(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.utility;
|
||||
using mbts.ui.utility.text;
|
||||
@@ -208,7 +209,7 @@ namespace mbts.spark {
|
||||
"Parent Key",
|
||||
nameof(OrderBookRowVM.parent_order_key),
|
||||
r => r.parent_order_key.store,
|
||||
80,
|
||||
DefaultColumnWidths.ParentKey,
|
||||
new RegexFilterVM<OrderBookRowVM>(
|
||||
r => r.parent_order_key.text,
|
||||
value_path(nameof(OrderBookRowVM.parent_order_key))),
|
||||
@@ -321,6 +322,7 @@ namespace mbts.spark {
|
||||
ListSortDirection? _cached_sort_direction;
|
||||
readonly string _filter_template;
|
||||
protected readonly Func<OrderBookRowVM, IOrderBookCellStore> _get_cell_store;
|
||||
GlyphContext _root_glyph_context, _child_glyph_context;
|
||||
|
||||
protected OrderbookColumnBase(Columns col_key, string header, Func<OrderBookRowVM, IOrderBookCellStore> get_cell_store, string sort_prop, double width, IDataGridColumnPersistentFilter<OrderBookRowVM, T> filter, string filter_template) : base(col_key) {
|
||||
id = key.ToString();
|
||||
@@ -331,6 +333,12 @@ namespace mbts.spark {
|
||||
this.filter = filter;
|
||||
_filter_template = filter_template;
|
||||
_cached_sort_direction = SortDirection;
|
||||
font_params_changed += refresh_glyph_contexts;
|
||||
refresh_glyph_contexts();
|
||||
void refresh_glyph_contexts() {
|
||||
_root_glyph_context = GlyphContext.create(font_family, RenderUtils.root_font_weight);
|
||||
_child_glyph_context = GlyphContext.create(font_family, RenderUtils.child_font_weight);
|
||||
}
|
||||
}
|
||||
|
||||
public IDataGridColumnPersistentFilter<OrderBookRowVM, T> filter { get; }
|
||||
@@ -346,6 +354,7 @@ namespace mbts.spark {
|
||||
_cached_sort_direction = new_value;
|
||||
base.OnSortDirectionChanged(new_value);
|
||||
}
|
||||
protected GlyphContext _get_glyph_context(OrderBookRowVM row) => row.is_child ? _child_glyph_context : _root_glyph_context;
|
||||
}
|
||||
|
||||
public class SimpleTextColumn<TFilterState> : OrderbookColumnBase<TFilterState>, IRenderColumn<OrderBookRowVM> {
|
||||
@@ -355,7 +364,7 @@ namespace mbts.spark {
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) {
|
||||
var cell = _get_cell_store(row);
|
||||
if (!string.IsNullOrEmpty(cell.current.text))
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, cell.current.text, position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, cell.current.text, position, RowHeight,
|
||||
font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, cell.animations);
|
||||
}
|
||||
}
|
||||
@@ -366,7 +375,7 @@ namespace mbts.spark {
|
||||
: base(Columns.Side, "B/S", r => r.side.store, nameof(OrderBookRowVM.side), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.side.text, position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.text, position, RowHeight,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.side.store.animations);
|
||||
}
|
||||
|
||||
@@ -377,18 +386,18 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.quantity.text, position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.text, position, RowHeight,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.quantity.store.animations);
|
||||
}
|
||||
|
||||
public class FilledColumn : OrderbookColumnBase<UIntMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public FilledColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, UIntMinMaxState> filter)
|
||||
: base(Columns.Filled, "Filled Qty", r => r.filled_quantity.store, nameof(OrderBookRowVM.filled_quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax) {
|
||||
: base(Columns.Filled, "Filled Qty", r => r.filled_quantity.store, nameof(OrderBookRowVM.filled_quantity), DefaultColumnWidths.Filled, filter, DataGridFilterKnownTemplates.UIntMinMax) {
|
||||
sort_member_path = nameof(OrderBookRowVM.filled_quantity);
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.filled_quantity.text, position, RowHeight,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.text, position, RowHeight,
|
||||
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.filled_quantity.store.animations);
|
||||
}
|
||||
|
||||
@@ -397,26 +406,26 @@ namespace mbts.spark {
|
||||
: base(Columns.Price, "Price", r => r.price.store, nameof(OrderBookRowVM.price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.price.text,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.price.text,
|
||||
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.price.store.animations);
|
||||
}
|
||||
|
||||
public class AvgFilledColumn : OrderbookColumnBase<DoubleMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public AvgFilledColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, DoubleMinMaxState> filter)
|
||||
: base(Columns.AvgFilled, "Avg Filled", r => r.avg_price.store, nameof(OrderBookRowVM.avg_price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { }
|
||||
: base(Columns.AvgFilled, "Avg Filled", r => r.avg_price.store, nameof(OrderBookRowVM.avg_price), DefaultColumnWidths.AveragePrice, filter, DataGridFilterKnownTemplates.DoubleMinMax) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) {
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.avg_price.text,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.avg_price.text,
|
||||
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.avg_price.store.animations);
|
||||
}
|
||||
}
|
||||
|
||||
public class LastFilledColumn : OrderbookColumnBase<DoubleMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public LastFilledColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, DoubleMinMaxState> filter)
|
||||
: base(Columns.LastPrice, "Last Filled", r => r.last_price.store, nameof(OrderBookRowVM.last_price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { }
|
||||
: base(Columns.LastPrice, "Last Filled", r => r.last_price.store, nameof(OrderBookRowVM.last_price), DefaultColumnWidths.LastPrice, filter, DataGridFilterKnownTemplates.DoubleMinMax) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.last_price.text,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.last_price.text,
|
||||
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.last_price.store.animations);
|
||||
}
|
||||
|
||||
@@ -425,7 +434,7 @@ namespace mbts.spark {
|
||||
: base(Columns.Time, "Time", r => r.time_created.store, nameof(OrderBookRowVM.time_created), DefaultColumnWidths.Time, filter, DataGridFilterKnownTemplates.TimeFilter) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.time_created.text,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.time_created.text,
|
||||
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.time_created.store.animations);
|
||||
}
|
||||
|
||||
@@ -434,7 +443,7 @@ namespace mbts.spark {
|
||||
: base(Columns.LastChange, "Last Change", r => r.last_change.store, nameof(OrderBookRowVM.last_change), DefaultColumnWidths.Time, filter, DataGridFilterKnownTemplates.TimeFilter) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.last_change.text,
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.last_change.text,
|
||||
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.last_change.store.animations);
|
||||
}
|
||||
|
||||
@@ -470,7 +479,7 @@ namespace mbts.spark {
|
||||
id = key.ToString();
|
||||
this.filter = filter;
|
||||
Header = "Last Message";
|
||||
Width = 80;
|
||||
Width = DIYGrid.DefaultColumnWidths.LastMessage;
|
||||
sort_member_path = nameof(OrderBookRowVM.last_msg);
|
||||
tooltip_binding = new Binding(nameof(OrderBookRowVM.last_msg));
|
||||
}
|
||||
@@ -521,7 +530,9 @@ namespace mbts.spark {
|
||||
toggle_button.SetBinding(ToggleButton.VisibilityProperty, new Binding(nameof(OrderBookRowVM.is_parent)) { Converter = _expander_visiblilty_converter });
|
||||
toggle_button.SetResourceReference(ToggleButton.StyleProperty, ExpandCollapseToggleStyleKey);
|
||||
//
|
||||
var security_view = new TextBlock { FontFamily = font_family, FontSize = font_size, Foreground = font_brush };
|
||||
var security_view = new TextBlock { Foreground = font_brush };
|
||||
security_view.SetBinding(TextBlock.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
|
||||
security_view.SetBinding(TextBlock.FontSizeProperty, new Binding(nameof(font_size)) { Source = this });
|
||||
security_view.SetBinding(TextBlock.TextProperty, $"{nameof(OrderBookRowVM.security)}.{nameof(IGridCell.text)}");
|
||||
security_view.SetBinding(TextBlock.HorizontalAlignmentProperty, new Binding(nameof(horizontal_alignment)) { Source = this });
|
||||
security_view.SetBinding(TextBlock.ForegroundProperty, new Binding(nameof(font_brush)) { Source = this });
|
||||
@@ -548,7 +559,10 @@ namespace mbts.spark {
|
||||
#endregion
|
||||
|
||||
static class RenderUtils {
|
||||
public static readonly GlyphContext _child_glyph_context = TextUtils.StandardGlyphContext.with(FontWeights.Light);
|
||||
public static FontWeight
|
||||
root_font_weight = FontWeights.Normal,
|
||||
child_font_weight = FontWeights.Light;
|
||||
|
||||
public static double font_size = FontSizes.Small;
|
||||
|
||||
public static readonly IValueConverter
|
||||
@@ -567,7 +581,6 @@ namespace mbts.spark {
|
||||
return mb;
|
||||
}
|
||||
|
||||
public static GlyphContext get_glyph_context(OrderBookRowVM r) => r.is_child ? _child_glyph_context : TextUtils.StandardGlyphContext;
|
||||
public static Brush get_side_brush(OrderBookRowVM r) => r.side.value == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask;
|
||||
public static BindingBase get_font_weight_binding() => new Binding(nameof(OrderBookRowVM.is_child)) { Converter = _font_weight_converter };
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using mbts.collections;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
@@ -23,12 +24,17 @@ using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using static bts.shell.OrderBookWindowFactory;
|
||||
using static mbts.spark.OrderBookView;
|
||||
using _GridSettingsManager = mbts.ui.GridSettingsManager<mbts.spark.OrderBookRowVM, mbts.spark.OrderRecordTuple, bts.shell.INotificationServiceDataCache, mbts.OrderRecordTuplePredicate, bts.shell.OrderBookWindowFactory.OrderBookGridSettings>;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class OrderBookVM : DisposableViewModelBase, IHandleRowSelection {
|
||||
readonly LiveSortingColumKeys _live_sorting_columns;
|
||||
readonly Property<OrderBookRowVM> _current_row;
|
||||
readonly Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)> _order_securities_subject =
|
||||
new Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)>();
|
||||
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
|
||||
OrderBookStartupData _state;
|
||||
|
||||
Dictionary<uint, OrderBookRowVM> _order_rows_cache = new Dictionary<uint, OrderBookRowVM>();
|
||||
Dictionary<uint, BranchFilterSource> _branch_filter_roots = new Dictionary<uint, BranchFilterSource>();
|
||||
ObservableCollection<OrderBookRowVM> _order_rows = new ObservableCollection<OrderBookRowVM>();
|
||||
@@ -36,7 +42,8 @@ namespace mbts.spark {
|
||||
SetMap<uint, uint> _parent_to_children = new SetMap<uint, uint>();
|
||||
SetMap<uint, uint> _security_orders = new SetMap<uint, uint>();
|
||||
|
||||
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model) {
|
||||
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager<OrderBookGridSettings> settings_manager) {
|
||||
_settings_manager = settings_manager;
|
||||
var order_securities_subject = new Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)>();
|
||||
columns = new OrderBookColumnCollection(bus, model, order_securities_subject);
|
||||
rows_view = CollectionViewSource.GetDefaultView(_order_rows);
|
||||
@@ -63,6 +70,7 @@ namespace mbts.spark {
|
||||
});
|
||||
|
||||
grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, _order_rows, rows_view, columns);
|
||||
grid_settings_manager.load_from(OrderBookStartupData.DefaultGrid);
|
||||
_live_sorting_columns = new LiveSortingColumKeys(grid_settings_manager.live_sorting_props);
|
||||
|
||||
SetMap<uint, uint> missing_security_orders = new SetMap<uint, uint>();
|
||||
@@ -71,6 +79,7 @@ namespace mbts.spark {
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(on_securities),
|
||||
model.on_orders.OnDispatcher().Subscribe(on_orders),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
grid_settings_manager,
|
||||
columns,
|
||||
order_securities_subject);
|
||||
@@ -215,7 +224,7 @@ namespace mbts.spark {
|
||||
public OrderRecordImmutable? current_order => current_row?.order;
|
||||
public IEnumerable<OrderRecordImmutable> selected_orders => _selected_rows.Select(r => r.order);
|
||||
|
||||
public GridSettingsManager<OrderBookRowVM, OrderRecordTuple, INotificationServiceDataCache, OrderRecordTuplePredicate> grid_settings_manager { get; }
|
||||
public _GridSettingsManager grid_settings_manager { get; }
|
||||
public ICommand collapse_all_rows_command { get; }
|
||||
public ICommand expand_all_rows_command { get; }
|
||||
|
||||
@@ -225,14 +234,17 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public OrderBookStartupData create() {
|
||||
var (grid, animations) = grid_settings_manager.create();
|
||||
return new OrderBookStartupData(columns.create(), grid, animations.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications)));
|
||||
if (_settings_manager.instance_settings == null)
|
||||
return new OrderBookStartupData(columns.create(), use_defaults: true);
|
||||
var gs = grid_settings_manager.create();
|
||||
return new OrderBookStartupData(columns.create(), gs.general, gs.predicate_animations, use_defaults: false);
|
||||
}
|
||||
|
||||
public void load_from(OrderBookStartupData state) {
|
||||
if (state == null) return;
|
||||
_state = state;
|
||||
grid_settings_manager.load_from((state.grid, state.predicate_animations));
|
||||
var gs = state.grid_settings_wrapper();
|
||||
if (gs != null) grid_settings_manager.load_from(gs);
|
||||
columns.load_from(state.filters);
|
||||
}
|
||||
|
||||
@@ -242,7 +254,7 @@ namespace mbts.spark {
|
||||
protected override void Dispose(bool disposing) =>
|
||||
Disposable.dispose(_order_rows);
|
||||
|
||||
static GridSettingsManager<OrderBookRowVM, OrderRecordTuple, INotificationServiceDataCache, OrderRecordTuplePredicate> _create_grid_settings_manager(
|
||||
static _GridSettingsManager _create_grid_settings_manager(
|
||||
IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model,
|
||||
ObservableCollection<OrderBookRowVM> rows, ICollectionView rows_view, ObservableCollection<DIYGrid.ColumnBase> columns) {
|
||||
// make font-weight immutable for all columns (the font-weight rule is hard-coded: Regular for root-orders, Light for all others)
|
||||
@@ -254,7 +266,7 @@ namespace mbts.spark {
|
||||
foreach (var c in new[] { Columns.Side, Columns.Size, Columns.Filled }) {
|
||||
col_settings_flags[c.ToString()] = GridColumnSettingsFeatureFlags.ImmutableFontColor | GridColumnSettingsFeatureFlags.ImmutableFontWeight;
|
||||
}
|
||||
return new GridSettingsManager<OrderBookRowVM, OrderRecordTuple, INotificationServiceDataCache, OrderRecordTuplePredicate>(
|
||||
return new _GridSettingsManager(
|
||||
owner_title, bus, notification_service, model, rows, rows_view, columns,
|
||||
() => {
|
||||
var pvm = PredicateBuilderVMWrapper.create<OrderRecordTuple, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
|
||||
@@ -267,7 +279,9 @@ namespace mbts.spark {
|
||||
OrderRecordTuplePredicateVMFactory.get_oneofs(bus, model), rp => rp.predicate_type_case);
|
||||
pvm.selected_predicate_type = _corresponding_predicate_type(EnumHelper.parse_or_default<Columns>(col_id));
|
||||
return pvm;
|
||||
}, notification_service.data_cache, col_settings_flags);
|
||||
},
|
||||
(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);
|
||||
}
|
||||
|
||||
static OrderRecordTuplePredicate.PredicateTypeOneofCase _corresponding_predicate_type(Columns col) {
|
||||
|
||||
@@ -2,18 +2,27 @@
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
//using IOrderBookWSM = mbts.persist.IWindowSettingsManager<mbts.spark.GridSettingsWithNotifications<mbts.spark.OrderRecordTuple, mbts.OrderRecordTuplePredicate>>;
|
||||
using OrderBookGridSettings = bts.shell.OrderBookWindowFactory.OrderBookGridSettings;
|
||||
using OrderBookGridSettingsVM = mbts.spark.GridSettingsVM<mbts.spark.OrderRecordTuple, mbts.OrderRecordTuplePredicate, bts.shell.OrderBookWindowFactory.OrderBookGridSettings>;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal class OrderBookWindow : SparkWindow<OrderBookWindowFactory.OrderBookStartupData>, ISharedWindow {
|
||||
const string _window_type = "OrderBook", ORDERBOOK_THREAD_ID = "OrderBook";
|
||||
const string _window_type = "OrderBook", ORDERBOOK_THREAD_ID = "OrderBook", _default_title = "Order Book";
|
||||
readonly INotificationService _notification_service;
|
||||
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
|
||||
OrderBookWindowFactory.OrderBookStartupData _state;
|
||||
OrdersModel _model;
|
||||
OrderBookVM _vm;
|
||||
@@ -32,12 +41,14 @@ namespace mbts.spark {
|
||||
, _toggle_expand_all_command
|
||||
, _show_grid_settings_command;
|
||||
|
||||
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service, OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon)
|
||||
: base("Order Book", bus, factory, SparkCommands.ShowOrders.icon) {
|
||||
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service,
|
||||
OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon, IWindowSettingsManager<OrderBookGridSettings> settings_manager)
|
||||
: base(_default_title, bus, factory, SparkCommands.ShowOrders.icon) {
|
||||
_model = model;
|
||||
_factory = factory;
|
||||
_notification_service = notification_service;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
}
|
||||
|
||||
public override OrderBookWindowFactory.OrderBookStartupData SaveState => _state = _vm.create();
|
||||
@@ -45,8 +56,11 @@ namespace mbts.spark {
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override double MinWidth => 1577;
|
||||
|
||||
public override double MinWidth =>
|
||||
OrderBookWindowFactory.OrderBookStartupData.DefaultGrid.general.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
public string thread_id => ORDERBOOK_THREAD_ID;
|
||||
|
||||
public override void Dispose() {
|
||||
@@ -55,7 +69,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public void thread_init() {
|
||||
_vm = new OrderBookVM(this.observe_title(), _bus, _notification_service, _model);
|
||||
_vm = new OrderBookVM(this.observe_title(), _bus, _notification_service, _model, _settings_manager);
|
||||
_vm.load_from(_state);
|
||||
_view = new OrderBookView(_vm);
|
||||
|
||||
@@ -106,9 +120,32 @@ namespace mbts.spark {
|
||||
Icons.DownArrow,
|
||||
_vm.expand_all_rows_command);
|
||||
|
||||
_show_grid_settings_command = SparkCommands.window_settings_command(_vm.grid_settings_manager.launch_settings_editor);
|
||||
_show_grid_settings_command = SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var vm = new WindowSettingsDialogVM<OrderBookGridSettings, OrderBookGridSettingsVM>(
|
||||
_settings_manager,
|
||||
us => us.order_book_grid,
|
||||
UserSettings.FileExtensions.GRID,
|
||||
s => {
|
||||
if (s.general is null)
|
||||
s = OrderBookWindowFactory.OrderBookStartupData.DefaultGrid;
|
||||
return _vm.grid_settings_manager.create_vm(
|
||||
// we need to get the most up to date GridSettings when launching b/c users may
|
||||
// have changed some settings on the grid directly, like column widths.
|
||||
s.general.with(_vm.columns.Select(c => c.create())),
|
||||
this.observe_title()
|
||||
);
|
||||
}
|
||||
);
|
||||
// TODO: bind to main window's Title
|
||||
var dialog = new SimpleGridSettingsDialog { DataContext = vm };
|
||||
dialog.SetBinding(Window.TitleProperty, new Binding($"{nameof(vm.instance_vm)}.{nameof(OrderBookGridSettingsVM.owner_title)}") {
|
||||
Converter = ValueConverter.create<string, string>(str => (string.IsNullOrEmpty(str) ? _default_title : str) + " Settings")
|
||||
});
|
||||
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
|
||||
return dialog;
|
||||
}));
|
||||
|
||||
_commands = new SparkCommand[] {
|
||||
_commands = new SparkCommand[] {
|
||||
_show_fills_command,
|
||||
_modify_order_command,
|
||||
_cancel_selected_orders_command,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using Google.Protobuf.Collections;
|
||||
using bts.utils;
|
||||
using mbts;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
@@ -11,6 +12,7 @@ using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
@@ -22,19 +24,22 @@ namespace bts.shell {
|
||||
readonly INotificationService _notification_service;
|
||||
readonly INotificationServiceDataCache _notification_service_data_cache;
|
||||
readonly OrdersModel _model;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
|
||||
public OrderBookWindowFactory(IShellModule owner, IBus bus, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, OrdersModel model) : base(owner, "SparkOrderBookWindow") {
|
||||
public OrderBookWindowFactory(IShellModule owner, IBus bus, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, OrdersModel model, UserSettingsHub settings_hub) : base(owner, "SparkOrderBookWindow") {
|
||||
_bus = bus;
|
||||
_notification_service = notification_service;
|
||||
_notification_service_data_cache = notification_service_data_cache;
|
||||
_model = model;
|
||||
|
||||
_settings_hub = settings_hub;
|
||||
_dispose = bus.subscribe(SparkTags.ShowOrderBookTag).OnDispatcher().Subscribe(show);
|
||||
}
|
||||
|
||||
public override Task<IShellWindow> load(OrderBookStartupData state) {
|
||||
var w = new OrderBookWindow(_model, this, _bus, _notification_service, state, SparkCommands.ShowOrders.icon) as IShellWindow;
|
||||
return Task.FromResult(w);
|
||||
var settings_manager = new WindowSettingsManager<OrderBookGridSettings>(_settings_hub, state.grid_settings_wrapper(),
|
||||
us => us.order_book_grid, (us, s) => us.with(order_book_grid: s));
|
||||
var w = new OrderBookWindow(_model, this, _bus, _notification_service, state, SparkCommands.ShowOrders.icon, settings_manager.as_non_shared());
|
||||
return Task.FromResult<IShellWindow>(w);
|
||||
}
|
||||
|
||||
async void show(OrderBookStartupData startup_data) {
|
||||
@@ -49,27 +54,42 @@ namespace bts.shell {
|
||||
[ProtoMember(10)]
|
||||
public FilterStates filters;
|
||||
[ProtoMember(15)]
|
||||
[Obsolete]
|
||||
public GridSettings grid;
|
||||
[ProtoMember(20)]
|
||||
[Obsolete]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
[ProtoMember(25)]
|
||||
public bool use_defaults;
|
||||
|
||||
public OrderBookStartupData() { }
|
||||
|
||||
public OrderBookStartupData(FilterStates filters, GridSettings grid = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null) {
|
||||
public OrderBookStartupData(FilterStates filters, GridSettings grid = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null, bool use_defaults = false) {
|
||||
this.filters = filters;
|
||||
this.use_defaults = use_defaults;
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
this.grid = grid;
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
_predicate_animations.AddRange(predicate_animations);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
[Obsolete]
|
||||
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
|
||||
public OrderBookGridSettings grid_settings_wrapper() => use_defaults ? null : new OrderBookGridSettings(grid, predicate_animations);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
|
||||
public static OrderBookStartupData Default =>
|
||||
new OrderBookStartupData(
|
||||
new FilterStates {
|
||||
statuses = new[] { OrderStatus.Working }
|
||||
}, GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion));
|
||||
}, _default_general_grid, use_defaults: true);
|
||||
|
||||
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid);
|
||||
|
||||
static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion);
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security);
|
||||
@@ -85,14 +105,14 @@ namespace bts.shell {
|
||||
public static GridColumnSettings side => new GridColumnSettings(Columns.Side.ToString(), "B/S", width: DIYGrid.DefaultColumnWidths.Side);
|
||||
public static GridColumnSettings quantity => new GridColumnSettings(Columns.Size.ToString(), "Qty", width: DIYGrid.DefaultColumnWidths.Size, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings filled_quantity => new GridColumnSettings(Columns.Filled.ToString(), "Filled Qty", width: DIYGrid.DefaultColumnWidths.Size, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings avg_price => new GridColumnSettings(Columns.AvgFilled.ToString(), "Avg Filled", width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings filled_quantity => new GridColumnSettings(Columns.Filled.ToString(), "Filled Qty", width: DIYGrid.DefaultColumnWidths.Filled, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings avg_price => new GridColumnSettings(Columns.AvgFilled.ToString(), "Avg Filled", width: DIYGrid.DefaultColumnWidths.AveragePrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right);
|
||||
public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80);
|
||||
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: 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 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.OrderKey);
|
||||
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey);
|
||||
|
||||
public static IEnumerable<SortingCriteria> sorting_criterion {
|
||||
get {
|
||||
@@ -171,5 +191,38 @@ namespace bts.shell {
|
||||
[ProtoMember(20)]
|
||||
public string[] symbols;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OrderBookGridSettings : IGridSettingsWithNotifications<OrderRecordTuple, OrderRecordTuplePredicate>, IEquatable<OrderBookGridSettings> {
|
||||
[ProtoMember(1)]
|
||||
public GridSettings general { get; set; }
|
||||
[ProtoMember(2)]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
|
||||
public OrderBookGridSettings() { }
|
||||
public OrderBookGridSettings(GridSettings general = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null) {
|
||||
this.general = general;
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
_predicate_animations.AddRange(predicate_animations);
|
||||
}
|
||||
|
||||
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
|
||||
IEnumerable<IPredicateAnimationsData<OrderRecordTuple, OrderRecordTuplePredicate>> IGridSettingsWithNotifications<OrderRecordTuple, OrderRecordTuplePredicate>.predicate_animations => predicate_animations;
|
||||
|
||||
public OrderBookGridSettings with(GridSettings general) =>
|
||||
new OrderBookGridSettings(general ?? this.general, predicate_animations);
|
||||
|
||||
public bool Equals(OrderBookGridSettings other) =>
|
||||
other != null &&
|
||||
EqualityComparer.null_check_equals(general, other.general) &&
|
||||
EqualityComparer.safe_sequence_equals(predicate_animations, other.predicate_animations);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as OrderBookGridSettings);
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
EqualityComparer.safe_get_hash_code(general),
|
||||
EqualityComparer.safe_aggregate_hash(predicate_animations));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,38 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static mbts.spark.PAndLView;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
|
||||
namespace bts.shell {
|
||||
internal class PAndLWindowFactory : ShellWindowFactory<PAndLWindowFactory.PAndLStartupData> {
|
||||
readonly IBus _bus;
|
||||
readonly PAndLModel _model;
|
||||
public PAndLWindowFactory(IShellModule owner, IBus bus, PAndLModel model) : base(owner, "SparkPAndL") {
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
|
||||
public PAndLWindowFactory(IShellModule owner, IBus bus, UserSettingsHub settings_hub, PAndLModel model) : base(owner, "SparkPAndL") {
|
||||
_bus = bus;
|
||||
_model = model;
|
||||
|
||||
_settings_hub = settings_hub;
|
||||
_dispose = bus.subscribe(SparkTags.ShowPAndLTag).OnDispatcher().Subscribe(show);
|
||||
}
|
||||
|
||||
public static string col_id(Columns col) => col.ToString();
|
||||
|
||||
public override Task<IShellWindow> load(PAndLStartupData state) {
|
||||
var w = new PAndLWindow(_model, this, _bus, state, SparkCommands.ShowPAndL.icon) as IShellWindow;
|
||||
return Task.FromResult(w);
|
||||
return Task.FromResult(_create(state));
|
||||
}
|
||||
|
||||
async void show(PAndLStartupData startup_data) {
|
||||
@@ -32,6 +40,14 @@ namespace bts.shell {
|
||||
_bus.post(SparkTags.ShowWindow, w);
|
||||
}
|
||||
|
||||
IShellWindow _create(PAndLStartupData state) {
|
||||
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
|
||||
state.grid,
|
||||
get: us => us.pnl_grid,
|
||||
set: (us, gs) => us.with(provided_pnl_gridsettings: gs));
|
||||
return new PAndLWindow(_model, this, _bus, state, SparkCommands.ShowPAndL.icon, settings_manager);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class PAndLStartupData {
|
||||
|
||||
@@ -40,21 +56,75 @@ namespace bts.shell {
|
||||
/// </summary>
|
||||
public PAndLStartupData() { }
|
||||
|
||||
public PAndLStartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, pnl_calc_type calc_type) {
|
||||
this.sort_criteria = sorting_criteria;
|
||||
public PAndLStartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, pnl_calc_type calc_type, GridSettings grid = default) {
|
||||
_sort_criteria = sorting_criteria;
|
||||
this.filters = filters;
|
||||
this.calc_type = calc_type;
|
||||
this.grid = grid;
|
||||
}
|
||||
|
||||
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
|
||||
|
||||
public IReadOnlyList<SortingCriteria> sorting_criterion => _sort_criteria ?? (_sort_criteria = DefaultColumns.default_sorting_criterion.ToList());
|
||||
public static PAndLStartupData Default() =>
|
||||
new PAndLStartupData(null, new FilterStates(), pnl_calc_type.midpoint);
|
||||
|
||||
|
||||
[ProtoMember(5)]
|
||||
public readonly List<SortingCriteria> sort_criteria;
|
||||
List<SortingCriteria> _sort_criteria;
|
||||
[ProtoMember(10)]
|
||||
public FilterStates filters;
|
||||
[ProtoMember(15)]
|
||||
public pnl_calc_type calc_type;
|
||||
//[ProtoMember(20)]
|
||||
// ProtoMember 20 cannot be used further
|
||||
[ProtoMember(25)]
|
||||
public GridSettings grid;
|
||||
|
||||
static class DefaultColumns {
|
||||
|
||||
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange);
|
||||
public static GridColumnSettings symbol => new GridColumnSettings(col_id(Columns.Symbol), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol);
|
||||
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings security_type => new GridColumnSettings(col_id(Columns.SecurityType), DefaultColumnHeaders.SecurityType, width: DefaultColumnWidths.SecurityType);
|
||||
public static GridColumnSettings start_net_position => new GridColumnSettings(col_id(Columns.StartNetPos), DefaultColumnHeaders.StartNetPos, width: DefaultColumnWidths.Start_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings day_long_pnl => new GridColumnSettings(col_id(Columns.DayLong), DefaultColumnHeaders.DayLong, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings day_short_pnl => new GridColumnSettings(col_id(Columns.DayShort), DefaultColumnHeaders.DayShort, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings day_pnl => new GridColumnSettings(col_id(Columns.DayPNL), DefaultColumnHeaders.DayPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, width: DefaultColumnWidths.Total_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings pnl_price => new GridColumnSettings(col_id(Columns.PNLPrice), DefaultColumnHeaders.PnLPrice, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
yield return new SortingCriteria(account.id, true);
|
||||
yield return new SortingCriteria(exchange.id, true);
|
||||
yield return new SortingCriteria(symbol.id, true);
|
||||
yield return new SortingCriteria(security.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return account;
|
||||
yield return exchange;
|
||||
yield return symbol;
|
||||
yield return security;
|
||||
yield return security_type;
|
||||
yield return start_net_position;
|
||||
yield return start_pnl;
|
||||
yield return day_long_pnl;
|
||||
yield return day_short_pnl;
|
||||
yield return day_pnl;
|
||||
yield return total_net_pos;
|
||||
yield return total_pnl;
|
||||
yield return pnl_price;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.api;
|
||||
using bts.api.md.auxiliary;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
@@ -19,11 +20,12 @@ namespace bts.shell {
|
||||
PAndLWindowFactory _plwf;
|
||||
|
||||
[ImportingConstructor]
|
||||
public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model) {
|
||||
_lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model);
|
||||
_obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model);
|
||||
_fwf = new FillsWindowFactory(this, bus, orders_model);
|
||||
_plwf = new PAndLWindowFactory(this, bus, pandl_model);
|
||||
public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache,
|
||||
UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model, MarketDataAuxiliaryModel md_auxiliary_model) {
|
||||
_lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model, md_auxiliary_model);
|
||||
_obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model, settings_hub);
|
||||
_fwf = new FillsWindowFactory(this, bus, settings_hub, orders_model);
|
||||
_plwf = new PAndLWindowFactory(this, bus, settings_hub, pandl_model);
|
||||
}
|
||||
|
||||
public ImageSource icon => Icons.Trades;
|
||||
|
||||
@@ -53,18 +53,22 @@ namespace mbts.ui {
|
||||
ToastNotificationController _toast_controller;
|
||||
SoundNotificationPlayer _sound_player;
|
||||
Dispatcher _dispatcher;
|
||||
bool _toast_notifications_enabled, _sound_notifications_enabled = NotificationSettings.Default.sound_notification.enabled;
|
||||
bool
|
||||
_sound_notifications_enabled = Configuration.get<bts.config.toggles.SoundNotifications>().enabled,
|
||||
_toast_notifications_enabled = Configuration.get<bts.config.toggles.ToastNotifications>().enabled;
|
||||
|
||||
[ImportingConstructor]
|
||||
public Notifier(IBus bus, UserSettingsHub settings_hub) {
|
||||
_disposer = new ThreadSafeDisposable(dispose_core);
|
||||
Thread thread = new Thread(start) {
|
||||
IsBackground = true,
|
||||
Priority = ThreadPriority.BelowNormal,
|
||||
Name = "BTSNotifications"
|
||||
};
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
if (_sound_notifications_enabled || _toast_notifications_enabled) {
|
||||
Thread thread = new Thread(start) {
|
||||
IsBackground = true,
|
||||
Priority = ThreadPriority.BelowNormal,
|
||||
Name = "BTSNotifications"
|
||||
};
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
void start() {
|
||||
try {
|
||||
@@ -102,7 +106,9 @@ namespace mbts.ui {
|
||||
(_dispatcher = Dispatcher.CurrentDispatcher).UnhandledException += _on_dispatcher_UnhandledException;
|
||||
Dispatcher.Run();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (NAudio.MmException) { }
|
||||
catch (Exception ex) {
|
||||
Log.assert(false, $"Exception in {Thread.CurrentThread.Name} thread: {ex}");
|
||||
_disposer.Dispose();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
@@ -11,6 +12,60 @@ using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
struct OrderTemplateKey : IEquatable<OrderTemplateKey> {
|
||||
public readonly CustomOrderDefinition.CustomOneofCase type;
|
||||
public readonly string name;
|
||||
|
||||
public OrderTemplateKey(CustomOrderDefinition.CustomOneofCase type)
|
||||
: this(type, OrderUtils.order_type_display_name(type)) { }
|
||||
|
||||
public OrderTemplateKey(CustomOrderDefinition.CustomOneofCase type, string name) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public bool Equals(OrderTemplateKey other) =>
|
||||
type == other.type &&
|
||||
name == other.name;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is OrderTemplateKey other && Equals(other);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
type.GetHashCode(),
|
||||
name == null ? 0 : name.GetHashCode());
|
||||
|
||||
public override string ToString() => $"{name}:{type}";
|
||||
|
||||
public static bool operator ==(OrderTemplateKey a, OrderTemplateKey b) => a.Equals(b);
|
||||
public static bool operator !=(OrderTemplateKey a, OrderTemplateKey b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
struct OrderTemplateHeader : IEquatable<OrderTemplateHeader> {
|
||||
public readonly OrderTemplateKey key;
|
||||
// Note: display_name is NOT part of EQUALITY
|
||||
public readonly string display_name;
|
||||
|
||||
public OrderTemplateHeader(CustomOrderDefinition.CustomOneofCase type)
|
||||
: this(new OrderTemplateKey(type), OrderUtils.order_type_display_name(type)) { }
|
||||
|
||||
public OrderTemplateHeader(OrderTemplateKey key, string display_name) {
|
||||
this.key = key;
|
||||
this.display_name = display_name;
|
||||
}
|
||||
|
||||
public bool Equals(OrderTemplateHeader other) =>
|
||||
key.Equals(other.key);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is OrderTemplateHeader other && Equals(other);
|
||||
|
||||
public override int GetHashCode() => key.GetHashCode();
|
||||
|
||||
public override string ToString() => $"{display_name} ({key})";
|
||||
}
|
||||
|
||||
class InlineOrderProperties {
|
||||
public readonly InlineUserProperty<AccountTuple> account;
|
||||
public readonly InlineUserProperty<TimeInForce> time_in_force;
|
||||
@@ -39,7 +94,7 @@ namespace mbts.spark {
|
||||
class OrderPropertiesEditorVM : DisposableViewModelBase {
|
||||
readonly OrdersModel _model;
|
||||
readonly Property<SecurityImmutable> _security;
|
||||
readonly SelectorProperty<AccountTuple> _accounts;
|
||||
readonly SelectorProperty<AccountTuple, AccountTuple?> _accounts;
|
||||
readonly SelectorProperty<TimeInForce> _time_in_force;
|
||||
readonly SelectorProperty<IOrderTypeVM> _order_type;
|
||||
IDisposable _order_type_param_errors_sub;
|
||||
@@ -58,14 +113,21 @@ namespace mbts.spark {
|
||||
nameof(selected_account),
|
||||
model.factory.subscribe_account_tuples(),
|
||||
order_properties.Select(ops => ops.account),
|
||||
() => accounts?.FirstOrDefault() ?? default,
|
||||
val => (!string.IsNullOrWhiteSpace(val.name) && accounts.Contains(val), "Please select an account"));
|
||||
a => new AccountTuple?(a),
|
||||
() => {
|
||||
foreach (var a in accounts) {
|
||||
return a;
|
||||
}
|
||||
return new AccountTuple?();
|
||||
},
|
||||
EqualityComparer<AccountTuple?>.Default,
|
||||
new[] { new PropertyValidation<AccountTuple?>(a => (a.HasValue && !string.IsNullOrWhiteSpace(a.Value.name) && accounts.Contains(a.Value), "Please select an account")) }.ToReadOnlyList());
|
||||
_order_type = backing(
|
||||
nameof(selected_order_type),
|
||||
security_source.Select(s => model.factory.default_order_type_defs(s).Select(create_order_type_vm)),
|
||||
security_source.Select(s => model.factory.default_order_type_templates(s).Select(kv => create_order_type_vm(kv.Value, kv.Key.key.name, kv.Key.display_name))),
|
||||
order_properties.Select(get_order_type_vm_prop),
|
||||
get_default_order_type_vm,
|
||||
(a, b) => a.order_type == b.order_type);
|
||||
EqualityComparer.null_check_wrapper(OrderTypeVMExtensions.key_comparer));
|
||||
_order_type.Changed += on_selected_order_type_changed;
|
||||
_time_in_force = backing(
|
||||
nameof(selected_time_in_force),
|
||||
@@ -77,19 +139,22 @@ 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) {
|
||||
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, model, security_md_source, cd.Dynamic.DynamicOrderType);
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source, cd.Dynamic.DynamicOrderType);
|
||||
else
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source);
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source);
|
||||
vm.load_from(cd);
|
||||
return vm;
|
||||
}
|
||||
InlineUserProperty<IOrderTypeVM> get_order_type_vm_prop(InlineOrderProperties p) {
|
||||
if (!p.order_type_def.has_value)
|
||||
return new InlineUserProperty<IOrderTypeVM>();
|
||||
return new InlineUserProperty<IOrderTypeVM>(create_order_type_vm(p.order_type_def.value), p.order_type_def.force_override);
|
||||
return new InlineUserProperty<IOrderTypeVM>(
|
||||
create_order_type_vm(p.order_type_def.value, p.order_type_def.value.CustomCase.ToString(),
|
||||
OrderUtils.order_type_display_name(p.order_type_def.value.CustomCase)),
|
||||
p.order_type_def.force_override);
|
||||
}
|
||||
IOrderTypeVM get_default_order_type_vm() {
|
||||
var ot = model.factory.default_order_type(security);
|
||||
@@ -114,13 +179,13 @@ namespace mbts.spark {
|
||||
|
||||
public SecurityImmutable security => _security.Value;
|
||||
public ReadOnlyObservableCollection<AccountTuple> accounts => _accounts.items;
|
||||
public AccountTuple selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; }
|
||||
public AccountTuple? selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; }
|
||||
public ReadOnlyObservableCollection<TimeInForce> time_in_forces => _time_in_force.items;
|
||||
public TimeInForce selected_time_in_force { get => _time_in_force.selected_item; set => _time_in_force.selected_item = value; }
|
||||
public ReadOnlyObservableCollection<IOrderTypeVM> order_types => _order_type.items;
|
||||
public IOrderTypeVM selected_order_type { get => _order_type.selected_item; set => _order_type.selected_item = value; }
|
||||
|
||||
public IObservable<AccountTuple> observe_account(bool start_immediately = false) =>
|
||||
public IObservable<AccountTuple?> observe_account(bool start_immediately = false) =>
|
||||
_accounts.observe_current(start_immediately);
|
||||
|
||||
public IObservable<TimeInForce> observe_time_in_force(bool start_immediately = false) =>
|
||||
|
||||
@@ -17,22 +17,22 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineCoverOrderProperties
|
||||
{
|
||||
public InlineCoverOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
public InlineCoverOrderProperties(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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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 hedgetype = CustomOneofCase.Limit;
|
||||
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
|
||||
hedge_ticks = new InlineUserProperty<int>(1, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_hedge_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> hedge_order_types;
|
||||
|
||||
public readonly InlineUserProperty<int> hedge_ticks;
|
||||
|
||||
@@ -42,25 +42,25 @@ namespace mbts.spark
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineCoverOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
|
||||
IOrderTypeVM _hedge_order;
|
||||
IDisposable _hedge_order_errors_sub;
|
||||
|
||||
public int hedge_ticks { get; set; } = 1;
|
||||
|
||||
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineCoverOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
|
||||
new InlineCoverOrderProperties(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 CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineCoverOrderProperties> inline_properties) : base(model)
|
||||
IObservable<InlineCoverOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -75,11 +75,11 @@ namespace mbts.spark
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
|
||||
{
|
||||
_select_hedge_order(p.selected_hedge_order_type.value);
|
||||
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
|
||||
}
|
||||
if (p.hedge_ticks.force_override)
|
||||
{
|
||||
@@ -116,7 +116,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -200,7 +200,7 @@ namespace mbts.spark
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
@@ -216,7 +216,7 @@ namespace mbts.spark
|
||||
void _select_hedge_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
|
||||
_inline_props.selected_hedge_order_type.value :
|
||||
_inline_props.selected_hedge_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_hedge_order(ot);
|
||||
}
|
||||
|
||||
@@ -53,14 +53,14 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineDynamicOrderProperties
|
||||
{
|
||||
public InlineDynamicOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
public InlineDynamicOrderProperties(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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
}
|
||||
|
||||
class DynamicOrderVM : OrderTypeVMBase<DynamicOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
@@ -68,8 +68,8 @@ namespace mbts.spark
|
||||
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineDynamicOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
@@ -77,13 +77,13 @@ namespace mbts.spark
|
||||
string _dyn_type;
|
||||
List<NameValueVM> _parameters;
|
||||
|
||||
public DynamicOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string dyn_type)
|
||||
public DynamicOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string dyn_type, string id, string display_name)
|
||||
: this(model, security_md_source, dyn_type, security_md_source.Select(smd =>
|
||||
new InlineDynamicOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
|
||||
new InlineDynamicOrderProperties(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 DynamicOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string dyn_type,
|
||||
IObservable<InlineDynamicOrderProperties> inline_properties) : base(model)
|
||||
IObservable<InlineDynamicOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dyn_type = dyn_type;
|
||||
@@ -100,7 +100,7 @@ namespace mbts.spark
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
@@ -139,7 +139,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
}
|
||||
@@ -185,7 +185,7 @@ namespace mbts.spark
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Input;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
namespace mbts.spark {
|
||||
interface IOrderTypeVM : IPropertyWrapperVM<CustomOrderDefinition, CustomOrderDefinitionImmutable> {
|
||||
string id { get; }
|
||||
CustomOneofCase order_type { get; }
|
||||
string type_name { get; }
|
||||
bool is_bts_type { get; }
|
||||
@@ -21,4 +23,12 @@ namespace mbts.spark {
|
||||
ICollection<IOrderTypeVM> child_order_types { get; }
|
||||
IOrderTypeVM child_order { get; }
|
||||
}
|
||||
|
||||
static class OrderTypeVMExtensions {
|
||||
public static IEqualityComparer<IOrderTypeVM> key_comparer =>
|
||||
EqualityComparer.Create<IOrderTypeVM, OrderTemplateKey>(a => a.key());
|
||||
|
||||
public static OrderTemplateKey key(this IOrderTypeVM vm) =>
|
||||
new OrderTemplateKey(vm.order_type, vm.id);
|
||||
}
|
||||
}
|
||||
@@ -14,34 +14,34 @@ using static bts.api.orderman.CustomOrderDefinition;
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
class InlineIcebergOrderProperties {
|
||||
public InlineIcebergOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
|
||||
public InlineIcebergOrderProperties(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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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);
|
||||
show_quantity = new InlineUserProperty<double>(1, true);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<double> show_quantity;
|
||||
}
|
||||
|
||||
class IcebergOrderVM : OrderTypeVMBase<IcebergOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineIcebergOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineIcebergOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
new InlineIcebergOrderProperties(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 IcebergOrderVM(OrdersModel model,
|
||||
IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineIcebergOrderProperties> inline_properties)
|
||||
: base(model) {
|
||||
IObservable<InlineIcebergOrderProperties> 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()
|
||||
@@ -52,7 +52,7 @@ namespace mbts.spark {
|
||||
_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);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (p.show_quantity.force_override) {
|
||||
show_quantity = p.show_quantity.value;
|
||||
@@ -68,7 +68,7 @@ namespace mbts.spark {
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null) {
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ namespace mbts.spark {
|
||||
|
||||
void _select_child_order() {
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -17,19 +17,19 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineJoinOrderProperties
|
||||
{
|
||||
public InlineJoinOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
public InlineJoinOrderProperties (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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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 hedgetype = CustomOneofCase.Lean;
|
||||
if(!child_order_types.Contains(hedgetype))
|
||||
if(!child_order_types.Any(t => t.key.type == hedgetype))
|
||||
hedgetype = CustomOneofCase.Limit;
|
||||
|
||||
//ExceptionAssert.InvalidOperation.That(child_order_types.Contains(hedgetype), $"Hedge order type {hedgetype} is not in hedge_order_types");
|
||||
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
|
||||
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
|
||||
join_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
@@ -37,10 +37,10 @@ namespace mbts.spark
|
||||
enable_hedge = new InlineUserProperty<bool>(false, false);
|
||||
hedge_ticks = new InlineUserProperty<int>(1, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_hedge_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> hedge_order_types;
|
||||
|
||||
|
||||
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
|
||||
@@ -55,13 +55,13 @@ namespace mbts.spark
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineJoinOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
|
||||
IOrderTypeVM _hedge_order;
|
||||
IDisposable _hedge_order_errors_sub;
|
||||
|
||||
@@ -79,12 +79,12 @@ namespace mbts.spark
|
||||
}
|
||||
public int hedge_ticks { get; set; } = 1;
|
||||
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineJoinOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
new InlineJoinOrderProperties(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 JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineJoinOrderProperties> inline_properties) : base(model)
|
||||
IObservable<InlineJoinOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -98,11 +98,11 @@ namespace mbts.spark
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
|
||||
{
|
||||
_select_hedge_order(p.selected_hedge_order_type.value);
|
||||
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
|
||||
}
|
||||
if (p.tick_offset_on_deploy.force_override)
|
||||
{
|
||||
@@ -157,7 +157,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -241,7 +241,7 @@ namespace mbts.spark
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
@@ -257,7 +257,7 @@ namespace mbts.spark
|
||||
void _select_hedge_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
|
||||
_inline_props.selected_hedge_order_type.value :
|
||||
_inline_props.selected_hedge_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_hedge_order(ot);
|
||||
}
|
||||
|
||||
@@ -17,18 +17,18 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineLeanOrderProperties
|
||||
{
|
||||
public InlineLeanOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
public InlineLeanOrderProperties (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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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);
|
||||
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(2, false);
|
||||
trade_toward_behavior = new InlineUserProperty<LeanOrderDefinition.Types.OnToward>(LeanOrderDefinition.Types.OnToward.Cancel, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
|
||||
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
|
||||
public readonly InlineUserProperty<uint> max_chase_ticks;
|
||||
@@ -39,8 +39,8 @@ namespace mbts.spark
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineLeanOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
@@ -49,12 +49,12 @@ namespace mbts.spark
|
||||
public uint max_chase_ticks { get; set; } = 2;
|
||||
public LeanOrderDefinition.Types.OnToward trade_toward_behavior { get; set; } = LeanOrderDefinition.Types.OnToward.Cancel;
|
||||
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineLeanOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
new InlineLeanOrderProperties(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 LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineLeanOrderProperties> inline_properties) : base(model)
|
||||
IObservable<InlineLeanOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -68,7 +68,7 @@ namespace mbts.spark
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (p.cross_threshold.force_override)
|
||||
{
|
||||
@@ -115,7 +115,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ namespace mbts.spark
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
private class LimitOrderVM : OrderTypeVMBase<LimitOrderDefinition> {
|
||||
internal LimitOrderVM(OrdersModel model) : base(model) { }
|
||||
internal LimitOrderVM(OrdersModel model) : base(model, "Limit", OrderUtils.order_type_display_name(CustomOrderDefinition.CustomOneofCase.Limit)) { }
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value) =>
|
||||
value.Limit = factory.limit_order();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
private class MarketOrderVM : OrderTypeVMBase<MarketOrderDefinition> {
|
||||
internal MarketOrderVM(OrdersModel model) : base(model) { }
|
||||
internal MarketOrderVM(OrdersModel model) : base(model, "Market", OrderUtils.order_type_display_name(CustomOrderDefinition.CustomOneofCase.Market)) { }
|
||||
|
||||
public override void assign_to(CustomOrderDefinition parent) =>
|
||||
parent.Market = factory.market_order();
|
||||
|
||||
@@ -12,36 +12,36 @@ namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
public static IOrderTypeVM market(OrdersModel model) => new MarketOrderVM(model);
|
||||
public static IOrderTypeVM limit(OrdersModel model) => new LimitOrderVM(model);
|
||||
public static IOrderTypeVM stop(OrdersModel model) => new StopOrderVM(model);
|
||||
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source) => new StopLimitOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TimeBoxOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TickOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source) => new LeanOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source) => new JoinOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TopOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM cover(OrdersModel model, IObservable<SecurityMD> security_md_source) => new CoverOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TriggerOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source) => new IcebergOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source) => new SpreaderOrderVM(model, security_source);
|
||||
public static IOrderTypeVM dynamic(OrdersModel model, IObservable<SecurityMD> security_md_source, string type) => new DynamicOrderVM(model, security_md_source, type);
|
||||
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 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);
|
||||
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source, string id, string display_name) => new SpreaderOrderVM(model, security_source, id, display_name);
|
||||
public static IOrderTypeVM dynamic(OrdersModel model, IObservable<SecurityMD> security_md_source, string dynamic_type, string id, string display_name) => new DynamicOrderVM(model, security_md_source, dynamic_type, id, display_name);
|
||||
|
||||
public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model,
|
||||
public static IOrderTypeVM create(CustomOneofCase order_type, string id, string display_name, OrdersModel model,
|
||||
IObservable<SecurityMD> security_md_source, string dynamic_type = null ) {
|
||||
switch (order_type) {
|
||||
case CustomOneofCase.Market: return market(model);
|
||||
case CustomOneofCase.Limit: return limit(model);
|
||||
case CustomOneofCase.Stop: return stop(model);
|
||||
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source);
|
||||
case CustomOneofCase.TimeBox: return time_box(model, security_md_source);
|
||||
case CustomOneofCase.Tick: return tick(model, security_md_source);
|
||||
case CustomOneofCase.Lean: return lean(model, security_md_source);
|
||||
case CustomOneofCase.Join: return join(model, security_md_source);
|
||||
case CustomOneofCase.Top: return top(model, security_md_source);
|
||||
case CustomOneofCase.Cover: return cover(model, security_md_source);
|
||||
case CustomOneofCase.Trigger: return trigger(model, security_md_source);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source);
|
||||
case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security));
|
||||
case CustomOneofCase.Dynamic: return dynamic(model, security_md_source, dynamic_type);
|
||||
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.Trigger: return trigger(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source, id, display_name);
|
||||
case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security), id, display_name);
|
||||
case CustomOneofCase.Dynamic: return dynamic(model, security_md_source, dynamic_type, id, display_name);
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -51,12 +51,17 @@ namespace mbts.spark {
|
||||
protected readonly OrdersModel model;
|
||||
protected OrderFactory factory => model.factory;
|
||||
|
||||
protected OrderTypeVMBase(OrdersModel model) => this.model = model;
|
||||
protected OrderTypeVMBase(OrdersModel model, string id, string display_name) {
|
||||
this.model = model;
|
||||
this.id = id;
|
||||
this.display_name = display_name;
|
||||
}
|
||||
|
||||
public abstract CustomOneofCase order_type { get; }
|
||||
public abstract string type_name { get; }
|
||||
public bool is_bts_type => display_name.StartsWith("BTS");
|
||||
public string display_name => OrderUtils.order_type_display_name(order_type);
|
||||
public string id { get; }
|
||||
public string display_name { get; }
|
||||
public OrderMetadata meta => factory.get_metadata(order_type);
|
||||
public virtual bool has_editable_properties => !meta.NoProps;
|
||||
public virtual bool editable_properties_open { get; set; }
|
||||
@@ -76,7 +81,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
private abstract class OrderTypeVMBase<T> : OrderTypeVMBase where T : Google.Protobuf.IMessage, new() {
|
||||
protected OrderTypeVMBase(OrdersModel model) : base(model) { }
|
||||
protected OrderTypeVMBase(OrdersModel model, string id, string display_name) : base(model, id, display_name) { }
|
||||
|
||||
public override CustomOneofCase order_type =>
|
||||
OrderDataHelper.get_order_type(new T());
|
||||
|
||||
@@ -13,20 +13,20 @@ namespace mbts.spark {
|
||||
readonly StrategyEditorWrapper _strategy_editor_wrapper;
|
||||
IDisposable _dispose;
|
||||
|
||||
public SpreaderOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
public SpreaderOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source, string id, string display_name)
|
||||
: this(model, security_source, security_source.SelectMany(s => model.spreader.security_strategy(s.Id)
|
||||
.Select(strategy => {
|
||||
var so = model.factory.spreader_order();
|
||||
so.Security = s.CloneToMutable();
|
||||
so.ExecutionStrategy = strategy.CloneToMutable();
|
||||
return new InlineUserProperty<SpreaderOrderDefinitionImmutable>(so, true);
|
||||
}))) { }
|
||||
})), id, display_name) { }
|
||||
|
||||
public SpreaderOrderVM(
|
||||
OrdersModel model,
|
||||
IObservable<SecurityImmutable> security_source,
|
||||
IObservable<InlineUserProperty<SpreaderOrderDefinitionImmutable>> order_template_source)
|
||||
: base(model) {
|
||||
IObservable<InlineUserProperty<SpreaderOrderDefinitionImmutable>> order_template_source, string id, string display_name)
|
||||
: base(model, id, display_name) {
|
||||
_template = backing(nameof(template), new SpreaderOrderDefinitionImmutable?());
|
||||
_strategy_editor_wrapper = new StrategyEditorWrapper(this);
|
||||
_dispose = Disposable.Create(
|
||||
|
||||
@@ -29,10 +29,11 @@ namespace mbts.spark {
|
||||
InlineStopLimitOrderProperties _inline_props;
|
||||
IDisposable _dispose;
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source)) { }
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source), id, display_name) { }
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties) : base(model) {
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties, string id, string display_name)
|
||||
: base(model, id, display_name) {
|
||||
_stop_price = new UserProperty<double>(backing(nameof(stop_price), double.NaN));
|
||||
_dispose = Disposable.Create(
|
||||
spec_source.OnDispatcher().Subscribe(s => {
|
||||
|
||||
@@ -6,8 +6,7 @@ namespace mbts.spark {
|
||||
private class StopOrderVM : OrderTypeVMBase<StopOrderDefinition> {
|
||||
double _stop_price = 0;
|
||||
|
||||
internal StopOrderVM(OrdersModel model) : base(model) { }
|
||||
|
||||
internal StopOrderVM(OrdersModel model, string id, string display_name) : base(model, id, display_name) { }
|
||||
|
||||
public double stop_price {
|
||||
get => _stop_price;
|
||||
|
||||
@@ -17,11 +17,11 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineTickOrderProperties
|
||||
{
|
||||
public InlineTickOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
public InlineTickOrderProperties (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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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);
|
||||
@@ -29,8 +29,8 @@ namespace mbts.spark
|
||||
max_chase_ticks = new InlineUserProperty<uint>(10, false);
|
||||
detach_on_toward = new InlineUserProperty<bool>(true, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
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;
|
||||
@@ -43,8 +43,8 @@ namespace mbts.spark
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineTickOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
@@ -55,12 +55,12 @@ namespace mbts.spark
|
||||
public uint max_chase_ticks { get; set; } = 10;
|
||||
public bool detach_on_toward { get; set; } = true;
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTickOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
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) { }
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTickOrderProperties> inline_properties) : base(model)
|
||||
IObservable<InlineTickOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -74,7 +74,7 @@ namespace mbts.spark
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (p.tick_offset_on_deploy.force_override)
|
||||
{
|
||||
@@ -131,7 +131,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,7 @@ namespace mbts.spark
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@ using static bts.api.orderman.CustomOrderDefinition;
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
class InlineTimeBoxOrderProperties {
|
||||
public InlineTimeBoxOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
|
||||
public InlineTimeBoxOrderProperties(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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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);
|
||||
start = new InlineUserProperty<DateTime>(DateTime.Now, true);
|
||||
stop = new InlineUserProperty<DateTime>(DateTime.Now.AddMinutes(5), true);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<DateTime> start, stop;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ namespace mbts.spark {
|
||||
const double _start_immediately_delay_ms = 500;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineTimeBoxOrderProperties _inline_props;
|
||||
TimeTextTuple _absolute_start = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(5)), _absolute_stop = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(10));
|
||||
TimeSpan? _relative_start = TimeSpan.FromMinutes(5), _relative_stop = TimeSpan.FromMinutes(5);
|
||||
@@ -40,14 +40,14 @@ namespace mbts.spark {
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTimeBoxOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
new InlineTimeBoxOrderProperties(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 TimeBoxOrderVM(OrdersModel model,
|
||||
IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTimeBoxOrderProperties> inline_properties)
|
||||
: base(model) {
|
||||
IObservable<InlineTimeBoxOrderProperties> 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()
|
||||
@@ -58,7 +58,7 @@ namespace mbts.spark {
|
||||
_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);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (p.start.force_override) {
|
||||
absolute_start = TimeTextTuple.FromDateTime(p.start.value);
|
||||
@@ -94,7 +94,7 @@ namespace mbts.spark {
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null) {
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
@@ -173,7 +173,7 @@ namespace mbts.spark {
|
||||
|
||||
void _select_child_order() {
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineTopOrderProperties
|
||||
{
|
||||
public InlineTopOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
public InlineTopOrderProperties(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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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 hedgetype = CustomOneofCase.Lean;
|
||||
if(!child_order_types.Contains(hedgetype))
|
||||
if(!child_order_types.Any(ct => ct.key.type == hedgetype))
|
||||
hedgetype = CustomOneofCase.Limit;
|
||||
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
|
||||
watch_level_tick_offset = new InlineUserProperty<uint>(0, false);
|
||||
top_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
@@ -35,10 +35,10 @@ namespace mbts.spark
|
||||
enable_hedge = new InlineUserProperty<bool>(false, false);
|
||||
hedge_ticks = new InlineUserProperty<int>(1, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_hedge_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> hedge_order_types;
|
||||
|
||||
|
||||
public readonly InlineUserProperty<uint> watch_level_tick_offset;
|
||||
@@ -53,13 +53,13 @@ namespace mbts.spark
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineTopOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
|
||||
IOrderTypeVM _hedge_order;
|
||||
IDisposable _hedge_order_errors_sub;
|
||||
|
||||
@@ -77,13 +77,13 @@ namespace mbts.spark
|
||||
}
|
||||
public int hedge_ticks { get; set; } = 1;
|
||||
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTopOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
|
||||
new InlineTopOrderProperties(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 TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTopOrderProperties> inline_properties) : base(model)
|
||||
IObservable<InlineTopOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -98,11 +98,11 @@ namespace mbts.spark
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
|
||||
{
|
||||
_select_hedge_order(p.selected_hedge_order_type.value);
|
||||
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
|
||||
}
|
||||
if (p.watch_level_tick_offset.force_override)
|
||||
{
|
||||
@@ -157,7 +157,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -241,7 +241,7 @@ namespace mbts.spark
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
@@ -257,7 +257,7 @@ namespace mbts.spark
|
||||
void _select_hedge_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
|
||||
_inline_props.selected_hedge_order_type.value :
|
||||
_inline_props.selected_hedge_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_hedge_order(ot);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ using static bts.api.orderman.CustomOrderDefinition;
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
class InlineTriggerOrderProperties {
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<TriggerPriceType> trigger_price_type;
|
||||
public readonly InlineUserProperty<TriggerPayupPrice.Types.TriggerPayupPriceType> payup_price_type;
|
||||
public readonly InlineUserProperty<double> fixed_price;
|
||||
@@ -23,10 +23,10 @@ namespace mbts.spark {
|
||||
public readonly InlineUserProperty<bool> use_fixed_price, is_priceout_checked, is_timeout_checked;
|
||||
public readonly InlineUserProperty<int> timeout_secs;
|
||||
|
||||
public InlineTriggerOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
|
||||
public InlineTriggerOrderProperties(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<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
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);
|
||||
trigger_price_type = new InlineUserProperty<TriggerPriceType>(TriggerPriceType.Bid, false);
|
||||
payup_price_type = new InlineUserProperty<TriggerPayupPrice.Types.TriggerPayupPriceType>();
|
||||
use_fixed_price = new InlineUserProperty<bool>(false);
|
||||
@@ -47,19 +47,20 @@ namespace mbts.spark {
|
||||
readonly UserProperty<bool> _use_fixed_price, _is_timeout_checked, _is_priceout_checked;
|
||||
SecurityMD? _security_md;
|
||||
SecurityImmutable? _security => _security_md.security();
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
InlineTriggerOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
ulong _bbo_ref = 0, _last_trade_ref = 0;
|
||||
double _best_bid = double.NaN, _best_ask = double.NaN, _last_trade_price = double.NaN;
|
||||
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTriggerOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
new InlineTriggerOrderProperties(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 TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineTriggerOrderProperties> inline_properties) : base(model) {
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineTriggerOrderProperties> inline_properties, string id, string display_name)
|
||||
: base(model, id, display_name) {
|
||||
_trigger_price_type = create_user_prop(nameof(trigger_price_type), TriggerPriceType.Bid);
|
||||
_use_fixed_price = create_user_prop(nameof(use_fixed_price), true);
|
||||
_fixed_price = create_user_prop(nameof(fixed_price), double.NaN);
|
||||
@@ -88,7 +89,7 @@ namespace mbts.spark {
|
||||
_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);
|
||||
_select_child_order(p.selected_child_order_type.value.key.type);
|
||||
}
|
||||
// trigger price
|
||||
_trigger_price_type.on_inline_prop(p.trigger_price_type);
|
||||
@@ -135,7 +136,7 @@ namespace mbts.spark {
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
_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;
|
||||
}
|
||||
}
|
||||
@@ -216,7 +217,7 @@ namespace mbts.spark {
|
||||
|
||||
void _select_child_order() {
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
_inline_props.selected_child_order_type.value.key.type :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -75,6 +75,11 @@
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="view\Controls\DIYGrid\Settings\SimpleGridSettingsDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Target Name="diagnostic" AfterTargets="PreBuildEvent">
|
||||
</Target>
|
||||
<Target Name="copy_defaults" AfterTargets="PostBuildEvent">
|
||||
|
||||
@@ -113,10 +113,10 @@ namespace bts.spark
|
||||
|
||||
// TODO: remove this method, eventually want all windows to use window_settings_command()
|
||||
public static SparkCommand change_securities_command(Func<(PartialCommand<SecurityCriteria>, SecurityCriteria)> get_param) =>
|
||||
SparkCommand.create("Settings", Icons.Admin, SparkTags.ShowAndEditPickMultiple, get_param);
|
||||
SparkCommand.create("Securities", Icons.HandPointingOverDocument, SparkTags.ShowAndEditPickMultiple, get_param);
|
||||
|
||||
public static PartialCommand<SecurityCriteria> change_securities_callback(string window_name) =>
|
||||
SparkCommand.create(window_name, Icons.Admin, new SparkTags.SparkTag<SecurityCriteria>($"Change {window_name} securities callback"));
|
||||
SparkCommand.create(window_name, Icons.HandPointingOverDocument, new SparkTags.SparkTag<SecurityCriteria>($"Change {window_name} securities callback"));
|
||||
|
||||
public static SparkCommand window_settings_command(IDialogCommand launch_settings_editor) =>
|
||||
new SparkCommandWrapper("Settings", Icons.Admin, launch_settings_editor);
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace mbts.utils {
|
||||
public static class Argument {
|
||||
public static ArgumentException must_be(string description, string name) =>
|
||||
new ArgumentException($"Must be {description}.", name);
|
||||
|
||||
public static ArgumentException unexpected_case<T>(T value, string name) =>
|
||||
new ArgumentException($"Unexpected {typeof(T)} '{value}.'", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,6 +608,12 @@ namespace mbts.ui {
|
||||
public static BTSColor positive_diff { get { return Color.FromRgb(0, 90, 50); } }
|
||||
}
|
||||
|
||||
public static class LadderSpecial {
|
||||
public static BTSColor passive_volume_bar { get; } = Colors.SlateBlue;
|
||||
public static BTSColor total_volume_bar { get; } = 0x009B69;
|
||||
public static BTSColor default_column_background { get; } = 0xBABABA;
|
||||
}
|
||||
|
||||
// Style Guide Colors
|
||||
public static Color brand_lightest { get { return UI.brand_lightest; } }
|
||||
public static Brush brand_lightest_brush { get { return new SolidColorBrush(brand_lightest); } }
|
||||
|
||||
@@ -271,9 +271,6 @@ namespace System.Collections.Generic {
|
||||
#endregion
|
||||
|
||||
#region Max
|
||||
public static T Max<TElement, T>(this IEnumerable<TElement> enumerable, Func<TElement, T> select_value) where T : IComparable<T> =>
|
||||
Max(enumerable, select_value, default(T));
|
||||
|
||||
public static T Max<TElement, T>(this IEnumerable<TElement> enumerable, Func<TElement, T> select_value, T return_if_empty) where T : IComparable<T> {
|
||||
var etr = enumerable.GetEnumerator();
|
||||
if (!etr.MoveNext()) return return_if_empty;
|
||||
|
||||
@@ -153,40 +153,6 @@ namespace mbts
|
||||
return rv.ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||
}
|
||||
|
||||
//this overloaded declaration, (as opposed to one function with comp = null) allows observable_collection.sync_with to be passed into functions as a parameterless Action
|
||||
public static void sync_with<T>(this IList<T> dest, IEnumerable<T> src) { dest.sync_with(src, EqualityComparer<T>.Default); }
|
||||
public static void sync_with<T>(this IList<T> dest, IEnumerable<T> src, IEqualityComparer<T> comp) {
|
||||
Log.notnull(dest);
|
||||
if (src == null) {
|
||||
dest.Clear();
|
||||
return;
|
||||
}
|
||||
var srcList = src as IList<T> ?? src.ToList();
|
||||
var changes = bts.utils.Diff.diff(dest, srcList, comp).ToList();
|
||||
// Apply the diff
|
||||
for(int i = 0; i < changes.Count; ++i) {
|
||||
var c = changes[i];
|
||||
// removes
|
||||
for (var x = 0; x < c.deletedLeft; ++x) {
|
||||
dest.RemoveAt(c.StartLeft);
|
||||
}
|
||||
// inserts
|
||||
for (var x = 0; x < c.insertedRight; ++x) {
|
||||
dest.Insert(c.StartLeft + x, srcList[c.StartRight + x]);
|
||||
}
|
||||
|
||||
// Adjust start index of remaining changes
|
||||
for (int ii = i + 1; ii < changes.Count; ++ii) {
|
||||
var cc = changes[ii];
|
||||
var delta = c.insertedRight - c.deletedLeft;
|
||||
if (cc.StartLeft >= c.StartLeft) {
|
||||
cc.StartLeft += delta;
|
||||
changes[ii] = cc;//remember: struct
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool sync_with_setwise<T>(this ObservableCollection<T> dest, IEnumerable<T> src) { return dest.sync_with_setwise(src, EqualityComparer<T>.Default); }
|
||||
public static bool sync_with_setwise<T>(this ObservableCollection<T> dest, IEnumerable<T> src, IEqualityComparer<T> comp) {
|
||||
if (!src.is_distinct(comp)) {
|
||||
|
||||
@@ -103,9 +103,23 @@ namespace mbts {
|
||||
return _round_tick(tuf, val);
|
||||
}
|
||||
|
||||
public static (int n_low, int n_high) tickdiff(this RegionalTickFactors regional_tick, double x, double y) {
|
||||
if (DoubleHelper.IsNaNOrInfinity(x)) throw new ArgumentException("Cannot be NaN or Infinity.", nameof(x));
|
||||
if (DoubleHelper.IsNaNOrInfinity(y)) throw new ArgumentException("Cannot be NaN or Infinity.", nameof(y));
|
||||
public static bool try_get_tickdiff(this RegionalTickFactors regional_tick, double x, double y, out int n_low, out int n_high) {
|
||||
if (DoubleHelper.IsNaNOrInfinity(x) || DoubleHelper.IsNaNOrInfinity(y)) {
|
||||
n_low = default;
|
||||
n_high = default;
|
||||
return false;
|
||||
}
|
||||
(n_low, n_high) = tickdiff_unchecked(regional_tick, x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callers should be certain that <paramref name="x"/> and <paramref name="y"/> are not nan or infinity.
|
||||
/// </summary>
|
||||
/// <param name="regional_tick"></param>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
internal static (int n_low, int n_high) tickdiff_unchecked(this RegionalTickFactors regional_tick, double x, double y) {
|
||||
double ltie = regional_tick.low_tick_inclusive_end;
|
||||
double raw_diff = x - y;
|
||||
if (x < ltie) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using bts.proto;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
|
||||
@@ -12,7 +12,12 @@ namespace mbts.spark {
|
||||
public static UserSettings factory_defaults =>
|
||||
new UserSettings {
|
||||
_ladder = LadderSettings.Default,
|
||||
_notification = NotificationSettings.Default
|
||||
_notification = NotificationSettings.Default,
|
||||
_market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings,
|
||||
_fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings,
|
||||
_time_and_sales_grid = bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultGridSettings,
|
||||
_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings,
|
||||
_order_book_grid = bts.shell.OrderBookWindowFactory.OrderBookStartupData.DefaultGrid
|
||||
};
|
||||
|
||||
[ProtoMember(1)]
|
||||
@@ -21,19 +26,53 @@ namespace mbts.spark {
|
||||
// This way, when they are enabled, there won't be any backwards compatability issues to worry about, as no clients will have persisted them yet.
|
||||
NotificationSettings _notification;
|
||||
|
||||
[ProtoMember(5)]
|
||||
GridSettings _market_view_grid;
|
||||
[ProtoMember(6)]
|
||||
GridSettings _time_and_sales_grid;
|
||||
[ProtoMember(7)]
|
||||
GridSettings _fills_grid;
|
||||
[ProtoMember(8)]
|
||||
GridSettings _synthetic_management_grid;
|
||||
[ProtoMember(9)]
|
||||
GridSettings _pnl_grid;
|
||||
[ProtoMember(10)]
|
||||
bts.shell.OrderBookWindowFactory.OrderBookGridSettings _order_book_grid;
|
||||
|
||||
public UserSettings() { }
|
||||
|
||||
public LadderSettings ladder => _ladder ?? (_ladder = LadderSettings.Default);
|
||||
public NotificationSettings notification => _notification ?? (_notification = NotificationSettings.Default);
|
||||
public GridSettings market_view_grid => _market_view_grid ?? (_market_view_grid = bts.shell.MarketViewWindowFactory.StartupData.DefaultGridSettings);
|
||||
public GridSettings time_and_sales_grid => _time_and_sales_grid ?? (_time_and_sales_grid = GridSettings.Default.with(bts.shell.TimeAndSalesWindowFactory.StartupData.DefaultColumns.all));
|
||||
public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.FillsWindowFactory.StartupData.DefaultGridSettings);
|
||||
public GridSettings synthetic_management_grid => _synthetic_management_grid ?? (_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings);
|
||||
public GridSettings pnl_grid => _pnl_grid ?? (_pnl_grid = bts.shell.PAndLWindowFactory.PAndLStartupData.DefaultGridSettings);
|
||||
public bts.shell.OrderBookWindowFactory.OrderBookGridSettings order_book_grid => _order_book_grid ?? (_order_book_grid = bts.shell.OrderBookWindowFactory.OrderBookStartupData.Default.grid_settings_wrapper());
|
||||
|
||||
public UserSettings with(LadderSettings ladder = null, NotificationSettings notification = null) =>
|
||||
public UserSettings with(LadderSettings ladder = null,
|
||||
NotificationSettings notification = null,
|
||||
GridSettings provided_market_view_gridsettings = null,
|
||||
GridSettings time_and_sales = null,
|
||||
GridSettings provided_fills_gridsettings = null,
|
||||
GridSettings provided_synthetic_management_gridsettings = null,
|
||||
GridSettings provided_pnl_gridsettings = null,
|
||||
bts.shell.OrderBookWindowFactory.OrderBookGridSettings order_book_grid = null) =>
|
||||
new UserSettings {
|
||||
_ladder = ladder ?? _ladder,
|
||||
_notification = notification ?? _notification
|
||||
_notification = notification ?? _notification,
|
||||
_market_view_grid = provided_market_view_gridsettings ?? _market_view_grid,
|
||||
_time_and_sales_grid = time_and_sales ?? _time_and_sales_grid,
|
||||
_fills_grid = provided_fills_gridsettings ?? _fills_grid,
|
||||
_synthetic_management_grid = provided_synthetic_management_gridsettings ?? _synthetic_management_grid,
|
||||
_pnl_grid = provided_pnl_gridsettings ?? _pnl_grid,
|
||||
_order_book_grid = order_book_grid ?? _order_book_grid
|
||||
};
|
||||
|
||||
public static class FileExtensions {
|
||||
public const string LADDER = "ladder";
|
||||
public const string
|
||||
LADDER = "ladder",
|
||||
GRID = "grid";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace mbts.persist {
|
||||
const string NAME_SPACE = "settings", DEFAULT_KEY = "default";
|
||||
IUserPersist _persist;
|
||||
UserSettings _default_settings;
|
||||
readonly Subject<UserSettings> _default_settings_subject = new Subject<UserSettings>();
|
||||
readonly BehaviorSubject<UserSettings> _default_settings_subject = new BehaviorSubject<UserSettings>(UserSettings.factory_defaults);
|
||||
readonly object _default_settings_lock = new object();
|
||||
|
||||
[ImportingConstructor]
|
||||
public UserSettingsHub(IUserPersist persist) {
|
||||
@@ -33,7 +34,12 @@ namespace mbts.persist {
|
||||
/// Assuming there aren't any rogue agents saving UserSettings by themselves, there should never be any discrepancies.
|
||||
public IObservable<UserSettings> subscribe_default_settings() => _default_settings_subject.AsObservable();
|
||||
|
||||
internal UserSettings current => _default_settings ?? UserSettings.factory_defaults;
|
||||
internal UserSettings current {
|
||||
get {
|
||||
lock (_default_settings_lock)
|
||||
return _default_settings ?? UserSettings.factory_defaults;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task save_default(UserSettings value) {
|
||||
_set_and_notify(value);
|
||||
@@ -41,9 +47,11 @@ namespace mbts.persist {
|
||||
}
|
||||
|
||||
void _set_and_notify(UserSettings value) {
|
||||
_default_settings = value;
|
||||
// notify observers before persisting so the changes are reflected immediately in the ui
|
||||
_default_settings_subject.OnNext(value);
|
||||
lock (_default_settings_lock) {
|
||||
_default_settings = value;
|
||||
// notify observers before persisting so the changes are reflected immediately in the ui
|
||||
_default_settings_subject.OnNext(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
-2
@@ -1,7 +1,9 @@
|
||||
using mbts.spark;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.persist {
|
||||
@@ -11,8 +13,15 @@ namespace mbts.persist {
|
||||
event Action<T> instance_settings_changed;
|
||||
IObservable<T> subscribe_default();
|
||||
Task save_default_settings(T value);
|
||||
IWindowSettingsManagerNonShared<T> as_non_shared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An implementation of IWindowSettingsManager that uses a clone of the actual default settings;
|
||||
/// provides a convenient way to avoid problems that arise from sharing a single default settings object among multiple window instances.
|
||||
/// </summary>
|
||||
interface IWindowSettingsManagerNonShared<T> : IWindowSettingsManager<T>, IDisposable where T : class { }
|
||||
|
||||
class WindowSettingsManager<T> : IWindowSettingsManager<T> where T : class {
|
||||
readonly UserSettingsHub _hub;
|
||||
readonly Func<UserSettings, T> _get;
|
||||
@@ -43,6 +52,42 @@ namespace mbts.persist {
|
||||
|
||||
public IObservable<T> subscribe_default() =>
|
||||
_hub.subscribe_default_settings().Select(_get).DistinctUntilChanged();
|
||||
|
||||
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
|
||||
new NonSharedWrapper(this);
|
||||
|
||||
class NonSharedWrapper : IWindowSettingsManagerNonShared<T> {
|
||||
readonly WindowSettingsManager<T> _wsm;
|
||||
readonly BehaviorSubject<T> _default_subject;
|
||||
IDisposable _dispose;
|
||||
|
||||
public NonSharedWrapper(WindowSettingsManager<T> wsm) {
|
||||
_wsm = wsm;
|
||||
_default_subject = new BehaviorSubject<T>(_wsm.default_settings ?? wsm._get(UserSettings.factory_defaults));
|
||||
_dispose = wsm.subscribe_default().Subscribe(s =>
|
||||
_default_subject.OnNext(default_settings = MiscUtils.clone_proto(s)));
|
||||
}
|
||||
|
||||
public T instance_settings {
|
||||
get => _wsm.instance_settings;
|
||||
set => _wsm.instance_settings = value;
|
||||
}
|
||||
public T default_settings { get; private set; }
|
||||
public event Action<T> instance_settings_changed {
|
||||
add => _wsm.instance_settings_changed += value;
|
||||
remove => _wsm.instance_settings_changed -= value;
|
||||
}
|
||||
public async Task save_default_settings(T value) =>
|
||||
await _wsm.save_default_settings(value);
|
||||
|
||||
public IObservable<T> subscribe_default() =>
|
||||
_default_subject.AsObservable();
|
||||
|
||||
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
|
||||
new NonSharedWrapper(_wsm);
|
||||
|
||||
public void Dispose() => Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
|
||||
static class WindowSettingsManagerExtensions {
|
||||
@@ -54,8 +99,7 @@ namespace mbts.persist {
|
||||
Observable.CombineLatest(
|
||||
Observable.FromEvent<Action<T>, T>(cb => wsm.instance_settings_changed += cb, cb => wsm.instance_settings_changed -= cb)
|
||||
.StartWith(wsm.instance_settings),
|
||||
wsm.subscribe_default()
|
||||
.StartWith(wsm.default_settings),
|
||||
wsm.subscribe_default(),
|
||||
(i, d) => i ?? d
|
||||
).Where(v => v != null)
|
||||
.DistinctUntilChanged();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
@@ -14,16 +13,13 @@ namespace mbts.utils {
|
||||
public static void sync_with_never_duplicate<T>(ObservableCollection<T> dest, IEnumerable<T> src, IEqualityComparer<T> comp) {
|
||||
int i = 0;
|
||||
foreach (var val in src.empty_if_null()) {
|
||||
int curr_index = dest.TakeWhile(dval => !comp.Equals(dval, val)).Count();
|
||||
if (curr_index == dest.Count) {
|
||||
dest.Add(val);
|
||||
} else if (curr_index == i) {
|
||||
int curr_index = dest.IndexOf(val);
|
||||
if (curr_index == i) {
|
||||
continue;
|
||||
} else if (curr_index < dest.Count) {
|
||||
} else if (curr_index > 0 && curr_index < dest.Count) {
|
||||
dest.RemoveAt(curr_index);
|
||||
dest.Insert(i, val);
|
||||
}
|
||||
++i;
|
||||
dest.Insert(i++, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,5 +45,12 @@ namespace mbts.utils {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
public static TResult[] convert_array<T, TResult>(T[] source, Func<T, TResult> selector) {
|
||||
TResult[] rv = new TResult[source.Length];
|
||||
for (int i = 0; i < rv.Length; i++)
|
||||
rv[i] = selector(source[i]);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using bts;
|
||||
using bts.api;
|
||||
|
||||
namespace mbts.ui.utility
|
||||
{
|
||||
class ExpirationFormatter
|
||||
{
|
||||
private static class DateFormatter
|
||||
{
|
||||
private static string[] month_codes = new string[] { "F", "G", "H", "J", "K", "M", "N", "Q", "U", "V", "X", "Z" };
|
||||
private static string[] month_three_letters = new string[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
|
||||
public static string format_condensed(DateTime date, bool use_month_code, bool show_year)
|
||||
{
|
||||
return String.Format("{0}{1}{2}", date.Day, month_codes[date.Month - 1], show_year ? (date.Year - 2000).ToString() : String.Empty);
|
||||
}
|
||||
|
||||
public static string format(DateTime date, bool use_month_code, bool show_day) {
|
||||
string month_text = use_month_code ? "{0}{1:00}".fmt(month_codes[date.Month - 1], date.Year - 2000) : "{0} {1:00}".fmt(month_three_letters[date.Month - 1], date.Year - 2000);
|
||||
return show_day ? "{0} {1}".fmt(date.Day, month_text) : month_text;
|
||||
}
|
||||
|
||||
public static string format_standard(DateTime date) { return "{0} {1} {2:00}".fmt(date.Day, month_three_letters[date.Month - 1], date.Year - 2000); }
|
||||
public static string format_coded(DateTime date) { return "{0} {1}{2:00}".fmt(date.Day, month_codes[date.Month - 1], date.Year - 2000); }
|
||||
public static string format_coded_hide_day(DateTime date) { return "{0}{1}".fmt(month_codes[date.Month - 1], date.Year - 2000); }
|
||||
|
||||
public static DateTime parse_standard(string date_text) {
|
||||
string[] split = date_text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
int year = 0;
|
||||
int month = Array.IndexOf(month_three_letters, split[1]) + 1;
|
||||
int day = 0;
|
||||
int.TryParse(split[2], out year);
|
||||
int.TryParse(split[0], out day);
|
||||
return new DateTime(year + 2000, month, day);
|
||||
}
|
||||
|
||||
public static DateTime parse_coded(string date_text) {
|
||||
string[] split = date_text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string year_text = split[1].Substring(1);
|
||||
string month_text = split[1].Substring(0, 1);
|
||||
string day_text = split[0];
|
||||
int month = Array.IndexOf(month_codes, month_text) + 1;
|
||||
int day, year;
|
||||
int.TryParse(year_text, out year);
|
||||
int.TryParse(day_text, out day);
|
||||
return new DateTime(year + 2000, month, day);
|
||||
}
|
||||
|
||||
public static DateTime parse_coded_hide_day(string date_text) {
|
||||
string year_text = date_text.Substring(1);
|
||||
string month_text = date_text.Substring(0, 1);
|
||||
int month = Array.IndexOf(month_codes, month_text) + 1;
|
||||
int year;
|
||||
int.TryParse(year_text, out year);
|
||||
return new DateTime(year + 2000, month, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static IDictionary<SecurityType, IDictionary<string, bts.config.ExpirationDisplayOffsetEntry>> _offset_map;
|
||||
|
||||
static ExpirationFormatter()
|
||||
{
|
||||
try
|
||||
{
|
||||
_offset_map = new Dictionary<SecurityType, IDictionary<string, bts.config.ExpirationDisplayOffsetEntry>>();
|
||||
_offset_map[SecurityType.Option] = new Dictionary<string, bts.config.ExpirationDisplayOffsetEntry>();
|
||||
_offset_map[SecurityType.Future] = new Dictionary<string, bts.config.ExpirationDisplayOffsetEntry>();
|
||||
|
||||
bts.config.ExpirationDisplayOffset offset_list = mbts.config.Configuration.get<bts.config.ExpirationDisplayOffset>();
|
||||
foreach (bts.config.ExpirationDisplayOffsetEntry e in offset_list.entries)
|
||||
{
|
||||
string security_name = e.security_name;
|
||||
string security_type_text = e.security_type;
|
||||
SecurityType security_type = SecurityType.Unset;
|
||||
Enum.TryParse<SecurityType>(security_type_text, true, out security_type);
|
||||
if (security_type == SecurityType.Option || security_type == SecurityType.Future)
|
||||
_offset_map[security_type][security_name] = e;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Ui.error(String.Format("Exception message = {0}", ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
private static bts.config.ExpirationDisplayOffsetEntry _get_offset_entry(SecurityType security_type, string security_name)
|
||||
{
|
||||
bts.config.ExpirationDisplayOffsetEntry rv = null;
|
||||
IDictionary<string, bts.config.ExpirationDisplayOffsetEntry> this_security_type_map = null;
|
||||
if (_offset_map.TryGetValue(security_type, out this_security_type_map))
|
||||
if (this_security_type_map.TryGetValue(security_name, out rv))
|
||||
return rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static DateTime convert_real_to_mapped(SecurityType security_type, string security_name, DateTime security_expiration)
|
||||
{
|
||||
bts.config.ExpirationDisplayOffsetEntry offset_entry = _get_offset_entry(security_type, security_name);
|
||||
DateTime rv = security_expiration;
|
||||
if (offset_entry != null)
|
||||
{
|
||||
if (offset_entry.addition_type == bts.config.ExpirationOffsetType.type.Month)
|
||||
rv = security_expiration.AddMonths(offset_entry.addition_count);
|
||||
else if (offset_entry.addition_type == bts.config.ExpirationOffsetType.type.MonthMoveToStart)
|
||||
{
|
||||
rv = security_expiration.AddMonths(offset_entry.addition_count);
|
||||
rv = new DateTime(rv.Year, rv.Month, 1);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration) {
|
||||
return format_expiration_standard(security_type, security_name, security_expiration, map_expiration, false);
|
||||
}
|
||||
|
||||
public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool use_month_code) {
|
||||
return format_expiration_standard(security_type, security_name, security_expiration, map_expiration, use_month_code, true);
|
||||
}
|
||||
|
||||
public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool use_month_code, bool show_day) {
|
||||
DateTime mapped_expiration = map_expiration ? convert_real_to_mapped(security_type, security_name, security_expiration) : security_expiration;
|
||||
return DateFormatter.format(mapped_expiration, use_month_code, show_day);
|
||||
}
|
||||
|
||||
public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration) {
|
||||
return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, false, false, true);
|
||||
}
|
||||
|
||||
public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type) {
|
||||
return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, show_security_type, false, true);
|
||||
}
|
||||
|
||||
public static string format_symbol_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type, bool use_month_code) {
|
||||
return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, show_security_type, use_month_code, true);
|
||||
}
|
||||
|
||||
public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type, bool use_month_code, bool show_day) {
|
||||
string exp_text = format_expiration_standard(security_type, security_name, security_expiration, map_expiration, use_month_code, show_day);
|
||||
return !show_security_type ? String.Format("{0} {1}", security_name, exp_text) : String.Format("{0} {1} {2}", security_type.ToString().ToUpper(), security_name, exp_text);
|
||||
}
|
||||
|
||||
public static string format_option_expiration_condensed(string option_name, DateTime option_expiration, bool map_expiration, bool show_symbol_name, bool show_year)
|
||||
{
|
||||
DateTime mapped_expiration = map_expiration ? convert_real_to_mapped(SecurityType.Option, option_name, option_expiration) : option_expiration;
|
||||
return String.Format("{0}{1}", show_symbol_name ? option_name : String.Empty, DateFormatter.format_condensed(mapped_expiration, true, show_year));
|
||||
}
|
||||
|
||||
public static DateTime parse_standard_formatted_expiration(SecurityType security_type, string security_name, string security_expiration) { return DateFormatter.parse_standard(security_expiration); }
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,8 @@ namespace mbts.utils {
|
||||
/// <seealso cref="Observable.SelectMany"/>
|
||||
class KeyDrivenObservableSeries<TKey, TValue> : ISubject<TKey, TValue>, IDisposable {
|
||||
readonly Func<TKey, IObservable<TValue>> _selector;
|
||||
object _lock = new object();
|
||||
readonly object _lock = new object();
|
||||
bool _is_disposed;
|
||||
Subject<TValue> _value_subject = new Subject<TValue>();
|
||||
IDisposable _current_key_sub;
|
||||
|
||||
@@ -31,15 +32,19 @@ namespace mbts.utils {
|
||||
// need to lock bc simultaneous callbacks on diff threads could generate 2 subscriptions at once,
|
||||
// only 1 of which would get disposed in the next callback.
|
||||
Disposable.dispose(ref _current_key_sub);
|
||||
_current_key_sub = _selector(key)?.Subscribe(_value_subject);
|
||||
if (!_is_disposed)
|
||||
_current_key_sub = _selector(key)?.Subscribe(_value_subject);
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<TValue> observer) => _value_subject.Subscribe(observer);
|
||||
|
||||
public void Dispose() {
|
||||
Disposable.dispose(ref _current_key_sub);
|
||||
Disposable.dispose(ref _value_subject);
|
||||
lock (_lock) {
|
||||
_is_disposed = true;
|
||||
Disposable.dispose(ref _current_key_sub);
|
||||
Disposable.dispose(ref _value_subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace mbts.utils {
|
||||
/// <summary>
|
||||
/// Facilities multi-threaded read/write operations to a particular <typeparamref name="T"/> reference.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The reference type to be read/written.</typeparam>
|
||||
class RefValueContainer<T> where T : class {
|
||||
readonly object _lock = new object();
|
||||
T _value;
|
||||
bool _is_dirty;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current value if it has not been read already, otherwise returns null.
|
||||
/// </summary>
|
||||
public T read_new_value() {
|
||||
lock (_lock) {
|
||||
if (_is_dirty) {
|
||||
_is_dirty = false;
|
||||
return _value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void write_value(T value) {
|
||||
lock (_lock) {
|
||||
_is_dirty = true;
|
||||
_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void mark_dirty() {
|
||||
lock (_lock)
|
||||
_is_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ namespace mbts.ui.shell
|
||||
#endregion
|
||||
|
||||
#region automatic property
|
||||
protected delegate void PropertyChange<T>(T oldval, T newval);
|
||||
protected internal delegate void PropertyChange<T>(T oldval, T newval);
|
||||
|
||||
// use nameof() to add compile time checks
|
||||
protected Property<T> backing<T>(string name, T default_value = default(T)) =>
|
||||
@@ -526,7 +526,7 @@ namespace mbts.ui.shell
|
||||
/// <para/>If the user has not selected an item, but the current items do not contain the desired item, it will re-evaluate whether or not to select the item when new items arrive.
|
||||
/// </summary>
|
||||
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, IReadOnlyList<PropertyValidation<T>> validation_steps) =>
|
||||
backing(name, items, desired_item, select_default, null, validation_steps);
|
||||
backing(name, items, desired_item, select_default, EqualityComparer<T>.Default, validation_steps);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a SelectorProperty<T> that observes an items source, and a desired item. When either observable ticks, it determines whether or not to assign the selected_item property.
|
||||
@@ -538,8 +538,8 @@ namespace mbts.ui.shell
|
||||
/// <param name="desired_item">The observable sequence of a desired item.</param>
|
||||
/// <param name="select_default">The fallback function to select an item by default.</param>
|
||||
/// <param name="items_match">The item-matching function used to preserve a selected item when new items arrive.</param>
|
||||
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, Func<T, T, bool> items_match) =>
|
||||
backing(name, items, desired_item, select_default, items_match, new List<PropertyValidation<T>>(0));
|
||||
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, IEqualityComparer<T> comparer) =>
|
||||
backing(name, items, desired_item, select_default, comparer, new List<PropertyValidation<T>>(0));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a SelectorProperty<T> that observes an items source, and a desired item. When either observable ticks, it determines whether or not to assign the selected_item property.
|
||||
@@ -552,14 +552,24 @@ namespace mbts.ui.shell
|
||||
/// <param name="select_default">The fallback function to select an item by default.</param>
|
||||
/// <param name="items_match">The item-matching function used to preserve a selected item when new items arrive.</param>
|
||||
/// <param name="validation_steps">The validation steps to take when selected_item changes.</param>
|
||||
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, Func<T, T, bool> items_match, IReadOnlyList<PropertyValidation<T>> validation_steps) {
|
||||
if (_observable_properties == null) _observable_properties = new List<IDisposable>();
|
||||
var prop = new SelectorProperty<T>(this, name, select_default, items_match, validation_steps);
|
||||
_observable_properties.Add(items.OnDispatcher().Subscribe(prop));
|
||||
_observable_properties.Add(desired_item.OnDispatcher().Subscribe(prop));
|
||||
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, IEqualityComparer<T> comparer, IReadOnlyList<PropertyValidation<T>> validation_steps) {
|
||||
var prop = new SelectorProperty<T>(this, name, select_default, comparer, validation_steps);
|
||||
attach(prop, items, desired_item);
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected SelectorProperty<T, V> backing<T, V>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T, V> item_to_value, Func<V> select_default, IEqualityComparer<V> comparer, IReadOnlyList<PropertyValidation<V>> validation_steps) {
|
||||
var prop = new SelectorProperty<T, V>(this, name, item_to_value, select_default, comparer, validation_steps);
|
||||
attach(prop, items, desired_item);
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected void attach<T, V>(SelectorProperty<T, V> selector_property, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item) {
|
||||
if (_observable_properties == null) _observable_properties = new List<IDisposable>();
|
||||
_observable_properties.Add(items.OnDispatcher().Subscribe(selector_property));
|
||||
_observable_properties.Add(desired_item.OnDispatcher().Subscribe(selector_property));
|
||||
}
|
||||
|
||||
protected void set_dispose(IDisposable disposable) {
|
||||
if (_dispose != null) throw new InvalidOperationException("Cannot set disposable twice");
|
||||
_dispose = disposable;
|
||||
@@ -576,37 +586,44 @@ namespace mbts.ui.shell
|
||||
OnDisposed?.Invoke(true);
|
||||
}
|
||||
|
||||
protected class SelectorProperty<T> : SelectorProperty<T, T> {
|
||||
public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func<T> select_default, IEqualityComparer<T> comparer, IReadOnlyList<PropertyValidation<T>> validation_steps)
|
||||
: base(owner, prop_name, i => i, select_default, comparer, validation_steps) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Designed to observe an items source, and a [settings-driven] desired item. When a new desired item is observed, it will evaluate it's current state and determine if it should be assigned to the selected_item.
|
||||
/// <para/>Notes: If the user has selected an item (ie the selected_item has been assigned through the public setter), the desired item will be ignored unless force_override is true.
|
||||
/// <para/>If the user has not selected an item, but the current items do not contain the desired item, it will re-evaluate whether or not to select the item when new items arrive.
|
||||
/// </summary>
|
||||
protected class SelectorProperty<T> : IObserver<InlineUserProperty<T>>, IObserver<IEnumerable<T>>, IObservable<(T oldval, T newval)> {
|
||||
protected class SelectorProperty<TItem, TValue> : IObserver<InlineUserProperty<TItem>>, IObserver<IEnumerable<TItem>>, IObservable<(TValue oldval, TValue newval)> {
|
||||
readonly DisposableViewModelBase _owner;
|
||||
readonly string _prop_name;
|
||||
readonly IReadOnlyList<PropertyValidation<T>> _validation_steps;
|
||||
readonly ObservableCollection<T> _items = new ObservableCollection<T>();
|
||||
readonly Func<T> _select_default;
|
||||
readonly Func<T, T, bool> _items_match; // used to maintain selected_item through an items-change
|
||||
T _selected_item;
|
||||
InlineUserProperty<T> _desired_item;
|
||||
readonly IReadOnlyList<PropertyValidation<TValue>> _validation_steps;
|
||||
readonly ObservableCollection<TItem> _items = new ObservableCollection<TItem>();
|
||||
readonly Func<TValue> _select_default;
|
||||
readonly IEqualityComparer<TValue> _value_comparer;
|
||||
readonly Func<TItem, TValue> _item_to_value;
|
||||
TValue _selected_item;
|
||||
InlineUserProperty<TItem> _desired_item = new InlineUserProperty<TItem>();
|
||||
bool _is_user_selected_item;
|
||||
|
||||
public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func<T> select_default, Func<T, T, bool> items_match, IReadOnlyList<PropertyValidation<T>> validation_steps) {
|
||||
public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func<TItem, TValue> item_to_value, Func<TValue> select_default, IEqualityComparer<TValue> value_comparer, IReadOnlyList<PropertyValidation<TValue>> validation_steps) {
|
||||
_owner = owner;
|
||||
_prop_name = prop_name;
|
||||
_item_to_value = item_to_value;
|
||||
_select_default = select_default;
|
||||
_items_match = items_match;
|
||||
_value_comparer = value_comparer ?? EqualityComparer<TValue>.Default;
|
||||
_validation_steps = validation_steps;
|
||||
items = new ReadOnlyObservableCollection<T>(_items);
|
||||
items = new ReadOnlyObservableCollection<TItem>(_items);
|
||||
}
|
||||
|
||||
public event PropertyChange<T> Changing;
|
||||
public event PropertyChange<T> Changed;
|
||||
public event PropertyChange<TValue> Changing;
|
||||
public event PropertyChange<TValue> Changed;
|
||||
|
||||
public ReadOnlyObservableCollection<T> items { get; }
|
||||
public ReadOnlyObservableCollection<TItem> items { get; }
|
||||
|
||||
public T selected_item {
|
||||
public TValue selected_item {
|
||||
get => _selected_item;
|
||||
set {
|
||||
_is_user_selected_item = true;
|
||||
@@ -614,31 +631,31 @@ namespace mbts.ui.shell
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<(T oldval, T newval)> observer) {
|
||||
public IDisposable Subscribe(IObserver<(TValue oldval, TValue newval)> observer) {
|
||||
Changed += pch;
|
||||
return Disposable.Create(() => Changed -= pch);
|
||||
void pch(T o, T n) => observer.OnNext((o, n));
|
||||
void pch(TValue o, TValue n) => observer.OnNext((o, n));
|
||||
}
|
||||
|
||||
public IObservable<T> observe_current(bool start_now = false) {
|
||||
public IObservable<TValue> observe_current(bool start_now = false) {
|
||||
var source = this.Select(vals => vals.newval);
|
||||
if (start_now)
|
||||
source = source.StartWith(_selected_item);
|
||||
source = source.StartWith(selected_item);
|
||||
return source;
|
||||
}
|
||||
|
||||
void IObserver<InlineUserProperty<T>>.OnNext(InlineUserProperty<T> iup) {
|
||||
void IObserver<InlineUserProperty<TItem>>.OnNext(InlineUserProperty<TItem> iup) {
|
||||
_desired_item = iup;
|
||||
_try_auto_select();
|
||||
}
|
||||
void IObserver<InlineUserProperty<T>>.OnError(Exception error) { }
|
||||
void IObserver<InlineUserProperty<T>>.OnCompleted() { }
|
||||
void IObserver<InlineUserProperty<TItem>>.OnError(Exception error) { }
|
||||
void IObserver<InlineUserProperty<TItem>>.OnCompleted() { }
|
||||
|
||||
void IObserver<IEnumerable<T>>.OnNext(IEnumerable<T> value) {
|
||||
if (_items_match != null && _is_user_selected_item) {
|
||||
void IObserver<IEnumerable<TItem>>.OnNext(IEnumerable<TItem> value) {
|
||||
if (_is_user_selected_item) {
|
||||
var curr_selected_item = selected_item;
|
||||
_items.sync_with(value);
|
||||
if (_try_match_item(curr_selected_item, out T match))
|
||||
if (_try_match_item(curr_selected_item, out TValue match))
|
||||
selected_item = match;
|
||||
else {
|
||||
_is_user_selected_item = false;
|
||||
@@ -646,18 +663,13 @@ namespace mbts.ui.shell
|
||||
}
|
||||
} else {
|
||||
_items.sync_with(value);
|
||||
if (_is_user_selected_item) {
|
||||
_is_user_selected_item = _items.Contains(_selected_item);
|
||||
}
|
||||
if (!_is_user_selected_item) {
|
||||
_try_auto_select();
|
||||
}
|
||||
_try_auto_select();
|
||||
}
|
||||
}
|
||||
void IObserver<IEnumerable<T>>.OnError(Exception error) { }
|
||||
void IObserver<IEnumerable<T>>.OnCompleted() { }
|
||||
void IObserver<IEnumerable<TItem>>.OnError(Exception error) { }
|
||||
void IObserver<IEnumerable<TItem>>.OnCompleted() { }
|
||||
|
||||
void _run_validation(T val) {
|
||||
void _run_validation(TValue val) {
|
||||
var invalid_steps = _validation_steps.Select(v => v.validate(val)).Where(r => !r.valid);
|
||||
if (invalid_steps.Count() > 0) {
|
||||
var error_msg = string.Join("\n", invalid_steps.Where(r => !string.IsNullOrEmpty(r.msg)).Select(r => r.msg));
|
||||
@@ -667,15 +679,15 @@ namespace mbts.ui.shell
|
||||
|
||||
void _try_auto_select() {
|
||||
if (_is_user_selected_item) {
|
||||
if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out T match)) {
|
||||
if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out TValue match)) {
|
||||
_set_selected_item(match);
|
||||
return;
|
||||
}
|
||||
} else if (desired_item_valid() && _try_match_item(_desired_item.value, out T match)) {
|
||||
} else if (desired_item_valid() && _try_match_item(_desired_item.value, out TValue match)) {
|
||||
_set_selected_item(match);
|
||||
return;
|
||||
}
|
||||
if (_try_match_item(_select_default(), out T default_match))
|
||||
if (_try_match_item(_select_default(), out TValue default_match))
|
||||
_set_selected_item(default_match);
|
||||
else {
|
||||
// the _set_selected_item has never been entered, so validation has never ran, force it here
|
||||
@@ -685,8 +697,8 @@ namespace mbts.ui.shell
|
||||
bool desired_item_valid() => (_desired_item?.has_value).GetValueOrDefault();
|
||||
}
|
||||
|
||||
void _set_selected_item(T value) {
|
||||
if (!Equals(value, _selected_item)) {
|
||||
void _set_selected_item(TValue value) {
|
||||
if (!_value_comparer.Equals(value, _selected_item)) {
|
||||
Changing?.Invoke(_selected_item, value);
|
||||
var old = _selected_item;
|
||||
_selected_item = value;
|
||||
@@ -696,10 +708,20 @@ namespace mbts.ui.shell
|
||||
}
|
||||
}
|
||||
|
||||
bool _try_match_item(T item, out T match) =>
|
||||
_items_match != null ?
|
||||
_items.TryGetFirst(i => _items_match(i, item), out match) :
|
||||
_items.TryGetFirst(i => i.Equals(item), out match);
|
||||
bool _try_match_item(TValue value, out TValue match) {
|
||||
foreach (TItem item in _items) {
|
||||
TValue v = _item_to_value(item);
|
||||
if (_value_comparer.Equals(v, value)) {
|
||||
match = v;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
match = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _try_match_item(TItem item, out TValue match) =>
|
||||
_try_match_item(_item_to_value(item), out match);
|
||||
}
|
||||
|
||||
public event Action<bool> OnDisposed;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using bts.utils;
|
||||
using mbts.ui;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace bts.proto {
|
||||
[ProtoContract]
|
||||
struct ColorShowProto : IEquatable<ColorShowProto> {
|
||||
[ProtoMember(1)]
|
||||
readonly uint _value;
|
||||
[ProtoMember(2)]
|
||||
readonly bool _show;
|
||||
|
||||
public ColorShowProto(Color value, bool show = true)
|
||||
: this(value.rgb_color(), show) { }
|
||||
|
||||
public ColorShowProto(uint value, bool show = true) {
|
||||
_value = value;
|
||||
_show = show;
|
||||
}
|
||||
|
||||
public Color color => _value.rgb_color();
|
||||
public bool show => _show;
|
||||
|
||||
public Color? result {
|
||||
get {
|
||||
if (_show)
|
||||
return color;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(ColorShowProto other) =>
|
||||
_value == other._value &&
|
||||
_show == other._show;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is ColorShowProto ncp && Equals(ncp);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(_value.GetHashCode(), _show.GetHashCode());
|
||||
|
||||
public static bool operator ==(ColorShowProto a, ColorShowProto b) => a.Equals(b);
|
||||
public static bool operator !=(ColorShowProto a, ColorShowProto b) => !a.Equals(b);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace mbts.spark {
|
||||
value = initial_value;
|
||||
}
|
||||
|
||||
event Action<(T oldval, T newval)> _value_set;
|
||||
protected event Action<(T oldval, T newval)> _value_set;
|
||||
|
||||
public T value { get; private set; }
|
||||
public string text => _text ?? (_text = _format(value));
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace mbts.ui.controls {
|
||||
bool _is_mouse_over_text, _can_execute;
|
||||
Pen _underline_pen;
|
||||
|
||||
static HyperlinkCell() =>
|
||||
static HyperlinkCell() {
|
||||
WPFHelper.override_metadata<HorizontalAlignment, HyperlinkCell>(
|
||||
HorizontalAlignmentProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsArrange |
|
||||
@@ -33,6 +33,13 @@ namespace mbts.ui.controls {
|
||||
o._horizontal_alignment = v;
|
||||
o._glyph_run = null;
|
||||
}, HorizontalAlignment.Left);
|
||||
WPFHelper.override_metadata<double, HyperlinkCell>(
|
||||
WidthProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsArrange |
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._glyph_run = null);
|
||||
}
|
||||
|
||||
public HyperlinkCell() {
|
||||
_background = Background;
|
||||
@@ -279,24 +286,50 @@ namespace mbts.ui.controls {
|
||||
drawingContext.DrawGlyphRun(_foreground, _glyph_run);
|
||||
if (_is_mouse_over_text) {
|
||||
// draw underline
|
||||
var pt = new Point(Padding.Left, 0.5 * (ActualHeight + _font_size) + 1);
|
||||
Point pt;
|
||||
switch (_horizontal_alignment) {
|
||||
case HorizontalAlignment.Left:
|
||||
pt = new Point(Padding.Left, 0.5 * (ActualHeight + _font_size) + 1);
|
||||
break;
|
||||
case HorizontalAlignment.Right:
|
||||
pt = new Point(ActualWidth - Padding.Right - _glyph_width, 0.5 * (ActualHeight + _font_size) + 1);
|
||||
break;
|
||||
case HorizontalAlignment.Center:
|
||||
default:
|
||||
pt = new Point(0.5 * (ActualWidth - _glyph_width), 0.5 * (ActualHeight + _font_size) + 1);
|
||||
break;
|
||||
}
|
||||
drawingContext.DrawLine(_underline_pen, pt, new Point(pt.X + _glyph_width, pt.Y));
|
||||
|
||||
}
|
||||
drawingContext.Pop();
|
||||
}
|
||||
var bgo = GetBackgroundOverlay(this);
|
||||
if (bgo != null)
|
||||
drawingContext.DrawRectangle(bgo, null, rect);
|
||||
if (bg != null) {
|
||||
var bgo = GetBackgroundOverlay(this);
|
||||
if (bgo != null)
|
||||
drawingContext.DrawRectangle(bgo, null, rect);
|
||||
}
|
||||
}
|
||||
|
||||
bool _point_horizontally_over_text(double x) {
|
||||
switch (_horizontal_alignment) {
|
||||
case HorizontalAlignment.Left:
|
||||
return x >= _padding.Left && x <= _padding.Left + _glyph_width;
|
||||
case HorizontalAlignment.Right:
|
||||
return x >= ActualWidth - Padding.Right - _glyph_width && x <= ActualWidth - _padding.Right;
|
||||
case HorizontalAlignment.Center:
|
||||
default:
|
||||
return x >= 0.5 * (ActualWidth - _glyph_width) && x <= 0.5 * (ActualWidth - _glyph_width) + _glyph_width;
|
||||
}
|
||||
}
|
||||
|
||||
void _check_mouse_over_text(MouseEventArgs e) {
|
||||
var pt = e.GetPosition(this);
|
||||
bool over_text =
|
||||
_glyph_run != null &&
|
||||
pt.X >= _padding.Left &&
|
||||
pt.X <= _padding.Left + _glyph_width &&
|
||||
pt.Y >= _padding.Top &&
|
||||
pt.Y <= _padding.Top + _font_size;
|
||||
_glyph_run != null &&
|
||||
_point_horizontally_over_text(pt.X) &&
|
||||
pt.Y >= _padding.Top &&
|
||||
pt.Y <= _padding.Top + _font_size;
|
||||
if (_is_mouse_over_text != over_text)
|
||||
_toggle_mouse_over_text();
|
||||
}
|
||||
|
||||
@@ -16,22 +16,40 @@ namespace mbts.ui.controls {
|
||||
partial class DIYGrid {
|
||||
public static class DefaultColumnWidths {
|
||||
public const double
|
||||
Account = 68,
|
||||
Exchange = 74,
|
||||
Account = 80,
|
||||
Exchange = 86,
|
||||
FillKey = 68,
|
||||
OrderKey = 78,
|
||||
OrderType = 88,
|
||||
Price = 56,
|
||||
MViewPrice = 56,
|
||||
Price = 68,
|
||||
AveragePrice = 76,
|
||||
LastPrice = 78,
|
||||
Settlement = 82,
|
||||
Security = 80,
|
||||
SecurityKey = 90,
|
||||
SecurityType = 96,
|
||||
Side = 47,
|
||||
Size = 48,
|
||||
Symbol = 48,
|
||||
Time = 100,
|
||||
ImpliedDirectMergedSize = 68,
|
||||
Filled = 56,
|
||||
Symbol = 78,
|
||||
Time = 112,
|
||||
TimeInForce = 47,
|
||||
TradeKey = 76,
|
||||
User = 53,
|
||||
Aggressor = 80;
|
||||
Aggressor = 80,
|
||||
Volume = 74,
|
||||
FillKind = 70,
|
||||
Description = 300,
|
||||
N_Legs = 68,
|
||||
N_Working_Orders = 123,
|
||||
Start_Net_Pos = 100,
|
||||
Total_Net_Pos = 100,
|
||||
PnL_Generic = 80,
|
||||
Working = 72,
|
||||
LastMessage = 100,
|
||||
ParentKey = 100;
|
||||
}
|
||||
|
||||
public static class DefaultColumnHeaders {
|
||||
@@ -45,14 +63,41 @@ namespace mbts.ui.controls {
|
||||
Price = "Price",
|
||||
Security = "Security",
|
||||
SecurityKey = "Security Key",
|
||||
SecurityType = "Security Type",
|
||||
Side = "Side",
|
||||
Size = "Qty",
|
||||
MergedSize = "Qty (M)",
|
||||
DirectSize = "Qty (D)",
|
||||
ImpliedSize = "Qty (I)",
|
||||
Symbol = "Symbol",
|
||||
TimeInForce = "TIF",
|
||||
TradeKey = "Trade Key",
|
||||
User = "User",
|
||||
Time = "Time",
|
||||
Aggressor = "Aggressor";
|
||||
Aggressor = "Aggressor",
|
||||
Volume = "Volume",
|
||||
Last = "Last",
|
||||
Pos = "Pos",
|
||||
Working = "Working",
|
||||
Bid = "Bid",
|
||||
Ask = "Ask",
|
||||
Settlement = "Settlement",
|
||||
DayChange = "Change",
|
||||
SessionLow = "Low",
|
||||
SessionHigh = "High",
|
||||
FillKind = "Fill Kind",
|
||||
Name = "Name",
|
||||
Description = "Description",
|
||||
N_Legs = "# Legs",
|
||||
N_Working_Orders = "# Working Orders",
|
||||
StartNetPos = "Start Net Pos",
|
||||
StartPnL = "Start PnL",
|
||||
DayLong = "Day Long",
|
||||
DayShort = "Day Short",
|
||||
DayPnL = "Day PnL",
|
||||
TotalNetPos = "Total Net Pos",
|
||||
TotalPnL = "Total PnL",
|
||||
PnLPrice = "PnL Price";
|
||||
}
|
||||
|
||||
public interface IKeyedColumn<TColKey> {
|
||||
@@ -158,7 +203,7 @@ namespace mbts.ui.controls {
|
||||
public virtual void load_from(GridColumnSettings settings) {
|
||||
if (settings == null)
|
||||
return;
|
||||
using (ReflectionHelper.detect_property_changes(this, raise_prop_changed, nameof(is_visible), nameof(is_collapsed), nameof(font_weight_override), nameof(font_weight),
|
||||
using (ReflectionHelper.detect_property_changes(this, _raise_prop_changed, nameof(is_visible), nameof(is_collapsed), nameof(font_weight_override), nameof(font_weight),
|
||||
nameof(font_color_override), nameof(font_color), nameof(font_brush), nameof(font_family), nameof(font_size), nameof(horizontal_alignment), nameof(horizontal_offset))) {
|
||||
//
|
||||
id = settings.id;
|
||||
@@ -176,21 +221,24 @@ namespace mbts.ui.controls {
|
||||
Width = original_width;
|
||||
}
|
||||
//
|
||||
void raise_prop_changed(string prop) =>
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(prop));
|
||||
}
|
||||
|
||||
// 'fallback' font-params (aka 'grid-level' font-params)
|
||||
public void set_fallback_font_params(FontFamily font_family, double font_size, Color font_color, FontWeight font_weight) {
|
||||
_fallback_font_family = font_family;
|
||||
_fallback_font_size = font_size;
|
||||
_fallback_font_color = font_color;
|
||||
_fallback_font_weight = font_weight;
|
||||
using (ReflectionHelper.detect_property_changes(this, _raise_prop_changed, nameof(font_family), nameof(font_size), nameof(font_color), nameof(font_weight))) {
|
||||
_fallback_font_family = font_family;
|
||||
_fallback_font_size = font_size;
|
||||
_fallback_font_color = font_color;
|
||||
_fallback_font_weight = font_weight;
|
||||
}
|
||||
font_params_changed?.Invoke();
|
||||
}
|
||||
|
||||
protected virtual void OnSortDirectionChanged(ListSortDirection? new_value) =>
|
||||
sort_direction_changed?.Invoke(this, new_value);
|
||||
|
||||
void _raise_prop_changed(string prop) =>
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(prop));
|
||||
}
|
||||
|
||||
internal abstract class ColumnBase<TColKey> : ColumnBase, IKeyedColumn<TColKey>, IGridColumn {
|
||||
|
||||
+5
-1
@@ -29,9 +29,13 @@ namespace mbts.ui.controls {
|
||||
background_overlay = selection_fill;
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var hl = new HyperlinkCell { Foreground = font_brush, Background = background, FontSize = font_size, Padding = padding };
|
||||
var hl = new HyperlinkCell { Foreground = font_brush, Background = background, Padding = padding };
|
||||
hl.SetBinding(HyperlinkCell.TextProperty, _binding);
|
||||
hl.SetBinding(HyperlinkCell.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
|
||||
hl.SetBinding(HyperlinkCell.FontSizeProperty, new Binding(nameof(font_size)) { Source = this });
|
||||
hl.SetBinding(HyperlinkCell.HorizontalAlignmentProperty, new Binding(nameof(horizontal_alignment)) { Source = this });
|
||||
hl.SetBinding(HyperlinkCell.CommandProperty, _command_binding);
|
||||
hl.SetBinding(HyperlinkCell.WidthProperty, new Binding(nameof(ActualWidth)) { Source = this });
|
||||
if (background_overlay != null) {
|
||||
hl.SetBinding(BackgroundOverlayProperty, new Binding(nameof(ListBoxItem.IsSelected)) {
|
||||
Converter = new AnonymousValueConverter<bool, Brush>(val => val ? background_overlay : null),
|
||||
|
||||
+3
-1
@@ -22,8 +22,10 @@ namespace mbts.ui.controls {
|
||||
public VerticalAlignment vertical_alignment { get; set; } = VerticalAlignment.Center;
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var v = new TextCell { FontFamily = font_family, FontSize = font_size, Foreground = font_brush, HorizontalAlignment = horizontal_alignment, VerticalAlignment = vertical_alignment };
|
||||
var v = new TextCell { Foreground = font_brush, HorizontalAlignment = horizontal_alignment, VerticalAlignment = vertical_alignment };
|
||||
v.SetBinding(TextCell.TextProperty, _binding);
|
||||
v.SetBinding(TextCell.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
|
||||
v.SetBinding(TextCell.FontSizeProperty, new Binding(nameof(font_size)) { Source = this });
|
||||
try_set_binding(TextCell.BackgroundProperty, background_binding);
|
||||
try_set_binding(TextCell.ForegroundProperty, foreground_binding);
|
||||
try_set_binding(TextCell.FontWeightProperty, font_weight_binding);
|
||||
|
||||
+11
-6
@@ -1,21 +1,26 @@
|
||||
using mbts.spark;
|
||||
using bts.shell;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility.text;
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
partial class DIYGrid {
|
||||
internal class TimeAndSalesColumn : RenderTextColumn<TimeAndSalesVM.Row> {
|
||||
|
||||
readonly Func<TimeAndSalesVM.Row, string> _get_text;
|
||||
internal class TimeAndSalesColumn : RenderTextColumn<TimeAndSalesVM.Row>, IKeyedColumn<TimeAndSalesView.Columns> {
|
||||
readonly Func<TimeAndSalesVM.Row, Brush> _get_brush;
|
||||
|
||||
public TimeAndSalesColumn(Func<TimeAndSalesVM.Row, string> get_text, Func<TimeAndSalesVM.Row, Brush> get_brush, IDataGridColumnFilter filter = null, string filter_template = null, GlyphContext glyph_context = null) : base(get_text, filter, filter_template, glyph_context) {
|
||||
_get_text = get_text;
|
||||
public TimeAndSalesColumn(TimeAndSalesView.Columns key, Func<TimeAndSalesVM.Row, Brush> get_brush, IDataGridColumnFilter filter = null, string filter_template = null)
|
||||
: base(r => r[key], filter, filter_template) {
|
||||
this.key = key;
|
||||
// id is used for settings (another case of ProtoBufs vs Generics - strings seemed like the easiest way to provide an identifier for the common GridColumnSettings proto)
|
||||
id = TimeAndSalesWindowFactory.col_id(key);
|
||||
_get_brush = get_brush;
|
||||
}
|
||||
|
||||
public TimeAndSalesView.Columns key { get; }
|
||||
|
||||
public override void draw(TimeAndSalesVM.Row row, DrawingContext dc, ColumnGeometry position, double row_height) {
|
||||
CellRender.draw_text(dc
|
||||
, glyph_context
|
||||
|
||||
@@ -20,18 +20,20 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
|
||||
public class RenderTextColumn<TRowVM> : RenderColumnBase<TRowVM>, IRenderColumn<TRowVM>, IFilterableColumn {
|
||||
readonly Func<TRowVM, string> _get_text;
|
||||
protected readonly Func<TRowVM, string> _get_text;
|
||||
|
||||
public RenderTextColumn(
|
||||
Func<TRowVM, string> get_text,
|
||||
IDataGridColumnFilter filter = null,
|
||||
string filter_template = null,
|
||||
GlyphContext glyph_context = null)
|
||||
string filter_template = null)
|
||||
: base() {
|
||||
_get_text = get_text;
|
||||
this.filter = filter;
|
||||
this.filter_template = filter_template;
|
||||
this.glyph_context = glyph_context ?? TextUtils.StandardGlyphContext;
|
||||
font_params_changed += refresh_glyph_context;
|
||||
refresh_glyph_context();
|
||||
void refresh_glyph_context() =>
|
||||
glyph_context = GlyphContext.create(font_family, font_weight);
|
||||
}
|
||||
|
||||
public IDataGridColumnFilter filter { get; }
|
||||
@@ -39,7 +41,7 @@ namespace mbts.ui.controls {
|
||||
public Brush background { get; set; }
|
||||
public VerticalAlignment vertical_alignment { get; set; } = VerticalAlignment.Center;
|
||||
public Thickness padding { get; set; } = DefaultCellPadding;
|
||||
public GlyphContext glyph_context { get; set; } = TextUtils.StandardGlyphContext;
|
||||
public GlyphContext glyph_context { get; private set; }
|
||||
|
||||
public override void draw(TRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(
|
||||
|
||||
@@ -335,6 +335,8 @@
|
||||
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" />
|
||||
|
||||
<!-- 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 -->
|
||||
<!-- another bug, but this thing sent me down a rabbit hole. F you TickUpDown -->
|
||||
<bts:TickUpDown Grid.RowSpan="3" Grid.ColumnSpan="2" Width="0" Height="0" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
TickUpCommand="{Binding tick_up}" TickDownCommand="{Binding tick_down}"
|
||||
x:Name="_edit" Visibility="Collapsed">
|
||||
|
||||
+6
-2
@@ -28,7 +28,7 @@ namespace mbts.ui.controls {
|
||||
new AnonymousValueConverter(obj =>
|
||||
obj is SoundNotification.SystemSound s ?
|
||||
EnumHelper.get_attribute<SoundNotification.SystemSound, LicenseAttribute>(s)?.link : null);
|
||||
|
||||
|
||||
public static readonly DependencyProperty BusProperty =
|
||||
WPFHelper.create_dp<IBus, SoundAnimationView>(nameof(Bus));
|
||||
|
||||
@@ -38,7 +38,11 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
|
||||
public static ICommand PreviewSoundCommand =>
|
||||
new DelegateCommand<SoundNotification.SystemSound>(s => NAudioPlayer.create(s).play());
|
||||
new DelegateCommand<SoundNotification.SystemSound>(s => {
|
||||
try {
|
||||
NAudioPlayer.create(s).play();
|
||||
} catch (NAudio.MmException) { }
|
||||
});
|
||||
|
||||
#region SoundTypes
|
||||
public static readonly DependencyProperty SoundTypesProperty =
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace mbts.ui.settings {
|
||||
|
||||
public GridColumnSettings with(
|
||||
string display_name = null, bool? is_visible = null, bool? is_collapsed = null, NullableFontWeightProto? font_weight_override = null, NullableColorProto? font_color_override = null,
|
||||
double? width = _default_width, HorizontalAlignment? horizontal_alignment = _default_horizontal_alignment, double? horizontal_offset = null) =>
|
||||
double? width = null, HorizontalAlignment? horizontal_alignment = null, double? horizontal_offset = null) =>
|
||||
//
|
||||
new GridColumnSettings(_id,
|
||||
display_name ?? this.display_name,
|
||||
|
||||
@@ -4,7 +4,6 @@ using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utilities;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -28,22 +27,15 @@ namespace mbts.spark {
|
||||
ImmutableFontWeight
|
||||
}
|
||||
|
||||
partial class GridSettingsVM<TRowData, TPredicateBuilder> {
|
||||
public interface IColumnVM : IValueFactory<(GridColumnSettings settings, ILookup<TPredicateBuilder, CellAnimation> animations)>, IDisposable {
|
||||
partial class GridSettingsVM<TRowData, TPredicateBuilder, TSettingsData> {
|
||||
public interface IColumnVM : ISimpleColumnVM, IValueFactory<(GridColumnSettings settings, ILookup<TPredicateBuilder, CellAnimation> animations)>, IDisposable {
|
||||
ObservableEditableCollection<PredicateCellAnimationVM> predicate_animations { get; }
|
||||
string id { get; }
|
||||
string display_name { get; set; }
|
||||
double width { get; set; }
|
||||
bool is_visible { get; set; }
|
||||
bool is_font_color_overridden { get; set; }
|
||||
ColorPickerPopupVM font_color_override { get; }
|
||||
bool is_font_weight_overridden { get; set; }
|
||||
FontWeight font_weight_override { get; set; }
|
||||
void apply_flags(GridColumnSettingsFeatureFlags flags);
|
||||
new (GridColumnSettings settings, ILookup<TPredicateBuilder, CellAnimation> animations) create();
|
||||
new void load_from((GridColumnSettings settings, ILookup<TPredicateBuilder, CellAnimation> animations) args);
|
||||
}
|
||||
|
||||
public class ColumnVM : DisposableViewModelBase, IColumnVM {
|
||||
public readonly GridSettingsVM<TRowData, TPredicateBuilder> owner;
|
||||
public readonly GridSettingsVM<TRowData, TPredicateBuilder, TSettingsData> owner;
|
||||
|
||||
readonly Property<string> _id, _display_name;
|
||||
readonly Property<double> _width;
|
||||
@@ -52,7 +44,7 @@ namespace mbts.spark {
|
||||
readonly Property<FontWeight> _font_weight_override;
|
||||
GridColumnSettings _settings;
|
||||
|
||||
public ColumnVM(GridSettingsVM<TRowData, TPredicateBuilder> owner) {
|
||||
public ColumnVM(GridSettingsVM<TRowData, TPredicateBuilder, TSettingsData> owner) {
|
||||
this.owner = owner;
|
||||
_id = backing(nameof(id), string.Empty);
|
||||
_display_name = backing(nameof(display_name), string.Empty);
|
||||
@@ -107,6 +99,7 @@ namespace mbts.spark {
|
||||
_horizontal_alignment.set(_settings.horizontal_alignment);
|
||||
_is_visible.set(_settings.is_visible);
|
||||
_is_font_weight_overridden.set(_settings.font_weight_override.HasValue);
|
||||
_is_font_color_overridden.set(_settings.font_color_override.HasValue);
|
||||
if (is_font_weight_overridden) {
|
||||
_font_weight_override.set(_settings.font_weight_override.Value);
|
||||
}
|
||||
@@ -133,6 +126,12 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public override string ToString() => $"{display_name} ({id})";
|
||||
|
||||
GridColumnSettings IValueFactory<GridColumnSettings>.create() =>
|
||||
create().settings;
|
||||
|
||||
void IValueFactory<GridColumnSettings>.load_from(GridColumnSettings value) =>
|
||||
load_from((value, new Dictionary<TPredicateBuilder, CellAnimation>().ToLookup(kv => kv.Key, kv => kv.Value)));
|
||||
}
|
||||
|
||||
public class PredicateCellAnimationVM : DisposableViewModelBase, IValueFactory<(TPredicateBuilder predicate, IEnumerable<CellAnimation> animations)> {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
@@ -10,7 +12,7 @@ using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui.settings {
|
||||
[ProtoContract]
|
||||
class GridSettings {
|
||||
class GridSettings : IEquatable<GridSettings> {
|
||||
const string _default_font_family = "Segoe UI";
|
||||
const double _default_font_size = 11;
|
||||
const uint _default_font_color = 0;
|
||||
@@ -94,6 +96,7 @@ namespace mbts.ui.settings {
|
||||
public FontWeight font_weight => FontWeight.FromOpenTypeWeight(_font_weight);
|
||||
public ColorOpacityProto selection_highlight => _selection_highlight;
|
||||
public IReadOnlyList<GridColumnSettings> columns => _columns;
|
||||
[Obsolete] // TODO: remove sorting_criterion from GridSettings entirely. This should be part of IShellWindow's StartupData, since it is an instance-specific setting, and prob not part of the shared defaults
|
||||
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criterion;
|
||||
|
||||
public static GridSettings get_default() =>
|
||||
@@ -110,5 +113,32 @@ namespace mbts.ui.settings {
|
||||
sorting_criterion ?? _sorting_criterion);
|
||||
|
||||
public GridSettings clone() => with();
|
||||
|
||||
public bool Equals(GridSettings other) =>
|
||||
other != null &&
|
||||
font_family == other.font_family &&
|
||||
font_size == other.font_size &&
|
||||
font_weight == other.font_weight &&
|
||||
font_color == other.font_color &&
|
||||
selection_highlight == other.selection_highlight &&
|
||||
EqualityComparer.safe_sequence_equals(columns, other.columns) &&
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
EqualityComparer.safe_sequence_equals(sorting_criterion, other.sorting_criterion);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GridSettings);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
font_family.GetHashCode(),
|
||||
font_size.GetHashCode(),
|
||||
font_weight.GetHashCode(),
|
||||
font_color.GetHashCode(),
|
||||
selection_highlight.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(columns),
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
EqualityComparer.safe_aggregate_hash(sorting_criterion));
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.api;
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using mbts.collections;
|
||||
using mbts.spark;
|
||||
@@ -17,10 +18,10 @@ using System.Windows.Media;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
|
||||
namespace mbts.ui {
|
||||
class GridSettingsManager<TRow, TRowData, TPredicateBuilderArg, TPredicateBuilder> :
|
||||
ViewModelBase, IValueFactory<(GridSettings grid, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations_data)>, IDisposable
|
||||
class GridSettingsManager<TRow, TRowData, TPredicateBuilderArg, TPredicateBuilder, TSettingsData> : ViewModelBase, IValueFactory<TSettingsData>, IObserver<TSettingsData>, IDisposable
|
||||
where TRow : IRowVM<TRowData>
|
||||
where TPredicateBuilder : IPredicateBuilder<TRowData, TPredicateBuilderArg>, new() {
|
||||
where TPredicateBuilder : IPredicateBuilder<TRowData, TPredicateBuilderArg>, new()
|
||||
where TSettingsData : class, IGridSettingsWithNotifications<TRowData, TPredicateBuilder> {
|
||||
//
|
||||
public readonly ObservableCollection<string> live_sorting_props;
|
||||
|
||||
@@ -39,12 +40,18 @@ namespace mbts.ui {
|
||||
readonly DelegateProperty<SolidColorBrush> _font_color, _selection_highlight;
|
||||
readonly SetMap<IPredicate<TRowData>, AnimationDefinitionWrapper<TRow>> _animation_defs = new SetMap<IPredicate<TRowData>, AnimationDefinitionWrapper<TRow>>();
|
||||
readonly TPredicateBuilderArg _arg;
|
||||
readonly Func<IPropertyWrapperVM<TPredicateBuilder>> _create_predicate_vm;
|
||||
readonly Func<string, IPropertyWrapperVM<TPredicateBuilder>> _create_column_predicate_vm;
|
||||
readonly Func<GridSettings, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>>, TSettingsData> _create_settings_obj;
|
||||
readonly IReadOnlyDictionary<string, ImmutableColumnSettingStore> _column_flags;
|
||||
IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> _predicate_animations_data;
|
||||
|
||||
public GridSettingsManager(
|
||||
IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel orders_model, ObservableCollection<TRow> rows,
|
||||
ICollectionView rows_view, ObservableCollection<ColumnBase> columns, Func<IPropertyWrapperVM<TPredicateBuilder>> create_predicate_vm,
|
||||
Func<string, IPropertyWrapperVM<TPredicateBuilder>> create_column_predicate_vm, TPredicateBuilderArg arg, IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> column_flags = null) {
|
||||
Func<string, IPropertyWrapperVM<TPredicateBuilder>> create_column_predicate_vm,
|
||||
Func<GridSettings, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>>, TSettingsData> create_settings_obj,
|
||||
TPredicateBuilderArg arg, IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> column_flags = null) {
|
||||
//
|
||||
_bus = bus;
|
||||
_notification_service = notification_service;
|
||||
@@ -52,7 +59,11 @@ namespace mbts.ui {
|
||||
_rows = rows;
|
||||
_rows_view = rows_view;
|
||||
_columns = columns;
|
||||
_create_predicate_vm = create_predicate_vm;
|
||||
_create_column_predicate_vm = create_column_predicate_vm;
|
||||
_create_settings_obj = create_settings_obj;
|
||||
_arg = arg;
|
||||
_column_flags = column_flags.ToDictionary(kv => kv.Key, kv => ImmutableColumnSettingStore.create(kv.Value));
|
||||
_grid_settings = backing(nameof(grid_settings), GridSettings.Default);
|
||||
_font_family = backing(nameof(font_family), () => new FontFamily(grid_settings.font_family), nameof(grid_settings));
|
||||
_font_weight = backing(nameof(font_weight), () => grid_settings.font_weight, nameof(grid_settings));
|
||||
@@ -72,10 +83,10 @@ namespace mbts.ui {
|
||||
// return pa;
|
||||
//}
|
||||
Window create_settings_view() {
|
||||
var vm = new GridSettingsVM<TRowData, TPredicateBuilder>(owner_title, _bus, _orders_model, create_predicate_vm, create_column_predicate_vm);
|
||||
var vm = new GridSettingsVM<TRowData, TPredicateBuilder, TSettingsData>(owner_title, _bus, _orders_model, create_predicate_vm, create_column_predicate_vm, _create_settings_obj);
|
||||
// need to refresh before launching the settings-view bc users can chanve some grid-column-settings properties directly from the grid (e.g. width, is_collapsed, etc.)
|
||||
_grid_settings.set(grid_settings.with(columns.Select(c => c.create())));
|
||||
vm.load_from((grid_settings, _predicate_animations_data));
|
||||
vm.load(_create_settings_obj(grid_settings, _predicate_animations_data));
|
||||
if (column_flags != null) {
|
||||
foreach (var col in vm.columns) {
|
||||
if (column_flags.TryGetValue(col.id, out GridColumnSettingsFeatureFlags f))
|
||||
@@ -84,10 +95,10 @@ namespace mbts.ui {
|
||||
}
|
||||
var window = new GridSettingsDialog { DataContext = vm };
|
||||
vm.committed += () => {
|
||||
var (grid, predicate_animations_data) = vm.create();
|
||||
TSettingsData sd = vm.get_result();
|
||||
// IMPORTANT: don't overwrite current sorting
|
||||
grid = grid.with(sorting_criterion: rows_view.get_sorting_criteria(columns));
|
||||
load_from((grid, predicate_animations_data));
|
||||
sd = _create_settings_obj(sd.general.with(sorting_criterion: rows_view.get_sorting_criteria(columns)), sd.predicate_animations);
|
||||
load_from(sd);
|
||||
};
|
||||
vm.RequestClose += (s, e) => WindowUtilities.force_close(window);
|
||||
return window;
|
||||
@@ -107,20 +118,37 @@ namespace mbts.ui {
|
||||
public SolidColorBrush selection_highlight => _selection_highlight.Value;
|
||||
public IDialogCommand launch_settings_editor { get; }
|
||||
|
||||
public (GridSettings grid, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations_data) create() {
|
||||
// IMPORTANT: don't overwrite current sorting
|
||||
_grid_settings.set(grid_settings.with(_columns.Select(c => c.create()), _rows_view.get_sorting_criteria(_columns)));
|
||||
return (grid_settings, _predicate_animations_data);
|
||||
public GridSettingsVM<TRowData, TPredicateBuilder, TSettingsData> create_vm(GridSettings settings, IObservable<string> title) {
|
||||
var vm = new GridSettingsVM<TRowData, TPredicateBuilder, TSettingsData>(title, _bus, _orders_model, _create_predicate_vm, _create_column_predicate_vm, _create_settings_obj, _column_flags.ToDictionary(kv => kv.Key, kv => kv.Value.flags));
|
||||
vm.load(_create_settings_obj(settings, _predicate_animations_data));
|
||||
return vm;
|
||||
}
|
||||
|
||||
public void load_from((GridSettings grid, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations_data) value) {
|
||||
public TSettingsData create() {
|
||||
// IMPORTANT: don't overwrite current sorting
|
||||
_grid_settings.set(grid_settings.with(_columns.Select(c => {
|
||||
var col_settings = c.create();
|
||||
if (_column_flags.TryGetValue(c.id, out ImmutableColumnSettingStore store))
|
||||
col_settings = store.apply(col_settings);
|
||||
return col_settings;
|
||||
}), _rows_view.get_sorting_criteria(_columns)));
|
||||
return _create_settings_obj(grid_settings, _predicate_animations_data);
|
||||
}
|
||||
|
||||
public void load_from(TSettingsData value) => _load_from_core(value, true);
|
||||
|
||||
void _load_from_core(TSettingsData value, bool reset_immutables) {
|
||||
Disposable.dispose(_notification_subs);
|
||||
var grid = value.grid ?? GridSettings.Default;
|
||||
var grid = value.general ?? GridSettings.Default;
|
||||
_grid_settings.set(grid);
|
||||
foreach (ColumnBase col in _columns)
|
||||
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight);
|
||||
_rows_view.set_sort_descriptions(grid.sorting_criterion, _columns);
|
||||
_predicate_animations_data = value.predicate_animations_data;
|
||||
if (reset_immutables) {
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
_rows_view.set_sort_descriptions(grid.sorting_criterion, _columns);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
_predicate_animations_data = value.predicate_animations;
|
||||
_animation_defs.clear();
|
||||
foreach (var pa in _predicate_animations_data) {
|
||||
var predicate = pa.predicate_builder.create_predicate(_arg);
|
||||
@@ -140,6 +168,12 @@ namespace mbts.ui {
|
||||
// ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings
|
||||
// don't add insult to injury by crashing **
|
||||
if (grid_settings.columns != null && _columns != null) {
|
||||
if (reset_immutables) {
|
||||
foreach (var kv in _column_flags) {
|
||||
if (grid.columns.TryGetFirst(c => c.id == kv.Key, out GridColumnSettings cs))
|
||||
kv.Value.store(cs);
|
||||
}
|
||||
}
|
||||
var col_settings_id_map = grid_settings.columns.ToDictionary(cs => id_key(cs.id));
|
||||
var columns_id_map = _columns.ToDictionary(c => id_key(c.id));
|
||||
if (col_settings_id_map.Count == columns_id_map.Count && col_settings_id_map.Count == grid_settings.columns.Count) {
|
||||
@@ -159,5 +193,26 @@ namespace mbts.ui {
|
||||
IGridColumn<TRow> lookup_column(string col_id) =>
|
||||
_columns.OfType<IGridColumn<TRow>>().FirstOrDefault(c => string.Equals(col_id, c.id, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
|
||||
void IObserver<TSettingsData>.OnNext(TSettingsData sd) {
|
||||
if (sd == null) return;
|
||||
try {
|
||||
var grid = sd.general;
|
||||
if (grid.columns != null) {
|
||||
var cols_with_applied_flags = grid.columns.ToList();
|
||||
for (int i = 0; i < cols_with_applied_flags.Count; i++) {
|
||||
if (_column_flags.TryGetValue(cols_with_applied_flags[i].id, out ImmutableColumnSettingStore store)) {
|
||||
cols_with_applied_flags[i] = store.apply(cols_with_applied_flags[i]);
|
||||
}
|
||||
}
|
||||
sd = _create_settings_obj(grid.with(cols_with_applied_flags), sd.predicate_animations);
|
||||
}
|
||||
_load_from_core(sd, false);
|
||||
} catch (Exception ex) {
|
||||
Log.Ui.error($"Error loading GridSettings (as observer): {ex}");
|
||||
}
|
||||
}
|
||||
void IObserver<TSettingsData>.OnError(Exception error) { }
|
||||
void IObserver<TSettingsData>.OnCompleted() { }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui;
|
||||
@@ -18,14 +19,38 @@ using CellAnimation = mbts.ui.CellAnimation;
|
||||
using FontFamily = System.Windows.Media.FontFamily;
|
||||
|
||||
namespace mbts.spark {
|
||||
interface IGridSettingsFactory<TRowData, TPredicateBuilder> : IValueFactory<(GridSettings grid, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations_data)>
|
||||
where TPredicateBuilder : IPredicateBuilder<TRowData>, new() { }
|
||||
interface IGridSettingsWithNotifications<TRowData, TPredicateBuilder> where TPredicateBuilder : IPredicateBuilder<TRowData> {
|
||||
GridSettings general { get; }
|
||||
IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations { get; }
|
||||
}
|
||||
|
||||
partial class GridSettingsVM<TRowData, TPredicateBuilder> : DisposableViewModelBase, IGridSettingsFactory<TRowData, TPredicateBuilder>, IRequestViewClose
|
||||
where TPredicateBuilder : IPredicateBuilder<TRowData>, new() {
|
||||
class GridSettingsWithNotifications<TRowData, TPredicateBuilder> : IEquatable<GridSettingsWithNotifications<TRowData, TPredicateBuilder>> where TPredicateBuilder : IPredicateBuilder<TRowData> {
|
||||
public readonly GridSettings grid;
|
||||
public readonly IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations_data;
|
||||
public GridSettingsWithNotifications(GridSettings grid, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations_data) {
|
||||
this.grid = grid;
|
||||
this.predicate_animations_data = predicate_animations_data;
|
||||
}
|
||||
|
||||
public bool Equals(GridSettingsWithNotifications<TRowData, TPredicateBuilder> other) =>
|
||||
other != null &&
|
||||
EqualityComparer.null_check_equals(grid, other.grid) &&
|
||||
EqualityComparer.safe_sequence_equals(predicate_animations_data, other.predicate_animations_data);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as GridSettingsWithNotifications<TRowData, TPredicateBuilder>);
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
EqualityComparer.safe_get_hash_code(grid),
|
||||
EqualityComparer.safe_aggregate_hash(predicate_animations_data));
|
||||
}
|
||||
|
||||
partial class GridSettingsVM<TRowData, TPredicateBuilder, TSettingsData> : DisposableViewModelBase, IRequestViewClose, IWindowSettingsVM<TSettingsData>, ITemplateSelectorKey
|
||||
where TPredicateBuilder : IPredicateBuilder<TRowData>, new()
|
||||
where TSettingsData :class, IGridSettingsWithNotifications<TRowData, TPredicateBuilder> {
|
||||
//
|
||||
protected readonly Func<IPropertyWrapperVM<TPredicateBuilder>> _create_predicate_vm;
|
||||
protected readonly Func<string, IPropertyWrapperVM<TPredicateBuilder>> _create_column_predicate_vm;
|
||||
readonly Func<GridSettings, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>>, TSettingsData> _create_settings_obj;
|
||||
readonly Property<IColumnVM> _current_column;
|
||||
readonly Property<FontFamily> _font_family;
|
||||
readonly Property<FontWeight> _font_weight;
|
||||
@@ -33,16 +58,22 @@ namespace mbts.spark {
|
||||
readonly Property<double> _font_size;
|
||||
readonly Property<bool> _show_predicate_animations, _show_cell_predicate_animations;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> _column_flags;
|
||||
|
||||
public GridSettingsVM(
|
||||
IObservable<string> owner_title, IBus bus, OrdersModel orders_model,
|
||||
Func<IPropertyWrapperVM<TPredicateBuilder>> create_predicate_vm, Func<string, IPropertyWrapperVM<TPredicateBuilder>> create_column_predicate_vm) {
|
||||
Func<IPropertyWrapperVM<TPredicateBuilder>> create_predicate_vm,
|
||||
Func<string, IPropertyWrapperVM<TPredicateBuilder>> create_column_predicate_vm,
|
||||
Func<GridSettings, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>>, TSettingsData> create_settings_obj,
|
||||
IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> column_flags = null) {
|
||||
//
|
||||
_owner_title = backing(nameof(owner_title), owner_title);
|
||||
this.bus = bus;
|
||||
_orders_model = orders_model;
|
||||
_create_predicate_vm = create_predicate_vm;
|
||||
_create_column_predicate_vm = create_column_predicate_vm;
|
||||
_create_settings_obj = create_settings_obj;
|
||||
_column_flags = column_flags ?? new Dictionary<string, GridColumnSettingsFeatureFlags>();
|
||||
_font_family = backing(nameof(font_family), TextUtils.StandardFontFamily);
|
||||
_font_size = backing(nameof(font_size), FontSizes.Small);
|
||||
_font_weight = backing(nameof(font_weight), FontWeights.Normal);
|
||||
@@ -68,6 +99,7 @@ namespace mbts.spark {
|
||||
public ColorPickerPopupVM font_color { get; } = new ColorPickerPopupVM();
|
||||
public ColorPickerPopupVM selection_highlight { get; } = new ColorPickerPopupVM();
|
||||
public double font_size { get => _font_size.Value; set => _font_size.set(value); }
|
||||
public bool enable_sound_triggers => true;
|
||||
public bool show_predicate_animations { get => _show_predicate_animations.Value; set => _show_predicate_animations.set(value); }
|
||||
public bool show_cell_predicate_animations { get => _show_cell_predicate_animations.Value; set => _show_cell_predicate_animations.set(value); }
|
||||
public ObservableEditableCollection<PredicateRowAnimationVM> predicate_animations { get; }
|
||||
@@ -80,7 +112,9 @@ namespace mbts.spark {
|
||||
new DialogSkirtItem("Apply", new DelegateCommand(() => committed?.Invoke()))
|
||||
};
|
||||
|
||||
public (GridSettings grid, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations_data) create() {
|
||||
public string key => ui.spark.GridOrLadderSettingsSelectorKeys.SimpleGrid;
|
||||
|
||||
public TSettingsData get_result() {
|
||||
var animation_map = predicate_animations
|
||||
.Select(p => p.create())
|
||||
.GroupBy(d => d.predicate)
|
||||
@@ -94,22 +128,22 @@ namespace mbts.spark {
|
||||
foreach (IGrouping<TPredicateBuilder, CellAnimation> g in animations)
|
||||
animation_map.AddOrUpdate(g.Key, p => animation_tuple(c: g), (p, t) => t.cell_animations.AddRange(g));
|
||||
}
|
||||
return (new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), col_settings),
|
||||
return _create_settings_obj(new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), col_settings),
|
||||
animation_map.Select(kv => PredicateAnimations.create_data<TRowData, TPredicateBuilder>(kv.Key, kv.Value.cell_animations, kv.Value.row_animations, kv.Value.sounds)));
|
||||
//
|
||||
(List<RowAnimation> row_animations, List<SoundNotification> sounds, List<CellAnimation> cell_animations) animation_tuple(IEnumerable<RowAnimation> r = null, IEnumerable<SoundNotification> s = null, IGrouping<TPredicateBuilder, CellAnimation> c = null) =>
|
||||
(r?.ToList() ?? new List<RowAnimation>(), s?.ToList() ?? new List<SoundNotification>(), c?.ToList() ?? new List<CellAnimation>());
|
||||
}
|
||||
|
||||
public void load_from((GridSettings grid, IEnumerable<IPredicateAnimationsData<TRowData, TPredicateBuilder>> predicate_animations_data) value) {
|
||||
_font_family.set(new FontFamily(value.grid.font_family));
|
||||
_font_weight.set(value.grid.font_weight);
|
||||
font_color.load_from(value.grid.font_color.rgb_solid_brush());
|
||||
selection_highlight.load_from(value.grid.selection_highlight.to_brush());
|
||||
_font_size.set(value.grid.font_size);
|
||||
public void load(TSettingsData sd) {
|
||||
_font_family.set(new FontFamily(sd.general.font_family));
|
||||
_font_weight.set(sd.general.font_weight);
|
||||
font_color.load_from(sd.general.font_color.rgb_solid_brush());
|
||||
selection_highlight.load_from(sd.general.selection_highlight.to_brush());
|
||||
_font_size.set(sd.general.font_size);
|
||||
var col_animations = new Dictionary<string, Dictionary<TPredicateBuilder, List<CellAnimation>>>();
|
||||
predicate_animations.Clear();
|
||||
foreach (var pa in value.predicate_animations_data) {
|
||||
foreach (var pa in sd.predicate_animations) {
|
||||
if (pa.row_animations.Count > 0 || pa.sound_notifications.Count > 0) {
|
||||
var vm = new PredicateRowAnimationVM(_create_predicate_vm());
|
||||
vm.load_from((pa.predicate_builder, pa.row_animations, pa.sound_notifications));
|
||||
@@ -122,9 +156,14 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
var curr_col_id = current_column?.id;
|
||||
columns.SetItems(value.grid.columns.Select(col => {
|
||||
columns.SetItems(sd.general.columns.Select(col => {
|
||||
var vm = new ColumnVM(this);
|
||||
vm.load_from((col, col_animations.GetValueOrDefault(col.id)?.ToFlatLookup(kv => kv.Key, kv => kv.Value)));
|
||||
// reapply our favorite column flags
|
||||
// this comes in handy when performing Load From Factory - the editor will contain
|
||||
// a consistent view of what can be overridden and what cannot
|
||||
if (_column_flags.TryGetValue(vm.id, out GridColumnSettingsFeatureFlags flags))
|
||||
vm.apply_flags(flags);
|
||||
return vm;
|
||||
}));
|
||||
if (curr_col_id == null || !columns.TryGetFirst(c => c.id == curr_col_id, out IColumnVM curr_column))
|
||||
|
||||
@@ -342,9 +342,8 @@
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalSplit}}"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<!--TODO: enable when FontFamilies are ready-->
|
||||
<TextBlock Foreground="{u:BTSColor disabled_text}">Font</TextBlock>
|
||||
<Grid Grid.Column="2" IsEnabled="False" HorizontalAlignment="Left" Width="{StaticResource ControlLongWidth}">
|
||||
<TextBlock>Font</TextBlock>
|
||||
<Grid Grid.Column="2" HorizontalAlignment="Left" Width="{StaticResource ControlLongWidth}">
|
||||
<c:PopupControl HorizontalAlignment="Stretch" Header="{Binding}" HeaderTemplate="{StaticResource FontPickerPopupHeaderTemplate}" Background="{StaticResource ComboBox.Static.Background}">
|
||||
<c:FontPicker SelectedFontFamily="{Binding font_family}" SelectedFontSize="{Binding font_size}" SelectedFontWeight="{Binding font_weight}"/>
|
||||
</c:PopupControl>
|
||||
@@ -359,6 +358,9 @@
|
||||
<GroupBox.Style>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding enable_sound_triggers}" Value="{u:False}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding show_predicate_animations}" Value="{u:False}">
|
||||
<Setter Property="Height" Value="{StaticResource CollapsedAnimationItemsHeight}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace mbts.ui.spark {
|
||||
|
||||
public static IMultiValueConverter ColumnDisplayNameVisibilityConverter =>
|
||||
ValueConverter.create<string, string, Visibility>((id, header) => id == header ? Visibility.Collapsed : Visibility.Visible);
|
||||
|
||||
public bool EnableSoundTriggers { get; set; }
|
||||
}
|
||||
|
||||
static class PredicateTemplateKeys {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using bts.proto;
|
||||
using mbts.spark;
|
||||
using mbts.ui.settings;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.ui {
|
||||
abstract class ImmutableColumnSettingStore {
|
||||
public readonly GridColumnSettingsFeatureFlags flags;
|
||||
|
||||
protected ImmutableColumnSettingStore(GridColumnSettingsFeatureFlags flags) =>
|
||||
this.flags = flags;
|
||||
|
||||
public static ImmutableColumnSettingStore create(GridColumnSettingsFeatureFlags flags) {
|
||||
var stores_per_flag = new List<ImmutableColumnSettingStore>();
|
||||
if ((flags & GridColumnSettingsFeatureFlags.ImmutableFontColor) != 0) {
|
||||
stores_per_flag.Add(new ImmutableFontColorOverrideStore());
|
||||
}
|
||||
if ((flags & GridColumnSettingsFeatureFlags.ImmutableFontWeight) != 0) {
|
||||
stores_per_flag.Add(new ImmutableFontWeightOverrideStore());
|
||||
}
|
||||
return new CompositeImmutableSettingsStore(stores_per_flag, flags);
|
||||
}
|
||||
|
||||
public abstract GridColumnSettings apply(GridColumnSettings settings);
|
||||
public abstract void store(GridColumnSettings settings);
|
||||
|
||||
class ImmutableFontColorOverrideStore : ImmutableColumnSettingStore {
|
||||
NullableColorProto _value;
|
||||
|
||||
public ImmutableFontColorOverrideStore()
|
||||
: base(GridColumnSettingsFeatureFlags.ImmutableFontColor) { }
|
||||
|
||||
public override GridColumnSettings apply(GridColumnSettings settings) =>
|
||||
settings.with(font_color_override: _value);
|
||||
|
||||
public override void store(GridColumnSettings settings) =>
|
||||
_value = new NullableColorProto(settings.font_color_override);
|
||||
}
|
||||
|
||||
class ImmutableFontWeightOverrideStore : ImmutableColumnSettingStore {
|
||||
NullableFontWeightProto _value;
|
||||
|
||||
public ImmutableFontWeightOverrideStore()
|
||||
: base(GridColumnSettingsFeatureFlags.ImmutableFontWeight) { }
|
||||
|
||||
public override GridColumnSettings apply(GridColumnSettings settings) =>
|
||||
settings.with(font_weight_override: _value);
|
||||
|
||||
public override void store(GridColumnSettings settings) =>
|
||||
_value = new NullableFontWeightProto(settings.font_weight_override);
|
||||
}
|
||||
|
||||
class CompositeImmutableSettingsStore : ImmutableColumnSettingStore {
|
||||
readonly IReadOnlyList<ImmutableColumnSettingStore> _stores;
|
||||
public CompositeImmutableSettingsStore(IReadOnlyList<ImmutableColumnSettingStore> stores, GridColumnSettingsFeatureFlags flags)
|
||||
: base(flags) => _stores = stores;
|
||||
|
||||
public override GridColumnSettings apply(GridColumnSettings settings) {
|
||||
foreach (var s in _stores) {
|
||||
settings = s.apply(settings);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
public override void store(GridColumnSettings settings) {
|
||||
foreach (var s in _stores) {
|
||||
s.store(settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using bts.proto;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SimpleGridSettingsVM {
|
||||
public class SimpleColumnVM : ViewModelBase, ISimpleColumnVM {
|
||||
public readonly SimpleGridSettingsVM owner;
|
||||
|
||||
readonly Property<string> _id, _display_name;
|
||||
readonly Property<double> _width;
|
||||
readonly Property<HorizontalAlignment> _horizontal_alignment;
|
||||
readonly Property<bool> _is_visible, _can_override_font_color, _is_font_color_overridden, _can_override_font_weight, _is_font_weight_overridden;
|
||||
readonly Property<FontWeight> _font_weight_override;
|
||||
GridColumnSettings _settings;
|
||||
|
||||
public SimpleColumnVM(SimpleGridSettingsVM owner) {
|
||||
this.owner = owner;
|
||||
_id = backing(nameof(id), string.Empty);
|
||||
_display_name = backing(nameof(display_name), string.Empty);
|
||||
_width = backing(nameof(width), 70.0);
|
||||
_horizontal_alignment = backing(nameof(horizontal_alignment), HorizontalAlignment.Left);
|
||||
_is_visible = backing(nameof(is_visible), false);
|
||||
// font color override
|
||||
_can_override_font_color = backing(nameof(can_override_font_color), true);
|
||||
_is_font_color_overridden = backing(nameof(is_font_color_overridden), false);
|
||||
// font weight override
|
||||
_can_override_font_weight = backing(nameof(can_override_font_weight), true);
|
||||
_is_font_weight_overridden = backing(nameof(is_font_weight_overridden), false);
|
||||
_font_weight_override = backing(nameof(font_weight_override), FontWeights.Normal);
|
||||
}
|
||||
|
||||
public string id => _id.Value;
|
||||
public string display_name { get => _display_name.Value; set => _display_name.set(value); }
|
||||
public double width { get => _width.Value; set => _width.set(value); }
|
||||
public HorizontalAlignment horizontal_alignment { get => _horizontal_alignment.Value; set => _horizontal_alignment.set(value); }
|
||||
public bool is_visible { get => _is_visible.Value; set => _is_visible.set(value); }
|
||||
// font color override
|
||||
public bool can_override_font_color => _can_override_font_color.Value;
|
||||
public bool is_font_color_overridden { get => _is_font_color_overridden.Value; set => _is_font_color_overridden.set(value); }
|
||||
public ColorPickerPopupVM font_color_override { get; } = new ColorPickerPopupVM();
|
||||
// font weight override
|
||||
public bool can_override_font_weight => _can_override_font_weight.Value;
|
||||
public bool is_font_weight_overridden { get => _is_font_weight_overridden.Value; set => _is_font_weight_overridden.set(value); }
|
||||
public FontWeight font_weight_override { get => _font_weight_override.Value; set => _font_weight_override.set(value); }
|
||||
|
||||
public GridColumnSettings create() =>
|
||||
_settings.with(display_name, is_visible, null,
|
||||
is_font_weight_overridden ? new NullableFontWeightProto(font_weight_override) : (NullableFontWeightProto?)null,
|
||||
is_font_color_overridden ? new NullableColorProto(font_color_override.color) : (NullableColorProto?)null,
|
||||
width,
|
||||
horizontal_alignment);
|
||||
|
||||
public void load_from(GridColumnSettings value) {
|
||||
_settings = value;
|
||||
_id.set(_settings.id);
|
||||
_display_name.set(_settings.display_name);
|
||||
_width.set(_settings.width);
|
||||
_horizontal_alignment.set(_settings.horizontal_alignment);
|
||||
_is_visible.set(_settings.is_visible);
|
||||
_is_font_weight_overridden.set(_settings.font_weight_override.HasValue);
|
||||
_is_font_color_overridden.set(_settings.font_color_override.HasValue);
|
||||
if (is_font_weight_overridden) {
|
||||
_font_weight_override.set(_settings.font_weight_override.Value);
|
||||
}
|
||||
if (is_font_color_overridden) {
|
||||
font_color_override.load_from(new SolidColorBrush(_settings.font_color_override.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public void apply_flags(GridColumnSettingsFeatureFlags flags) {
|
||||
var split_flags = EnumHelper.split_flags(flags).ToList();
|
||||
// font color
|
||||
_can_override_font_color.set(!split_flags.Contains(GridColumnSettingsFeatureFlags.ImmutableFontColor));
|
||||
if (!can_override_font_color)
|
||||
_is_font_color_overridden.set(false);
|
||||
// font weight
|
||||
_can_override_font_weight.set(!split_flags.Contains(GridColumnSettingsFeatureFlags.ImmutableFontWeight));
|
||||
if (!can_override_font_weight)
|
||||
_is_font_weight_overridden.set(false);
|
||||
}
|
||||
|
||||
public override string ToString() => $"{display_name} ({id})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<Window
|
||||
x:Class="mbts.ui.spark.SimpleGridSettingsDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
SizeToContent="WidthAndHeight"
|
||||
Style="{StaticResource BTSDialog}"
|
||||
>
|
||||
<Window.Resources>
|
||||
<cv:BooleanToVisibilityConverter x:Key="DefaultTabVisibilityConverter" VisibilityForTrue="Visible" VisibilityForFalse="Hidden"/>
|
||||
<cv:BooleanToVisibilityConverter x:Key="CustomTabVisibilityConverter" VisibilityForTrue="Hidden" VisibilityForFalse="Visible"/>
|
||||
<cv:ValueMap x:Key="TabHeaderFontWeightConverter" Default="{u:FontWeight Normal}">
|
||||
<cv:KeyValue Key="{u:True}" Value="{u:FontWeight Bold}"/>
|
||||
</cv:ValueMap>
|
||||
<Style TargetType="RadioButton">
|
||||
<Setter Property="BorderBrush" Value="{StaticResource ComboBox.Static.Border}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Background" Value="{StaticResource ComboBox.Static.Background}"/>
|
||||
<Setter Property="Padding" Value="6,3"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="RadioButton">
|
||||
<Border
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
>
|
||||
<ContentPresenter/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Padding" Value="6,4,6,6"/>
|
||||
<Setter Property="BorderThickness" Value="1,1,1,0"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
<Condition Property="IsChecked" Value="False"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Background" Value="{u:BTSColor UI.Button.mouseover_background}"/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="{u:BTSColor UI.Button.disabled_foreground}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<u:KeyedStyleSelector x:Key="LeftButtonStyleSelector">
|
||||
<u:StyleSelectorKeyValue Key="{x:Static usp:SimpleGridSettingsDialog.ResetToFactoryKey}">
|
||||
<Style TargetType="Button" BasedOn="{StaticResource DialogSkirtButtonStyle}">
|
||||
<Setter Property="Width" Value="94"/>
|
||||
</Style>
|
||||
</u:StyleSelectorKeyValue>
|
||||
<u:StyleSelectorKeyValue Key="{x:Static usp:SimpleGridSettingsDialog.CopyFromOtherKey}">
|
||||
<Style TargetType="Button" BasedOn="{StaticResource DialogSkirtButtonStyle}">
|
||||
<Setter Property="Width" Value="108"/>
|
||||
</Style>
|
||||
</u:StyleSelectorKeyValue>
|
||||
</u:KeyedStyleSelector>
|
||||
<u:KeyedTemplateSelector x:Key="GridOrLadderSettingsSelector">
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:GridOrLadderSettingsSelectorKeys.SimpleGrid}">
|
||||
<DataTemplate>
|
||||
<usp:GridSettingsView />
|
||||
</DataTemplate>
|
||||
</u:TemplateSelectorKeyValue>
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:GridOrLadderSettingsSelectorKeys.Ladder}">
|
||||
<DataTemplate>
|
||||
<usp:LadderSettingsView />
|
||||
</DataTemplate>
|
||||
</u:TemplateSelectorKeyValue>
|
||||
</u:KeyedTemplateSelector>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="1"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:ActionMenu HorizontalAlignment="Right" ItemsSource="{Binding action_menu_items}"/>
|
||||
<ContentControl Grid.Row="2"
|
||||
Margin="8"
|
||||
VerticalAlignment="Top"
|
||||
Content="{Binding instance_vm}"
|
||||
ContentTemplateSelector="{StaticResource GridOrLadderSettingsSelector}"
|
||||
/>
|
||||
<c:DialogSkirt
|
||||
Grid.Row="3"
|
||||
BorderThickness="1"
|
||||
BorderBrush="DarkGray"
|
||||
LeftItems="{Binding left_skirt_items}"
|
||||
LeftItemContainerStyleSelector="{StaticResource LeftButtonStyleSelector}"
|
||||
RightItems="{Binding right_skirt_items}"
|
||||
>
|
||||
<c:DialogSkirt.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
|
||||
<LinearGradientBrush.GradientStops>
|
||||
<GradientStop Color="Transparent"/>
|
||||
<GradientStop Color="{x:Static u:BTSColors.dialog_skirt}" Offset="1"/>
|
||||
</LinearGradientBrush.GradientStops>
|
||||
</LinearGradientBrush>
|
||||
</c:DialogSkirt.Background>
|
||||
</c:DialogSkirt>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
/// <summary>
|
||||
/// Interaction logic for SimpleGridSettingsDialog.xaml
|
||||
/// </summary>
|
||||
public partial class SimpleGridSettingsDialog : Window {
|
||||
public SimpleGridSettingsDialog() =>
|
||||
InitializeComponent();
|
||||
|
||||
public const string ResetToFactoryKey = "Reset", CopyFromOtherKey = "Copy";
|
||||
}
|
||||
|
||||
static class GridOrLadderSettingsSelectorKeys {
|
||||
public const string
|
||||
SimpleGrid = nameof(SimpleGrid),
|
||||
Ladder = nameof(Ladder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
using mbts.spark;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
|
||||
namespace mbts.ui {
|
||||
class SimpleGridSettingsManager<TRow> : ViewModelBase, IObserver<GridSettings> {
|
||||
readonly ObservableCollection<ColumnBase> _columns;
|
||||
readonly IReadOnlyCollection<IRowVM> _row_vms;
|
||||
readonly Property<GridSettings> _grid_settings;
|
||||
readonly DelegateProperty<FontFamily> _font_family;
|
||||
readonly DelegateProperty<FontWeight> _font_weight;
|
||||
readonly DelegateProperty<double> _font_size;
|
||||
readonly DelegateProperty<SolidColorBrush> _font_color, _selection_highlight;
|
||||
readonly IReadOnlyDictionary<string, ImmutableColumnSettingStore> _column_flags;
|
||||
|
||||
public SimpleGridSettingsManager(ObservableCollection<ColumnBase> columns)
|
||||
: this(columns, new Dictionary<string, GridColumnSettingsFeatureFlags>(), null) { }
|
||||
|
||||
public SimpleGridSettingsManager(ObservableCollection<ColumnBase> columns, IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> column_flags, IReadOnlyCollection<IRowVM> row_vms) {
|
||||
_columns = columns;
|
||||
_column_flags = column_flags.ToDictionary(kv => kv.Key, kv => ImmutableColumnSettingStore.create(kv.Value));
|
||||
_grid_settings = backing(nameof(grid_settings), GridSettings.Default);
|
||||
_font_family = backing(nameof(font_family), () => new FontFamily(grid_settings.font_family), nameof(grid_settings));
|
||||
_font_weight = backing(nameof(font_weight), () => grid_settings.font_weight, nameof(grid_settings));
|
||||
_font_size = backing(nameof(font_size), () => grid_settings.font_size, nameof(font_size));
|
||||
_font_color = backing(nameof(font_color), () => grid_settings.font_color.rgb_solid_brush().frozen(), nameof(grid_settings));
|
||||
_selection_highlight = backing(nameof(selection_highlight), () => grid_settings.selection_highlight.to_brush().frozen(), nameof(grid_settings));
|
||||
_row_vms = row_vms;
|
||||
}
|
||||
|
||||
public GridSettings grid_settings => _grid_settings.Value;
|
||||
public FontFamily font_family => _font_family.Value;
|
||||
public FontWeight font_weight => _font_weight.Value;
|
||||
public double font_size => _font_size.Value;
|
||||
public SolidColorBrush font_color => _font_color.Value;
|
||||
public SolidColorBrush selection_highlight => _selection_highlight.Value;
|
||||
|
||||
public SimpleGridSettingsVM create_vm(GridSettings settings, IObservable<string> title) {
|
||||
var vm = new SimpleGridSettingsVM(title, _column_flags.ToDictionary(kv => kv.Key, kv => kv.Value.flags));
|
||||
vm.load_from(settings);
|
||||
return vm;
|
||||
}
|
||||
|
||||
public GridSettings create() {
|
||||
// IMPORTANT: don't overwrite current sorting
|
||||
_grid_settings.set(grid_settings.with(_columns.Select(c => {
|
||||
var col_settings = c.create();
|
||||
if (_column_flags.TryGetValue(c.id, out ImmutableColumnSettingStore store))
|
||||
col_settings = store.apply(col_settings);
|
||||
return col_settings;
|
||||
})));
|
||||
return grid_settings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will overwrite the columns' immutable-value store (unlike the IObserver<<see cref="GridSettings"/>> 'OnNext' implementation)
|
||||
/// </summary>
|
||||
public void load_from(GridSettings grid) => _load_from_core(grid, true);
|
||||
|
||||
void IObserver<GridSettings>.OnCompleted() { }
|
||||
void IObserver<GridSettings>.OnError(Exception error) { }
|
||||
void IObserver<GridSettings>.OnNext(GridSettings grid) {
|
||||
if (grid.columns != null) {
|
||||
var cols_with_applied_flags = grid.columns.ToList();
|
||||
for (int i = 0; i < cols_with_applied_flags.Count; i++) {
|
||||
if (_column_flags.TryGetValue(cols_with_applied_flags[i].id, out ImmutableColumnSettingStore store)) {
|
||||
cols_with_applied_flags[i] = store.apply(cols_with_applied_flags[i]);
|
||||
}
|
||||
}
|
||||
grid = grid.with(cols_with_applied_flags);
|
||||
}
|
||||
_load_from_core(grid, false);
|
||||
}
|
||||
|
||||
void _load_from_core(GridSettings grid, bool reset_immutable_flag_stores) {
|
||||
grid = grid ?? GridSettings.Default;
|
||||
_grid_settings.set(grid);
|
||||
foreach (ColumnBase col in _columns)
|
||||
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight);
|
||||
// ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings
|
||||
// don't add insult to injury by crashing **
|
||||
if (grid_settings.columns != null) {
|
||||
if (reset_immutable_flag_stores) {
|
||||
foreach (var kv in _column_flags) {
|
||||
if (grid.columns.TryGetFirst(c => c.id == kv.Key, out GridColumnSettings cs))
|
||||
kv.Value.store(cs);
|
||||
}
|
||||
}
|
||||
if (_columns != null) {
|
||||
var col_settings_id_map = grid_settings.columns.ToDictionary(cs => id_key(cs.id));
|
||||
var columns_id_map = _columns.ToDictionary(c => id_key(c.id));
|
||||
if (col_settings_id_map.Count == columns_id_map.Count && col_settings_id_map.Count == grid_settings.columns.Count) {
|
||||
CollectionHelper.sync_with_never_duplicate(_columns, grid_settings.columns, cs => {
|
||||
throw new InvalidOperationException();
|
||||
}, c => id_key(c.id), cs => id_key(cs.id));
|
||||
}
|
||||
// ** don't assume that columns:column_settings have a perfect 1:1 relationship (they should, but it is possible we accidentally break the settings as grids/uis change) **
|
||||
foreach (var col in _columns) {
|
||||
if (col_settings_id_map.TryGetValue(id_key(col.id), out GridColumnSettings cs))
|
||||
col.load_from(cs);
|
||||
}
|
||||
}
|
||||
// invalidate all the rows to draw them again to reflect the latest settings
|
||||
if (_row_vms != null)
|
||||
foreach (var r in _row_vms) r.raise_invalidated();
|
||||
|
||||
//
|
||||
string id_key(string id) => id.ToLower();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility.text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using FontFamily = System.Windows.Media.FontFamily;
|
||||
|
||||
namespace mbts.spark {
|
||||
interface ISimpleColumnVM : IValueFactory<GridColumnSettings> {
|
||||
string id { get; }
|
||||
string display_name { get; set; }
|
||||
double width { get; set; }
|
||||
bool is_visible { get; set; }
|
||||
bool is_font_color_overridden { get; set; }
|
||||
ColorPickerPopupVM font_color_override { get; }
|
||||
bool is_font_weight_overridden { get; set; }
|
||||
FontWeight font_weight_override { get; set; }
|
||||
void apply_flags(GridColumnSettingsFeatureFlags flags);
|
||||
}
|
||||
|
||||
partial class SimpleGridSettingsVM : DisposableViewModelBase, IValueFactory<GridSettings>, IWindowSettingsVM<GridSettings>, ITemplateSelectorKey {
|
||||
readonly Property<ISimpleColumnVM> _current_column;
|
||||
readonly Property<FontFamily> _font_family;
|
||||
readonly Property<FontWeight> _font_weight;
|
||||
readonly Property<string> _font_display_name, _owner_title;
|
||||
readonly Property<double> _font_size;
|
||||
readonly Property<bool> _show_predicate_animations, _show_cell_predicate_animations;
|
||||
readonly IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> _column_flags;
|
||||
|
||||
public SimpleGridSettingsVM(IObservable<string> owner_title, IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> column_flags = null) {
|
||||
_column_flags = column_flags ?? new Dictionary<string, GridColumnSettingsFeatureFlags>();
|
||||
_owner_title = backing(nameof(owner_title), owner_title);
|
||||
_font_family = backing(nameof(font_family), TextUtils.StandardFontFamily);
|
||||
_font_size = backing(nameof(font_size), FontSizes.Small);
|
||||
_font_weight = backing(nameof(font_weight), FontWeights.Normal);
|
||||
_font_display_name = backing(nameof(font_display_name), () => TextUtils.display_name(font_family, font_size), nameof(font_family), nameof(font_size));
|
||||
_show_predicate_animations = backing(nameof(show_predicate_animations), true);
|
||||
_show_cell_predicate_animations = backing(nameof(show_cell_predicate_animations), true);
|
||||
_current_column = backing<ISimpleColumnVM>(nameof(current_column));
|
||||
}
|
||||
|
||||
public event EventHandler RequestClose;
|
||||
public event Action committed;
|
||||
|
||||
public string owner_title => _owner_title.Value;
|
||||
public ObservableCollection<ISimpleColumnVM> columns { get; } = new ObservableCollection<ISimpleColumnVM>();
|
||||
public ISimpleColumnVM current_column { get => _current_column.Value; set => _current_column.set(value); }
|
||||
public string font_display_name => _font_display_name.Value;
|
||||
public FontFamily font_family { get => _font_family.Value; set => _font_family.set(value); }
|
||||
public FontWeight font_weight { get => _font_weight.Value; set => _font_weight.set(value); }
|
||||
public ColorPickerPopupVM font_color { get; } = new ColorPickerPopupVM();
|
||||
public ColorPickerPopupVM selection_highlight { get; } = new ColorPickerPopupVM();
|
||||
public double font_size { get => _font_size.Value; set => _font_size.set(value); }
|
||||
public bool enable_sound_triggers => false;
|
||||
public bool show_predicate_animations { get => _show_predicate_animations.Value; set => _show_predicate_animations.set(value); }
|
||||
public bool show_cell_predicate_animations { get => _show_cell_predicate_animations.Value; set => _show_cell_predicate_animations.set(value); }
|
||||
public IReadOnlyList<DialogSkirtItem> dialog_skirt_items => new[] {
|
||||
new DialogSkirtItem("OK", new DelegateCommand(() => {
|
||||
committed?.Invoke();
|
||||
RequestClose?.Invoke(this, new EventArgs());
|
||||
})) { is_default = true },
|
||||
new DialogSkirtItem("Cancel", new DelegateCommand(() => RequestClose?.Invoke(this, new EventArgs()))) { is_cancel = true },
|
||||
new DialogSkirtItem("Apply", new DelegateCommand(() => committed?.Invoke()))
|
||||
};
|
||||
|
||||
public string key => ui.spark.GridOrLadderSettingsSelectorKeys.SimpleGrid;
|
||||
|
||||
public GridSettings create() =>
|
||||
new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), columns.Select(c => c.create()));
|
||||
|
||||
public void load_from(GridSettings value) {
|
||||
_font_family.set(new FontFamily(value.font_family));
|
||||
_font_weight.set(value.font_weight);
|
||||
font_color.load_from(value.font_color.rgb_solid_brush());
|
||||
selection_highlight.load_from(value.selection_highlight.to_brush());
|
||||
_font_size.set(value.font_size);
|
||||
var curr_col_id = current_column?.id;
|
||||
columns.SetItems(value.columns.Select(col => {
|
||||
var vm = new SimpleColumnVM(this);
|
||||
vm.load_from(col);
|
||||
// reapply our favorite column flags
|
||||
// this comes in handy when performing Load From Factory - the editor will contain
|
||||
// a consistent view of what can be overridden and what cannot
|
||||
if (_column_flags.TryGetValue(vm.id, out GridColumnSettingsFeatureFlags flags))
|
||||
vm.apply_flags(flags);
|
||||
return vm;
|
||||
}));
|
||||
if (curr_col_id == null || !columns.TryGetFirst(c => c.id == curr_col_id, out ISimpleColumnVM curr_column))
|
||||
curr_column = columns.FirstOrDefault();
|
||||
_current_column.set(curr_column);
|
||||
}
|
||||
|
||||
void IWindowSettingsVM<GridSettings>.load(GridSettings value) => load_from(value);
|
||||
GridSettings IWindowSettingsVM<GridSettings>.get_result() => create();
|
||||
}
|
||||
}
|
||||
Loaded 100 of 266 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user