Compare commits
789
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48f56e76d4 | ||
|
|
05e6c40a57 | ||
|
|
58464b8a0d | ||
|
|
1785f3e34d | ||
|
|
33aeb6b7ba | ||
|
|
d8fbfb49e8 | ||
|
|
4803c66506 | ||
|
|
046a0ad9c8 | ||
|
|
3f292e87da | ||
|
|
7c8a801407 | ||
|
|
4c7949ff8d | ||
|
|
fdf727a52a | ||
|
|
035911d126 | ||
|
|
58b16a4b2c | ||
|
|
6e9fd68ac4 | ||
|
|
c5289a5416 | ||
|
|
b33fe6c8cc | ||
|
|
bd95b87798 | ||
|
|
8132cb003d | ||
|
|
9e59f0c5a2 | ||
|
|
e1e0565fbd | ||
|
|
f69ec8d32b | ||
|
|
c3fdf8cb4f | ||
|
|
1670443b0f | ||
|
|
73fa2b669e | ||
|
|
2d3f576764 | ||
|
|
56b855ea83 | ||
|
|
8512b40f50 | ||
|
|
1983835588 | ||
|
|
cf6a9ac4fa | ||
|
|
c59569310f | ||
|
|
a86b231581 | ||
|
|
46365c4f2e | ||
|
|
c56adb2dc0 | ||
|
|
643482bb09 | ||
|
|
038946eddc | ||
|
|
07348a9ec8 | ||
|
|
0e29a141bf | ||
|
|
fdefad6a73 | ||
|
|
89ee079320 | ||
|
|
5f37bd5d14 | ||
|
|
892ba478fd | ||
|
|
ab88cd7920 | ||
|
|
02fedc577a | ||
|
|
87568a6a53 | ||
|
|
8440e144b4 | ||
|
|
11cbd7440d | ||
|
|
d59bb5ed0d | ||
|
|
d30c857e50 | ||
|
|
37aa7088a9 | ||
|
|
b92ad91799 | ||
|
|
6d8ee84813 | ||
|
|
05ac067b23 | ||
|
|
8e7163c5c6 | ||
|
|
763beda95c | ||
|
|
472e2b2f0c | ||
|
|
2998a2b753 | ||
|
|
036550aaae | ||
|
|
4378947e44 | ||
|
|
bd7a698259 | ||
|
|
65341ee35d | ||
|
|
e57a3d37f7 | ||
|
|
437e92b361 | ||
|
|
a9c3c41d6e | ||
|
|
5a12a973f3 | ||
|
|
639b2dfe09 | ||
|
|
8cdbf873cd | ||
|
|
7990c97921 | ||
|
|
fce22c28d1 | ||
|
|
339cd017d5 | ||
|
|
3380d2ba0b | ||
|
|
d18d8dcf37 | ||
|
|
6bf628da5f | ||
|
|
bbcc92c38c | ||
|
|
0ad1f73293 | ||
|
|
1b100bccf7 | ||
|
|
7a74c5852a | ||
|
|
49f3c8b329 | ||
|
|
9514a10c27 | ||
|
|
e6a7db2c61 | ||
|
|
f69039c043 | ||
|
|
40d6727b02 | ||
|
|
796c39988f | ||
|
|
0f2a8cd746 | ||
|
|
55d05ecd12 | ||
|
|
8b61a63634 | ||
|
|
3431ffc480 | ||
|
|
c22ce4b886 | ||
|
|
eab2852773 | ||
|
|
77d47fd387 | ||
|
|
eccd965ce1 | ||
|
|
ca8b691160 | ||
|
|
bcaf65d7c6 | ||
|
|
fa7725eba2 | ||
|
|
34aa1d20f2 | ||
|
|
85643adcf3 | ||
|
|
4b9e046046 | ||
|
|
4bafada602 | ||
|
|
701e883aea | ||
|
|
7e54eea62f | ||
|
|
b2eeddc628 | ||
|
|
9b2a4ca6b1 | ||
|
|
9488620041 | ||
|
|
0ad78b75ca | ||
|
|
c99a3c9fe9 | ||
|
|
bc4643fbf3 | ||
|
|
792801f936 | ||
|
|
dd4ef2429e | ||
|
|
0af6290566 | ||
|
|
7e8b804fa0 | ||
|
|
df769d55e4 | ||
|
|
1ea65185bd | ||
|
|
8d068221dc | ||
|
|
d3f2ecc78e | ||
|
|
38f212e633 | ||
|
|
97e73a23b0 | ||
|
|
7546c5f2e2 | ||
|
|
324d1a3d74 | ||
|
|
f9c15a4d44 | ||
|
|
68fb8dade4 | ||
|
|
ee1230c5f9 | ||
|
|
1c98cfab0d | ||
|
|
376e2c3af8 | ||
|
|
bbce4bd05c | ||
|
|
c98559beb6 | ||
|
|
c13c8ba16f | ||
|
|
4d9f26af90 | ||
|
|
23a732d6da | ||
|
|
7b2186261a | ||
|
|
63d88993a7 | ||
|
|
152c02e45e | ||
|
|
7bf4abf38d | ||
|
|
3ab3259f9a | ||
|
|
cf0916379c | ||
|
|
e8b4567e60 | ||
|
|
a0e86faa78 | ||
|
|
2cbd5510e2 | ||
|
|
b9de5602cd | ||
|
|
96369aa5ee | ||
|
|
2115ef9b74 | ||
|
|
75ee9a954b | ||
|
|
7ed0167c4e | ||
|
|
b5220050f2 | ||
|
|
875f32b980 | ||
|
|
e9e0d017aa | ||
|
|
1d065678d2 | ||
|
|
bb54a8b448 | ||
|
|
0629875c3a | ||
|
|
38e8463717 | ||
|
|
7a30b58cea | ||
|
|
e406f463de | ||
|
|
59d68503a8 | ||
|
|
909336ccbd | ||
|
|
7d45bc9a36 | ||
|
|
23c8b60ea3 | ||
|
|
4dc7fb0bea | ||
|
|
4ea08c09ee | ||
|
|
6ec39a9276 | ||
|
|
4a74066319 | ||
|
|
80fde10cd5 | ||
|
|
58c3303004 | ||
|
|
c58b6fee77 | ||
|
|
b8b284441c | ||
|
|
07e89ff972 | ||
|
|
da90ffef12 | ||
|
|
b12f7cac6d | ||
|
|
c40dfdd171 | ||
|
|
b8163c7f1e | ||
|
|
7693933a7b | ||
|
|
675f3e0675 | ||
|
|
44454ef4c0 | ||
|
|
f6f59adcc5 | ||
|
|
dad1e40f93 | ||
|
|
ab950cba91 | ||
|
|
4f32e86fbd | ||
|
|
132516b0a7 | ||
|
|
b95fd9e604 | ||
|
|
72fa852208 | ||
|
|
609bd05734 | ||
|
|
9dbf6cafbd | ||
|
|
3ae7a3976f | ||
|
|
5aff485890 | ||
|
|
430511e4a8 | ||
|
|
a2403c643d | ||
|
|
1bd2b4c601 | ||
|
|
4cd6da6c30 | ||
|
|
c2a5be6d67 | ||
|
|
9d70906aca | ||
|
|
8e0bbbc312 | ||
|
|
e52fc2d196 | ||
|
|
47b1e8f64d | ||
|
|
ae3b94d6f4 | ||
|
|
44cf948d5a | ||
|
|
d983db75c4 | ||
|
|
ba02db4e40 | ||
|
|
68ca16103d | ||
|
|
e36cb26bec | ||
|
|
74bc669083 | ||
|
|
986f228113 | ||
|
|
92b02833df | ||
|
|
96754066bd | ||
|
|
c6ed7a46cd | ||
|
|
8a05c1dbf7 | ||
|
|
5fcd2a08b2 | ||
|
|
6483a58351 | ||
|
|
69c64b47f1 | ||
|
|
b2fb253969 | ||
|
|
5967a7c1e7 | ||
|
|
b908c576ba | ||
|
|
56497df68a | ||
|
|
b89c561490 | ||
|
|
7794aa89c2 | ||
|
|
f11215df36 | ||
|
|
5c2a1fa48e | ||
|
|
29876ec04a | ||
|
|
513f7e1310 | ||
|
|
867a52024a | ||
|
|
9d6b985400 | ||
|
|
706b3b151d | ||
|
|
e1f6c27a94 | ||
|
|
cdaf9e9875 | ||
|
|
71993d4a2d | ||
|
|
84115df405 | ||
|
|
5407279504 | ||
|
|
e0b211d002 | ||
|
|
ec6d4936f1 | ||
|
|
135803b2ce | ||
|
|
2fb91e8dd9 | ||
|
|
b12fc408b5 | ||
|
|
7193122a73 | ||
|
|
1ea8a1c093 | ||
|
|
b7200c0581 | ||
|
|
1ab5b65060 | ||
|
|
ac53734052 | ||
|
|
cdbce6b141 | ||
|
|
f18235bf96 | ||
|
|
f8e638d74a | ||
|
|
32dd64fca7 | ||
|
|
395f686e19 | ||
|
|
62f91d6ebb | ||
|
|
2de2189710 | ||
|
|
51aa3edb17 | ||
|
|
01fb88a525 | ||
|
|
cd52dad334 | ||
|
|
f1a7fdf5cd | ||
|
|
96de949ac5 | ||
|
|
23a860f7bb | ||
|
|
2cb539508a | ||
|
|
7059edc0b6 | ||
|
|
d417d99357 | ||
|
|
abc400e8c4 | ||
|
|
52fd21c933 | ||
|
|
d93e7a9f84 | ||
|
|
bd8b15bbd9 | ||
|
|
8ac6993fd8 | ||
|
|
b518ae3eca | ||
|
|
64f19a93ad | ||
|
|
dad45f736a | ||
|
|
2b6a60a79f | ||
|
|
a0306a412d | ||
|
|
afae51012b | ||
|
|
eaa12d0788 | ||
|
|
a5c3d5cf09 | ||
|
|
1d9333724f | ||
|
|
fb48fd82c7 | ||
|
|
bf13115a14 | ||
|
|
4bf147eee4 | ||
|
|
39be08432c | ||
|
|
0474ea1d53 | ||
|
|
78090ab15c | ||
|
|
2d37f88553 | ||
|
|
97447fe634 | ||
|
|
e11835d654 | ||
|
|
7797cf7435 | ||
|
|
71071edd05 | ||
|
|
1fb38badfb | ||
|
|
bd3399560b | ||
|
|
1ef5c670bf | ||
|
|
095f258edf | ||
|
|
5da057dc50 | ||
|
|
0a84cb0c81 | ||
|
|
d8bc3c757f | ||
|
|
80cbb9869b | ||
|
|
4a517adefb | ||
|
|
18e12f47b8 | ||
|
|
b1687f92e0 | ||
|
|
a9fb92e1d2 | ||
|
|
4781f0ba06 | ||
|
|
122451c20f | ||
|
|
08efcf9737 | ||
|
|
fad111314d | ||
|
|
8c368affd9 | ||
|
|
730d43bfd8 | ||
|
|
13e95023af | ||
|
|
283a90bc50 | ||
|
|
3d33efd7a7 | ||
|
|
8bf0ed7536 | ||
|
|
1b2c0e03e0 | ||
|
|
1a7e9776dd | ||
|
|
61d297293d | ||
|
|
e9aec2aad6 | ||
|
|
96d76d26b5 | ||
|
|
c0bc773bbc | ||
|
|
c0cfd6e167 | ||
|
|
b44fec29b1 | ||
|
|
425b1a82fd | ||
|
|
2cdf21455e | ||
|
|
fd523a645d | ||
|
|
32ff893f99 | ||
|
|
5927be56a2 | ||
|
|
b35cfbd824 | ||
|
|
3f1d98a613 | ||
|
|
e8423ee11e | ||
|
|
6cca33841a | ||
|
|
11468548c2 | ||
|
|
402abafaca | ||
|
|
664a46d438 | ||
|
|
098fbb1a23 | ||
|
|
38e32c645d | ||
|
|
6de4d74d87 | ||
|
|
b27a54e0fb | ||
|
|
3040516f84 | ||
|
|
5262cb4966 | ||
|
|
5a4199bf93 | ||
|
|
4eb2790ee4 | ||
|
|
860640cda5 | ||
|
|
354c3e04bf | ||
|
|
ac6a5cd88d | ||
|
|
7384a11070 | ||
|
|
cc4bc755ae | ||
|
|
a907675a41 | ||
|
|
b25121d94a | ||
|
|
70eed209e5 | ||
|
|
868aa305d1 | ||
|
|
82d59252ec | ||
|
|
aa921a22b7 | ||
|
|
a5152fa4cc | ||
|
|
5208be6cd1 | ||
|
|
b764726d99 | ||
|
|
3170574272 | ||
|
|
6b557b1f88 | ||
|
|
be062cc014 | ||
|
|
f4a2b788ff | ||
|
|
b773e801f7 | ||
|
|
7bf099060a | ||
|
|
0120c58ed4 | ||
|
|
c791dd81d6 | ||
|
|
141e0a64d4 | ||
|
|
ab57fac1a8 | ||
|
|
0cda9ec9ae | ||
|
|
fefa659150 | ||
|
|
3ea77974d4 | ||
|
|
70e264e693 | ||
|
|
6f9d0c4897 | ||
|
|
198d006e6d | ||
|
|
0cbe1a6988 | ||
|
|
6bd8f1d1ac | ||
|
|
7fed123f99 | ||
|
|
c6e468aba4 | ||
|
|
2d96a7e9c4 | ||
|
|
aa345e2dd7 | ||
|
|
0946c528a9 | ||
|
|
0f8ce93a21 | ||
|
|
5106f99870 | ||
|
|
eaa09435c8 | ||
|
|
18f77215f0 | ||
|
|
2b29a6ae17 | ||
|
|
73544c13a7 | ||
|
|
b1f1fe682f | ||
|
|
4aa8818cb0 | ||
|
|
048d8d1882 | ||
|
|
60adb8b55b | ||
|
|
da5ebf27c8 | ||
|
|
5dfd0ffbdd | ||
|
|
12e4e3ad8d | ||
|
|
115bc16b12 | ||
|
|
7b2a81c51d | ||
|
|
75ccf5a1af | ||
|
|
09bc617859 | ||
|
|
f4890e0adc | ||
|
|
1fed3a0ccf | ||
|
|
075fbff042 | ||
|
|
88073c6c4f | ||
|
|
53d32753f1 | ||
|
|
10e62d1ce7 | ||
|
|
013153add7 | ||
|
|
cb53e1eb31 | ||
|
|
1d94edd4b9 | ||
|
|
7dcb1e67d9 | ||
|
|
19cc2c4ed8 | ||
|
|
83cb900c1c | ||
|
|
340c270e8a | ||
|
|
1263b91cfd | ||
|
|
4e27cc6140 | ||
|
|
688e0d650e | ||
|
|
d591420054 | ||
|
|
8b0dad3d61 | ||
|
|
f0605f1e96 | ||
|
|
be523c1848 | ||
|
|
6d721fa186 | ||
|
|
905f079757 | ||
|
|
e53c186ea9 | ||
|
|
3d08755ffe | ||
|
|
d3422010df | ||
|
|
70a768e475 | ||
|
|
2503dd414f | ||
|
|
30f5a56a8d | ||
|
|
3d42ef609e | ||
|
|
4ad679b65d | ||
|
|
fc8f55930f | ||
|
|
8d7c7fc742 | ||
|
|
3ffa831a4e | ||
|
|
3d6326502d | ||
|
|
8ec01e5930 | ||
|
|
ebe0c1e6ec | ||
|
|
8bbfcd7734 | ||
|
|
68bd4b5a73 | ||
|
|
16d02f30c5 | ||
|
|
1f0cf2b3f5 | ||
|
|
9a3b806d9e | ||
|
|
5e4e56dab6 | ||
|
|
8925db961b | ||
|
|
4b0c22b2fa | ||
|
|
ed4d50a2fd | ||
|
|
18a60d854d | ||
|
|
44caaf6b4c | ||
|
|
bc7f1af5be | ||
|
|
d7b3f5df7c | ||
|
|
31ba0f22e9 | ||
|
|
c0cfb63d7b | ||
|
|
945ea04102 | ||
|
|
b3bf25f81d | ||
|
|
8c4a7ae0f0 | ||
|
|
d739d19a42 | ||
|
|
663dd92751 | ||
|
|
a14ec89b4f | ||
|
|
e821548a5d | ||
|
|
0dc6c16e6b | ||
|
|
3bb527247d | ||
|
|
2290c955ef | ||
|
|
d28e8a2eab | ||
|
|
b8674fcbb9 | ||
|
|
adcaee1480 | ||
|
|
c748149174 | ||
|
|
65fd36e9ae | ||
|
|
e2a3055046 | ||
|
|
2d9a61ca15 | ||
|
|
0fe6da7eab | ||
|
|
01660153c0 | ||
|
|
95d0d17bcf | ||
|
|
9009678f7c | ||
|
|
d55c52df62 | ||
|
|
24a128d041 | ||
|
|
cd699d5ad7 | ||
|
|
51b62ac66f | ||
|
|
fed0427cbc | ||
|
|
fb498adffe | ||
|
|
eb84ba8371 | ||
|
|
ab078bdad3 | ||
|
|
6f0da78530 | ||
|
|
8a0daed378 | ||
|
|
2636f5dde4 | ||
|
|
3f1aa74bbb | ||
|
|
af0152b945 | ||
|
|
a2d83a79ff | ||
|
|
e0f2b7e2ed | ||
|
|
cc919a08d0 | ||
|
|
b515e55378 | ||
|
|
c2b8e9c382 | ||
|
|
e553e5fa1e | ||
|
|
1aee8d4920 | ||
|
|
07629cadcb | ||
|
|
f3adfc142d | ||
|
|
554ff8ea6f | ||
|
|
ff66c3767d | ||
|
|
af2504b9cd | ||
|
|
535b901d6f | ||
|
|
e45952ea2c | ||
|
|
f85dbc4b5f | ||
|
|
96d59d1885 | ||
|
|
9df77f7d70 | ||
|
|
7346b99371 | ||
|
|
47f9c837f0 | ||
|
|
3d88d5b2a6 | ||
|
|
a9dee31c41 | ||
|
|
5abd9879fc | ||
|
|
6fe3d1664d | ||
|
|
2edd509635 | ||
|
|
7340753458 | ||
|
|
330cd2ba6d | ||
|
|
f935c7109f | ||
|
|
284ed47ddc | ||
|
|
e5262ed44f | ||
|
|
8b812735fb | ||
|
|
98524ee1e4 | ||
|
|
141aa3b0ba | ||
|
|
d81d13b40a | ||
|
|
cc300a7a2d | ||
|
|
a852423f4b | ||
|
|
c9e875540b | ||
|
|
b020349fe3 | ||
|
|
bf13877750 | ||
|
|
2408c8dcc7 | ||
|
|
7a329522bb | ||
|
|
fca0489085 | ||
|
|
70aa95ba36 | ||
|
|
d36b2c6dab | ||
|
|
32a9a3c425 | ||
|
|
bd15bb43fe | ||
|
|
ff49178dff | ||
|
|
d9f1c17053 | ||
|
|
55dbccfb35 | ||
|
|
630a7aef17 | ||
|
|
eca65db215 | ||
|
|
8214f838b4 | ||
|
|
270e882c04 | ||
|
|
e3ca3143be | ||
|
|
43e68cd43c | ||
|
|
242784ab3d | ||
|
|
71913cfe83 | ||
|
|
ec7a4df5ee | ||
|
|
be918fc3b1 | ||
|
|
a5421dbe2d | ||
|
|
450a7a8c86 | ||
|
|
431bed6819 | ||
|
|
02cf1fbed9 | ||
|
|
6b3a88905c | ||
|
|
fc5ebd2cb0 | ||
|
|
703d03d24a | ||
|
|
ed4fdf4284 | ||
|
|
10252ba7c0 | ||
|
|
49e462b54d | ||
|
|
9bd6c53642 | ||
|
|
27940b65ee | ||
|
|
7db16e87f4 | ||
|
|
9929f38f4d | ||
|
|
ccef457e41 | ||
|
|
f169991241 | ||
|
|
15c8ecc3e6 | ||
|
|
dfbc319926 | ||
|
|
6541ca2685 | ||
|
|
a3b94a3cf3 | ||
|
|
5fbdedfdca | ||
|
|
25c793e232 | ||
|
|
e141800e60 | ||
|
|
b15f4cebf1 | ||
|
|
a444e71f70 | ||
|
|
ed1b3aca92 | ||
|
|
a8aa62aaaa | ||
|
|
245b48cb0a | ||
|
|
3dc8dd7acb | ||
|
|
ba4c04f55a | ||
|
|
799f06247d | ||
|
|
e4b155b270 | ||
|
|
fcffe2ad11 | ||
|
|
ce44f015e5 | ||
|
|
f3a2525b8d | ||
|
|
783a71217f | ||
|
|
49d16a3e03 | ||
|
|
9f624d54b3 | ||
|
|
643cd33762 | ||
|
|
cdc0831ca6 | ||
|
|
40d808ed7a | ||
|
|
2eb1468781 | ||
|
|
d223929caa | ||
|
|
c6783383ef | ||
|
|
9b776c52f7 | ||
|
|
627f228e4e | ||
|
|
720695b613 | ||
|
|
c7e89a8e0a | ||
|
|
1704abcd47 | ||
|
|
3ba69bfdbc | ||
|
|
d8ba3e1198 | ||
|
|
2b4ee75393 | ||
|
|
eaf6e27c56 | ||
|
|
d104a6b3b7 | ||
|
|
f188869413 | ||
|
|
6c9ec7238d | ||
|
|
d2fb9f2b7b | ||
|
|
dad7fe654a | ||
|
|
f6522db4f1 | ||
|
|
5ca1c87ba8 | ||
|
|
c05be5e6a1 | ||
|
|
9f4bed9cfe | ||
|
|
e1ccbfb672 | ||
|
|
ef65163ed8 | ||
|
|
fa290833ce | ||
|
|
8b75a6fdc3 | ||
|
|
4df93163f7 | ||
|
|
5867fba95d | ||
|
|
1f68d1d7a0 | ||
|
|
050057e43f | ||
|
|
4a327411d4 | ||
|
|
62bc828e79 | ||
|
|
cb9a9bdd92 | ||
|
|
5ac21f14fb | ||
|
|
3cc78e6f72 | ||
|
|
a4091cc47f | ||
|
|
e61be98c61 | ||
|
|
9413e89ce5 | ||
|
|
a1ba9579fc | ||
|
|
59467becf9 | ||
|
|
1073ff84e9 | ||
|
|
d819e440dd | ||
|
|
d76f465086 | ||
|
|
ca6e11715d | ||
|
|
2c976f0ad1 | ||
|
|
9e8e10fce8 | ||
|
|
ac5df69c67 | ||
|
|
671ef41a5b | ||
|
|
33b69b09a0 | ||
|
|
6e8c1df339 | ||
|
|
65c873da9a | ||
|
|
0480405291 | ||
|
|
03e317f1c5 | ||
|
|
2e4dbd4431 | ||
|
|
9a32a5ec67 | ||
|
|
3bbc42941b | ||
|
|
0d9abc047b | ||
|
|
53d8c12e3f | ||
|
|
98e9dd33b8 | ||
|
|
467858b101 | ||
|
|
f37cbbe0ee | ||
|
|
de2b4ae6fd | ||
|
|
5e490f7243 | ||
|
|
633b636c57 | ||
|
|
25c9ab908d | ||
|
|
6e415700c0 | ||
|
|
58ab793534 | ||
|
|
5985d1196e | ||
|
|
e5cf7ae712 | ||
|
|
96655304ff | ||
|
|
55194f0830 | ||
|
|
c285cbafa8 | ||
|
|
ff20d27f41 | ||
|
|
f46c271194 | ||
|
|
aac84e99e2 | ||
|
|
2a9c8f5342 | ||
|
|
d32e766561 | ||
|
|
7f5f3b496a | ||
|
|
6e066e5031 | ||
|
|
773ac8ffa0 | ||
|
|
a13be3156b | ||
|
|
d2123bef10 | ||
|
|
1a9d340db1 | ||
|
|
f99e6e9188 | ||
|
|
2c9c0f8563 | ||
|
|
91939df1e6 | ||
|
|
aa2bb376b1 | ||
|
|
c251e589c5 | ||
|
|
8ca84522ac | ||
|
|
7a18a58f5d | ||
|
|
00d7c5777c | ||
|
|
31bc2a8b9b | ||
|
|
756ffdcea8 | ||
|
|
e10b5e21a5 | ||
|
|
999d28c906 | ||
|
|
69e0968c35 | ||
|
|
71c4214cb3 | ||
|
|
786689ecfc | ||
|
|
45e8e8a9b7 | ||
|
|
2116149a30 | ||
|
|
674ee6144e | ||
|
|
b5006ff520 | ||
|
|
a6e7d77d1d | ||
|
|
46e80e277a | ||
|
|
194b947e1b | ||
|
|
e34e3194df | ||
|
|
9aeab69581 | ||
|
|
d7186b7052 | ||
|
|
3c51b091b9 | ||
|
|
5e86462676 | ||
|
|
36103452da | ||
|
|
fa2f08ba75 | ||
|
|
7251abdd60 | ||
|
|
80d87c4139 | ||
|
|
7eace1a892 | ||
|
|
44684e9377 | ||
|
|
fe7bf108f3 | ||
|
|
16bf0a7236 | ||
|
|
48491d6b90 | ||
|
|
4d3a0375aa | ||
|
|
f50118fb45 | ||
|
|
03773e52ca | ||
|
|
001d1aa5b4 | ||
|
|
87df2c4d6d | ||
|
|
59321c0e46 | ||
|
|
5ee027020b | ||
|
|
4c81f3bb1e | ||
|
|
83dd20dd55 | ||
|
|
f59a2f6f48 | ||
|
|
8388768edc | ||
|
|
38e8c9a0c8 | ||
|
|
03bf7212a0 | ||
|
|
84dca4a714 | ||
|
|
c14ffe900a | ||
|
|
4428e74d10 | ||
|
|
386b5f9b6b | ||
|
|
2394f0abc4 | ||
|
|
4a4a30b0fd | ||
|
|
d530108854 | ||
|
|
5836855636 | ||
|
|
6ecbe8c951 | ||
|
|
50a6cc3223 | ||
|
|
3caee644ab | ||
|
|
021f79ff8c | ||
|
|
419de81457 | ||
|
|
fe8cb76d52 | ||
|
|
f52516b49f | ||
|
|
70e66927a6 | ||
|
|
18a0ed75c4 | ||
|
|
c62490d410 | ||
|
|
3602fd479b | ||
|
|
335d590106 | ||
|
|
a9d8a3929e | ||
|
|
b814a23a57 | ||
|
|
d6aeda8cfd | ||
|
|
c95b402683 | ||
|
|
ebecef31f4 | ||
|
|
cc815c0778 | ||
|
|
6a5cde8a33 | ||
|
|
d58c1b5fca | ||
|
|
dd729f815d | ||
|
|
49f3ad6304 | ||
|
|
7c20402bd5 | ||
|
|
09f96d4cce | ||
|
|
04d0e82ea8 | ||
|
|
d855a7025e | ||
|
|
bdb7dfb8c6 | ||
|
|
a64001a333 | ||
|
|
07a406142a | ||
|
|
7523e812ea | ||
|
|
a008bac794 | ||
|
|
904cea0389 | ||
|
|
005073060a | ||
|
|
eafd648a19 | ||
|
|
964ad04e94 | ||
|
|
c082f181c6 | ||
|
|
747cc872ea | ||
|
|
ab00a4dc98 | ||
|
|
8045be92bf | ||
|
|
e6ff41428f | ||
|
|
f6bc4c953a | ||
|
|
a449ff23a4 | ||
|
|
8e5db104a2 | ||
|
|
dc4d3a9ea3 | ||
|
|
a8ffb45529 | ||
|
|
2532c2b2a1 | ||
|
|
3897dbc6e5 | ||
|
|
d1daf73086 | ||
|
|
4a02df82a4 | ||
|
|
3b73cb6e99 | ||
|
|
c891c3dd30 | ||
|
|
f318dffdeb | ||
|
|
9bc7edb246 | ||
|
|
ac2fa9b7bc | ||
|
|
9abf59170a | ||
|
|
fec3c68958 | ||
|
|
90fd7bfd1b | ||
|
|
9544b69047 | ||
|
|
9beb8e0035 | ||
|
|
27558a3dca | ||
|
|
8852671151 | ||
|
|
19b9eb69f1 | ||
|
|
b5869e348c | ||
|
|
2ed0a14c29 | ||
|
|
be751c0443 | ||
|
|
557e539fad | ||
|
|
057e41342b | ||
|
|
96f35493a4 | ||
|
|
2f65e3d257 | ||
|
|
dcffc8b2db | ||
|
|
04873b5847 | ||
|
|
076e81c529 | ||
|
|
1dc4293eb5 | ||
|
|
01d1753620 | ||
|
|
669da60852 | ||
|
|
7e46267e43 | ||
|
|
fde007bf30 | ||
|
|
1fdaf6933d | ||
|
|
522ba48c85 | ||
|
|
70fcc09b02 | ||
|
|
bfd72eb2b2 | ||
|
|
0e1352e99b | ||
|
|
feeb3348bf | ||
|
|
7c8da47f6e | ||
|
|
890143c806 | ||
|
|
51b8603fbb | ||
|
|
2934ed642d |
No files matched your search
@@ -9,6 +9,8 @@ cmake-build-debug/
|
||||
.vs
|
||||
obj/
|
||||
*.csproj.user
|
||||
*.vspx
|
||||
*.psess
|
||||
msbuild.log
|
||||
*.orig
|
||||
vcxproj.user
|
||||
@@ -16,3 +18,6 @@ src/client/spark/Properties/launchSettings.json
|
||||
compile_commands.json
|
||||
.cquery_cached_index/
|
||||
src/client/sparkcmd/Properties/launchSettings.json
|
||||
node_modules
|
||||
paket-files
|
||||
project.lock.json
|
||||
Vendored
+101
-95
@@ -1,96 +1,102 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"defines": [],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Windows",
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"includePath": [
|
||||
"${workspaceRoot}/src",
|
||||
"${workspaceRoot}/build/src",
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include"
|
||||
],
|
||||
"defines": [
|
||||
"__linux__",
|
||||
"__x86_64__"
|
||||
],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceRoot}/src",
|
||||
"${workspaceRoot}/build/src",
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/*"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "WindowsQuick",
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"includePath": [
|
||||
"${workspaceRoot}/src",
|
||||
"${workspaceRoot}/build/src",
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include"
|
||||
],
|
||||
"defines": [
|
||||
"__linux__",
|
||||
"__x86_64__"
|
||||
],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceRoot}/src"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": "${workspaceRoot}/.vscode/browse.vc.db"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 3
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"defines": [],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
},
|
||||
{
|
||||
"name": "Windows",
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"includePath": [
|
||||
"${workspaceRoot}/src",
|
||||
"${workspaceRoot}/build/src",
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include"
|
||||
],
|
||||
"defines": [
|
||||
"__linux__",
|
||||
"__x86_64__"
|
||||
],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceRoot}/src",
|
||||
"${workspaceRoot}/build/src",
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/*"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
},
|
||||
{
|
||||
"name": "WindowsQuick",
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"includePath": [
|
||||
"${workspaceRoot}/src",
|
||||
"${workspaceRoot}/build/src",
|
||||
"${workspaceRoot}/build/ninja_debug/src",
|
||||
"${workspaceRoot}/externals/windows/include",
|
||||
"${workspaceRoot}/externals/linux/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/local/include",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/lib/gcc/x86_64-linux-gnu/6/include-fixed",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu",
|
||||
"${workspaceRoot}/externals/windows/vscode/usr/include"
|
||||
],
|
||||
"defines": [
|
||||
"__linux__",
|
||||
"__x86_64__"
|
||||
],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceRoot}/src"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": "${workspaceRoot}/.vscode/browse.vc.db"
|
||||
},
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
Vendored
+77
-1
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
@@ -31,6 +32,17 @@
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "SDK Launch",
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
|
||||
"args": ["md","GLBX","Future","ES","ESM8"],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
"requireExactSource": false
|
||||
},
|
||||
{
|
||||
"name": "Client Attach",
|
||||
"type": "clr",
|
||||
@@ -106,12 +118,76 @@
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Admin Service Tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": ["test", "[admin_service]"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"preLaunchTask": "",
|
||||
"logging": {
|
||||
"engineLogging": false
|
||||
},
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "enable pretty-printer",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": ["bts@sparkvm"],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Limiter Tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": ["test", "[limiter]"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"preLaunchTask": "",
|
||||
"logging": {
|
||||
"engineLogging": false
|
||||
},
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "enable pretty-printer",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
],
|
||||
"pipeTransport": {
|
||||
"pipeCwd": "${workspaceRoot}",
|
||||
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
|
||||
"pipeArgs": ["bts@sparkvm"],
|
||||
"debuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/home/bts/spark/": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "MD Tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
|
||||
"args": ["test", "-a", "[synthetic]"],
|
||||
"args": ["test", "[md]"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
|
||||
"environment": [],
|
||||
|
||||
+2
-2
@@ -28,6 +28,7 @@ if (WIN32)
|
||||
|
||||
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
|
||||
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} /MT")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF /LTCG")
|
||||
|
||||
add_subdirectory(src/lib)
|
||||
else ()
|
||||
@@ -36,6 +37,5 @@ else ()
|
||||
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG -O3")
|
||||
add_subdirectory(src/lib)
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/web)
|
||||
endif()
|
||||
|
||||
|
||||
+1
-1
Submodule docker/linux updated: 735b688327...6037d094b7.
+1
-1
Submodule externals updated: 445b75b8cc...281bd00a2d.
+63
-38
@@ -1,10 +1,10 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2005
|
||||
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}") = "spark_lib", "build\src\lib\spark_lib.vcxproj", "{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spark_lib", "build\src\lib\spark_lib.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
|
||||
EndProject
|
||||
@@ -12,12 +12,14 @@ 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", "{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{2256015B-E7D5-3614-8F39-814CA68A4444}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{502FD050-679A-3B50-B438-098500700919} = {502FD050-679A-3B50-B438-098500700919}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(Performance) = preSolution
|
||||
HasPerformanceSessions = true
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
@@ -53,22 +55,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
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Debug|x64.Build.0 = Debug|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Release|x64.ActiveCfg = Release|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Release|x64.Build.0 = Release|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.Release|x86.ActiveCfg = Release|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||
{9186D3A8-1C5C-372A-A4EE-E1045BCCB0C7}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Debug|x64.Build.0 = Debug|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Release|x64.ActiveCfg = Release|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Release|x64.Build.0 = Release|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.Release|x86.ActiveCfg = Release|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||
{502FD050-679A-3B50-B438-098500700919}.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
|
||||
@@ -129,22 +131,42 @@ 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
|
||||
{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
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x64.Build.0 = Debug|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x64.ActiveCfg = Release|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x64.Build.0 = Release|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x86.ActiveCfg = Release|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
|
||||
{2256015B-E7D5-3614-8F39-814CA68A4444}.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
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|Any CPU.Build.0 = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x64.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x64.Build.0 = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x86.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.MinSizeRel|x86.Build.0 = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x64.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x64.Build.0 = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Release|x86.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x86.ActiveCfg = Release|x64
|
||||
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.RelWithDebInfo|x86.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -152,4 +174,7 @@ Global
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {422D1620-0166-4C26-9A81-CA8A2EB1AB58}
|
||||
EndGlobalSection
|
||||
GlobalSection(Performance) = preSolution
|
||||
HasPerformanceSessions = true
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,30 @@
|
||||
using bts.api;
|
||||
using bts.api.admin;
|
||||
using bts.api.core;
|
||||
using bts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.spark {
|
||||
[Export]
|
||||
class AdminModel {
|
||||
readonly AdminService _service;
|
||||
|
||||
[ImportingConstructor]
|
||||
private AdminModel(AdminService service) {
|
||||
_service = service;
|
||||
|
||||
on_admin_data = _service.subscribei_admin_data().Table().Replay(1).RefCount();
|
||||
}
|
||||
|
||||
public IObservable<TableUpdate<AdminDataImmutable, int>> on_admin_data { get; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,8 @@ namespace bts.shell {
|
||||
}
|
||||
|
||||
void _show_modify_order(uint order_key) {
|
||||
if (_orders.state.TryGetValue(order_key, out OrderRecordImmutable ori) && _md_map.TryGetValue(ori.Def.SecurityKey, out SecurityMD smd))
|
||||
if (_orders.state.TryGetValue(order_key, out OrderRecordImmutable ori) && _md_map.TryGetValue(ori.Def.SecurityKey, out SecurityMD smd)
|
||||
&& (ori.HasDef && ori.Def.HasCustomDef && !(ori.Def.CustomDef.HasStop || ori.Def.CustomDef.HasStopLimit)))
|
||||
_show_order_window(new OrderModificationDialog {
|
||||
DataContext = new ExistingOrderTicketVM(_model,
|
||||
Observable.Return(new OrderRecordTuple(ori, smd)),
|
||||
|
||||
@@ -11,6 +11,7 @@ using bts.support;
|
||||
using bts.api.orderman;
|
||||
using bts.api.synthetic;
|
||||
using bts.api.diagnostic;
|
||||
using bts.api.admin;
|
||||
|
||||
namespace Spark
|
||||
{
|
||||
@@ -34,6 +35,9 @@ namespace Spark
|
||||
[Export]
|
||||
public OrderService order_service => context.locate<OrderService>();
|
||||
|
||||
[Export]
|
||||
public AdminService admin_service => context.locate<AdminService>();
|
||||
|
||||
[Export]
|
||||
public SyntheticSecurityService synthetic_security_service => context.locate<SyntheticSecurityService>();
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using mbts.ui.shell;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui {
|
||||
interface IMenuItemVM {
|
||||
string name { get; }
|
||||
string group { get; }
|
||||
ImageSource icon { get; }
|
||||
ICommand command { get; }
|
||||
object parameter { get; }
|
||||
}
|
||||
|
||||
class MenuItemVM : ViewModelBase, IMenuItemVM {
|
||||
object _parameter;
|
||||
|
||||
public MenuItemVM(string name, ICommand command, ImageSource icon = null, string group = null, object parameter = null) {
|
||||
this.name = name;
|
||||
this.group = group;
|
||||
this.command = command;
|
||||
this.icon = icon;
|
||||
_parameter = parameter;
|
||||
}
|
||||
|
||||
public string name { get; }
|
||||
public string group { get; }
|
||||
public ImageSource icon { get; }
|
||||
public ICommand command { get; }
|
||||
public object parameter {
|
||||
get => _parameter;
|
||||
set {
|
||||
_parameter = value;
|
||||
command.TryRaiseCanExecuteChanged();
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -42,7 +42,6 @@ namespace mbts.ui
|
||||
|
||||
|
||||
public static ImageSource DataCopy { get { return load_image("DataCopy.ico"); } }
|
||||
public static ImageSource Import { get { return load_image("Import.ico"); } }
|
||||
public static ImageSource Refresh { get { return load_image("Refresh.png"); } }
|
||||
public static ImageSource FavoriteWindow { get { return load_image("FavoriteWindow.png"); } }
|
||||
//public static ImageSource Ticket { get { return load_image("Ticket.png"); } } // replaced with AddOrders
|
||||
@@ -98,14 +97,17 @@ namespace mbts.ui
|
||||
public static ImageSource Clock { get { return (DrawingImage)_resources["Clock"]; } }
|
||||
public static ImageSource Defrag { get { return (DrawingImage)_resources["Defrag"]; } }
|
||||
public static ImageSource Display { get { return (DrawingImage)_resources["Display"]; } }
|
||||
public static ImageSource Export => (DrawingImage)_resources["Export"];
|
||||
public static ImageSource Filter { get { return (DrawingImage)_resources["Filter"]; } }
|
||||
public static ImageSource FilterActive { get { return (DrawingImage)_resources["FilterActive"]; } }
|
||||
public static ImageSource FilterError { get { return (DrawingImage)_resources["FilterError"]; } }
|
||||
public static ImageSource Import => (DrawingImage)_resources["Import"];
|
||||
public static ImageSource Knight { get { return (DrawingImage)_resources["Knight"]; } }
|
||||
public static ImageSource Payment { get { return (DrawingImage)_resources["Payment"]; } }
|
||||
public static ImageSource Spawn { get { return (DrawingImage)_resources["Spawn"]; } }
|
||||
public static ImageSource Target { get { return (DrawingImage)_resources["Target"]; } }
|
||||
public static ImageSource Tetris { get { return (DrawingImage)_resources["Tetris"]; } }
|
||||
public static ImageSource ViewSettings => (DrawingImage)_resources["ViewSettings"];
|
||||
public static ImageSource Wrench { get { return (DrawingImage)_resources["Wrench"]; } }
|
||||
|
||||
public static ImageSource Support => (DrawingImage)_resources["SupportIcon"];
|
||||
|
||||
@@ -319,7 +319,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="Clock">
|
||||
<DrawingImage x:Key="Clock" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
@@ -608,7 +608,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
|
||||
<DrawingImage x:Key="CancelSelected">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
@@ -668,7 +668,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage-->
|
||||
|
||||
<DrawingImage x:Key="Synthetics">
|
||||
<DrawingImage x:Key="Synthetics" x:Shared="False">
|
||||
<!-- Spreads icon -->
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
@@ -681,7 +681,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="AddSynthetic">
|
||||
<DrawingImage x:Key="AddSynthetic" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
@@ -693,7 +693,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="RemoveSynthetic">
|
||||
<DrawingImage x:Key="RemoveSynthetic" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
@@ -706,7 +706,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage>
|
||||
|
||||
|
||||
<DrawingImage x:Key="ModifySynthetic">
|
||||
<DrawingImage x:Key="ModifySynthetic" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
@@ -718,7 +718,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="CloneSynthetic">
|
||||
<DrawingImage x:Key="CloneSynthetic" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
@@ -730,6 +730,45 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="ViewSettings" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing
|
||||
Brush="Black"
|
||||
Geometry="M22.889013,19.178117C23.365026,19.178117 23.75202,19.56508 23.75202,20.040055 23.75202,20.516007 23.365026,20.904069 22.889013,20.904069 22.413031,20.904069 22.02503,20.516007 22.02503,20.040055 22.02503,19.56508 22.413031,19.178117 22.889013,19.178117z M22.889013,15.528093C20.40003,15.528092 18.375006,17.552018 18.375006,20.040055 18.375006,22.529069 20.40003,24.554093 22.889013,24.554093 25.37702,24.554093 27.402014,22.529069 27.402014,20.040055 27.402014,17.552018 25.37702,15.528092 22.889013,15.528093z M22.167028,12.426042L23.834021,12.426042 23.834021,13.935075 23.981024,13.958024C24.912024,14.12404,25.772009,14.500016,26.509008,15.03505L26.62702,15.12404 27.757025,13.993058 28.935034,15.171037 27.806005,16.301042 27.940008,16.484025C28.422033,17.167008,28.769018,17.950089,28.943029,18.796037L28.996008,19.093034 30.500006,19.093034 30.500006,20.759049 29.02503,20.759049 28.997015,20.980119C28.848028,21.952043,28.472021,22.85109,27.924017,23.619035L27.805029,23.779069 28.935034,24.909074 27.757025,26.088029 26.62702,24.958024 26.61701,24.966081C25.866034,25.536027,24.981024,25.938004,24.019018,26.117082L23.834021,26.147111 23.834021,27.426042 22.167028,27.426042 22.167028,26.177018 21.949011,26.149064C20.942022,25.995011,20.014013,25.596086,19.228033,25.016007L19.151007,24.957047 18.019018,26.088029 16.841009,24.909074 17.972021,23.779069 17.863013,23.633073C17.294012,22.839005,16.908026,21.905046,16.767034,20.894059L16.75202,20.759049 15.500006,20.759049 15.500006,19.093034 16.783026,19.093034 16.812017,18.91005C16.991034,17.947038,17.393011,17.063004,17.963019,16.312028L17.972021,16.301042 16.841009,15.171037 18.019018,13.993058 19.15003,15.123063 19.310034,15.005021C20.078009,14.458024,20.977026,14.081071,21.949011,13.932023L22.167028,13.905046z M16.000014,6.0000114C14.346014,6.0000114 13.000014,7.3460112 13.000014,9.0000114 13.000014,10.654011 14.346014,12.000011 16.000014,12.000011 17.654014,12.000011 19.000014,10.654011 19.000014,9.0000114 19.000014,7.3460112 17.654014,6.0000114 16.000014,6.0000114z M16.000014,4.0000114C18.757014,4.0000114 21.000014,6.2430115 21.000014,9.0000114 21.000014,11.757011 18.757014,14.000011 16.000014,14.000011 13.243014,14.000011 11.000014,11.757011 11.000014,9.0000114 11.000014,6.2430115 13.243014,4.0000114 16.000014,4.0000114z M16.000123,0C24.651189,0 31.47924,8.0160384 31.765244,8.3580403 32.078245,8.7290418 32.078245,9.2720447 31.765244,9.6440461 31.700243,9.7220466 30.130231,11.580056 27.56321,13.533065L26.352201,11.941058C27.853213,10.798052 29.000222,9.6770465 29.645226,9.0010433 27.881213,7.1610341 22.366171,2.0000095 16.000123,2.0000095 9.6350741,2.0000095 4.1210318,7.1590343 2.3550186,9.0000434 4.013031,10.731052 8.9770689,15.388074 14.846114,15.946077L14.657112,17.936087C6.6410513,17.175083 0.49200439,9.9510479 0.23400211,9.6430461 -0.078000069,9.2720447 -0.078000069,8.7290418 0.23400211,8.3580403 0.52000427,8.0160384 7.3480568,0 16.000123,0z"
|
||||
/>
|
||||
</DrawingGroup.Children>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="Export" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing
|
||||
Brush="Black"
|
||||
Geometry="M23.471008,4.2230202L32,12.800012 23.471008,20.360005 23.471008,16.322012C23.471008,16.322012 8.7430115,12.294 5.0610046,20.360005 5.0610046,18.343985 6.3990173,8.2560073 23.471008,8.2560073z M0,0L21.334015,0 21.334015,2.1340014 21.334015,4.2690102 19.200012,4.2690102 19.200012,2.1340014 2.1340027,2.1340014 2.1340027,23.466997 19.200012,23.466997 19.200012,21.331989 21.334015,21.331989 21.334015,23.466997 21.334015,25.601 19.200012,25.601 0,25.601 0,23.466997 0,2.1340014z"
|
||||
/>
|
||||
</DrawingGroup.Children>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="Import" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing
|
||||
Brush="Black"
|
||||
Geometry="M13.594002,4.2219891L13.594002,8.255989C30.661003,8.255989 32,18.345018 32,20.360004 28.319,12.293011 13.594002,16.32399 13.594002,16.32399L13.594002,20.360004 5.0610008,12.798993z M0,0L21.333,0 21.333,2.1329974 21.333,4.2670025 19.201,4.2670025 19.201,2.1329974 2.1330032,2.1329974 2.1330032,23.467003 19.201,23.467003 19.201,21.332998 21.333,21.332998 21.333,23.467003 21.333,25.6 19.201,25.6 0,25.6 0,23.467003 0,2.1329974z"
|
||||
/>
|
||||
</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
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using mbts.co2;
|
||||
using mbts.collections;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -15,105 +18,129 @@ using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class MarketViewVM {
|
||||
public class Row : SecurityMDRow, IDisposable, IEquatable<Row> {
|
||||
public class Row : SecurityMDRow, IDisposable, IEquatable<Row>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly AnimationManager _animation = new AnimationManager();
|
||||
readonly ObservableCollection<OrderRecordImmutable> _orders = new ObservableCollection<OrderRecordImmutable>();
|
||||
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<int> _position;
|
||||
readonly Property<ushort> _bid_size, _ask_size, _last_size;
|
||||
readonly Property<ulong> _volume;
|
||||
readonly Property<bool> _inside_bids, _inside_asks;
|
||||
readonly DelegateProperty<int> _working_bid_volume, _working_ask_volume;
|
||||
readonly DelegateProperty<double> _working_best_bid, _working_best_ask;
|
||||
readonly Property<ushort> _bid_size, _ask_size;
|
||||
readonly CellProperty<int> _position;
|
||||
readonly CellProperty<ushort> _last_size;
|
||||
readonly CellProperty<ulong> _volume;
|
||||
readonly CellProperty<bool> _inside_bids, _inside_asks;
|
||||
readonly CellProperty<int> _working_bid_volume, _working_ask_volume;
|
||||
readonly CellProperty<double> _working_best_bid, _working_best_ask;
|
||||
readonly CellProperty<string> _working_bids_text, _working_asks_text;
|
||||
readonly MyOrderSummarizer _order_summarizer = new MyOrderSummarizer();
|
||||
ulong _bbo_ref, _last_ref;
|
||||
|
||||
public Row(IBus bus, SecurityMD smd, OrdersModel orders_model) : base(smd) {
|
||||
_bus = bus;
|
||||
_orders_model = orders_model;
|
||||
_position = backing(nameof(position), 0);
|
||||
_security_text = smd.security.ToString();
|
||||
_security_key_text = security_key.ToString();
|
||||
string format_price(double price) => security.ContractSpec.format(price);
|
||||
_position = new CellProperty<int>(0);
|
||||
_bid_price = backing(nameof(bid_price), double.NaN);
|
||||
_bid_size = backing(nameof(bid_size), (ushort)0);
|
||||
_ask_price = backing(nameof(ask_price), double.NaN);
|
||||
_ask_size = backing(nameof(ask_size), (ushort)0);
|
||||
_bid_size = backing<ushort>(nameof(bid_size), 0);
|
||||
_ask_size = backing<ushort>(nameof(ask_size), 0);
|
||||
_last_price = backing(nameof(last_price), double.NaN);
|
||||
_last_size = backing(nameof(last_size), (ushort)0);
|
||||
_last_size = new CellProperty<ushort>(0);
|
||||
_day_change = backing(nameof(day_change), double.NaN);
|
||||
_session_low = backing(nameof(session_low), double.NaN);
|
||||
_session_high = backing(nameof(session_high), double.NaN);
|
||||
_settlement = backing(nameof(settlement), double.NaN);
|
||||
_volume = backing(nameof(volume), 0ul);
|
||||
_volume = new CellProperty<ulong>(0);
|
||||
_inside_bids = new CellProperty<bool>();
|
||||
_inside_asks = new CellProperty<bool>();
|
||||
_working_bid_volume = new CellProperty<int>();
|
||||
_working_ask_volume = new CellProperty<int>();
|
||||
//
|
||||
_working_orders = new ObservableFilterCollection<OrderRecordImmutable>(_orders, o => o.is_active());
|
||||
_working_bids = new ObservableFilterCollection<OrderRecordImmutable>(_working_orders, o => o.Def.Side == Side.Buy);
|
||||
_working_asks = new ObservableFilterCollection<OrderRecordImmutable>(_working_orders, o => o.Def.Side == Side.Sell);
|
||||
_working_best_bid = backing(nameof(working_best_bid), () => _working_bids.Max(o => o.State.Price, double.NaN));
|
||||
_working_best_ask = backing(nameof(working_best_ask), () => _working_asks.Min(o => o.State.Price, double.NaN));
|
||||
|
||||
_inside_bids = backing(nameof(inside_bids), _bid_price.CombineLatest(_working_best_bid).Select(vals => vals.Item2.newval >= vals.Item1.newval), false);
|
||||
_inside_asks = backing(nameof(inside_asks), _ask_price.CombineLatest(_working_best_ask).Select(vals => vals.Item2.newval <= vals.Item1.newval), false);
|
||||
_working_bid_volume = backing(nameof(working_bid_volume), () => _working_bids.Sum(o => o.working_quantity()));
|
||||
_working_ask_volume = backing(nameof(working_ask_volume), () => _working_asks.Sum(o => o.working_quantity()));
|
||||
_working_best_bid = new CellProperty<double>(format_price, double.NaN);
|
||||
_working_best_ask = new CellProperty<double>(format_price, double.NaN);
|
||||
_working_bids_text = new CellProperty<string>(string.Empty);
|
||||
_working_asks_text = new CellProperty<string>(string.Empty);
|
||||
//
|
||||
_day_change = backing(nameof(day_change), double.NaN);
|
||||
_session_low = backing(nameof(session_low), double.NaN);
|
||||
_session_high = backing(nameof(session_high), double.NaN);
|
||||
_animation.AnimationsChanged += _animation_AnimationsChanged;
|
||||
//_working_bids_filled_animation = Disposable
|
||||
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));
|
||||
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));
|
||||
working_asks_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingAsks));
|
||||
_animation.AnimationsChanged += on_animations_changed;
|
||||
void on_animations_changed() => RaisePropertyChanged(nameof(animations));
|
||||
_dispose = Disposable.Create(
|
||||
_working_bids.Subscribe(_working_bids_CollectionChanged),
|
||||
_working_asks.Subscribe(_working_asks_CollectionChanged),
|
||||
_position.ObserveOnDispatcher().Subscribe(vals => _animation.animate_position(vals.newval > vals.oldval)),
|
||||
_inside_bids.ObserveOnDispatcher().Subscribe(vals => _animation.animate_working_bids_inside(vals.newval)),
|
||||
_inside_asks.ObserveOnDispatcher().Subscribe(vals => _animation.animate_working_asks_inside(vals.newval)),
|
||||
_day_change.ObserveOnDispatcher().Subscribe(vals => _animation.animate_day_change(vals.newval > vals.oldval)),
|
||||
_orders_model.on_security_orders(security.Id).ObserveOnDispatcher().Subscribe(_on_orders),
|
||||
Disposable.Create(() => _animation.AnimationsChanged -= _animation_AnimationsChanged)
|
||||
_position.ObserveOnDispatcher().Subscribe(v => _animation.animate_position(v.newval > v.oldval)),
|
||||
_inside_bids.ObserveOnDispatcher().Subscribe(v => _animation.animate_working_bids_inside(v.newval)),
|
||||
_inside_asks.ObserveOnDispatcher().Subscribe(v => _animation.animate_working_asks_inside(v.newval)),
|
||||
_day_change.ObserveOnDispatcher().Subscribe(v => _animation.animate_day_change(v.newval > v.oldval)),
|
||||
Disposable.Create(() => _animation.AnimationsChanged -= on_animations_changed)
|
||||
);
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
|
||||
#region Bound Properties
|
||||
public bool inside_bids => _inside_bids.Value;
|
||||
public bool inside_asks => _inside_asks.Value;
|
||||
public int position => _position.Value;
|
||||
public bool inside_bids => _inside_bids.value;
|
||||
public bool inside_asks => _inside_asks.value;
|
||||
public int position => _position.value;
|
||||
public double last_price => _last_price.Value;
|
||||
public ushort last_size => _last_size.Value;
|
||||
public ushort last_size => _last_size.value;
|
||||
public double bid_price => _bid_price.Value;
|
||||
public ushort bid_size => _bid_size.Value;
|
||||
public double ask_price => _ask_price.Value;
|
||||
public ushort ask_size => _ask_size.Value;
|
||||
public ulong volume => _volume.Value;
|
||||
public ulong volume => _volume.value;
|
||||
public double settlement => _settlement.Value;
|
||||
public double day_change => _day_change.Value;
|
||||
public double session_low => _session_low.Value;
|
||||
public double session_high => _session_high.Value;
|
||||
public int working_bid_volume => _working_bid_volume.Value;
|
||||
public int working_ask_volume => _working_ask_volume.Value;
|
||||
public string working_bids_text => OrderUtils.order_collection_summary(_working_bids, (x, y) => x.CompareTo(y), security.ContractSpec);
|
||||
public string working_asks_text => OrderUtils.order_collection_summary(_working_asks, (x, y) => y.CompareTo(x), security.ContractSpec);
|
||||
public double working_best_bid => _working_best_bid.Value;
|
||||
public double working_best_ask => _working_best_ask.Value;
|
||||
public IEnumerable<CellAnimation> animations => _animation.get_animations();
|
||||
public ICommand show_ladder => new DelegateCommand(_show_ladder);
|
||||
public ICommand show_order_book => new DelegateCommand<MarketViewView.Columns>(_show_order_book);
|
||||
public ICommand show_order_ticket => new DelegateCommand<MarketViewView.Columns>(_show_order_ticket);
|
||||
public int working_bid_volume => _working_bid_volume.value;
|
||||
public int working_ask_volume => _working_ask_volume.value;
|
||||
public string working_bids_text => OrderUtils.order_collection_summary(_order_summarizer.best_working_bid, _order_summarizer.best_working_bid_qty, _order_summarizer.working_bid_qty, security.ContractSpec);
|
||||
public string working_asks_text => OrderUtils.order_collection_summary(_order_summarizer.best_working_ask, _order_summarizer.best_working_ask_qty, _order_summarizer.working_ask_qty, security.ContractSpec);
|
||||
public double working_best_bid => _working_best_bid.value;
|
||||
public double working_best_ask => _working_best_ask.value;
|
||||
public IList<CellAnimation> animations => _animation.get_animations().ToList();
|
||||
public ICommand bid_price_click { get; }
|
||||
public ICommand bid_size_click { get; }
|
||||
public ICommand working_bids_click { get; }
|
||||
public ICommand ask_price_click { get; }
|
||||
public ICommand ask_size_click { get; }
|
||||
public ICommand working_asks_click { get; }
|
||||
public ICommand default_command { get; }
|
||||
#endregion
|
||||
|
||||
public void refresh() {
|
||||
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);
|
||||
_ask_price.set(a.price);
|
||||
_ask_size.set(a.size);
|
||||
_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);
|
||||
}
|
||||
if (md.md.get_last_trade(ref _last_ref, out NativePriceSize ps, out DateTime ts, out ulong v)) {
|
||||
_last_price.set(ps.price);
|
||||
_last_size.set(ps.size);
|
||||
_volume.set(v);
|
||||
_last_size.on_next_distinct(ps.size, ref dirty);
|
||||
_volume.on_next_distinct(v, ref dirty);
|
||||
}
|
||||
_settlement.set(md.md.settlement_price);
|
||||
_session_low.set(md.md.session_low);
|
||||
_session_high.set(md.md.session_high);
|
||||
_day_change.set(last_price - session_high);
|
||||
_day_change.set(last_price - settlement);
|
||||
if (dirty)
|
||||
invalidated?.Invoke();
|
||||
}
|
||||
|
||||
public bool Equals(Row other) =>
|
||||
@@ -130,7 +157,7 @@ namespace mbts.spark {
|
||||
void _show_ladder() => _bus.post(SparkTags.ShowLadderTag, md);
|
||||
|
||||
void _show_order_book(MarketViewView.Columns col) {
|
||||
switch(col) {
|
||||
switch (col) {
|
||||
case MarketViewView.Columns.WorkingBids: _show_order_book(Side.Buy); break;
|
||||
case MarketViewView.Columns.WorkingAsks: _show_order_book(Side.Sell); break;
|
||||
}
|
||||
@@ -175,39 +202,106 @@ namespace mbts.spark {
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
void _on_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
|
||||
uint removed_buys = 0, removed_sells = 0, added_buys = 0, added_sells = 0;
|
||||
foreach (var o in orders.removed) {
|
||||
_orders.Remove(o);
|
||||
switch (o.Def.Side) {
|
||||
case Side.Buy: removed_buys += o.State.FilledQuantity; break;
|
||||
case Side.Sell: removed_sells += o.State.FilledQuantity; break;
|
||||
}
|
||||
}
|
||||
foreach (var o in orders.added) {
|
||||
_orders.Add(o);
|
||||
switch (o.Def.Side) {
|
||||
case Side.Buy: added_buys += o.State.FilledQuantity; break;
|
||||
case Side.Sell: added_sells += o.State.FilledQuantity; break;
|
||||
}
|
||||
}
|
||||
_position.Value += (int)(removed_sells + added_buys - removed_buys - added_sells);
|
||||
void _on_orders(ITableUpdateTransform<OrderRecordImmutable, uint> orders) {
|
||||
foreach (var o in orders.removed)
|
||||
_order_summarizer.remove_order(o);
|
||||
foreach (var o in orders.added)
|
||||
_order_summarizer.add_order(o);
|
||||
bool dirty = false;
|
||||
_refresh_orders(ref dirty);
|
||||
if (dirty)
|
||||
invalidated?.Invoke();
|
||||
}
|
||||
|
||||
void _working_bids_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
|
||||
_working_best_bid.refresh();
|
||||
_working_bid_volume.refresh();
|
||||
RaisePropertyChanged(nameof(working_bids_text));
|
||||
void _refresh_orders(ref bool dirty) {
|
||||
_position.on_next_distinct(_order_summarizer.position, ref dirty);
|
||||
_working_bid_volume.on_next_distinct(_order_summarizer.working_bid_qty, ref dirty);
|
||||
_working_ask_volume.on_next_distinct(_order_summarizer.working_ask_qty, ref dirty);
|
||||
_working_best_bid.on_next_distinct(_order_summarizer.best_working_bid, ref dirty);
|
||||
_working_best_ask.on_next_distinct(_order_summarizer.best_working_ask, ref dirty);
|
||||
_inside_bids.on_next_distinct(_order_summarizer.best_working_bid >= bid_price, ref dirty);
|
||||
_inside_asks.on_next_distinct(_order_summarizer.best_working_ask <= ask_price, ref dirty);
|
||||
RaisePropertyChanged(nameof(working_bids_text), nameof(working_asks_text));
|
||||
}
|
||||
|
||||
void _working_asks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
|
||||
_working_best_ask.refresh();
|
||||
_working_ask_volume.refresh();
|
||||
RaisePropertyChanged(nameof(working_asks_text));
|
||||
}
|
||||
|
||||
void _animation_AnimationsChanged() => RaisePropertyChanged(nameof(animations));
|
||||
#endregion
|
||||
}
|
||||
|
||||
class MyOrderSummarizer {
|
||||
SortedSetMap<double, OrderRecordImmutable> _bids, _asks;
|
||||
int _best_bid_working_qty, _best_ask_working_qty, _position, _working_bid_qty, _working_ask_qty;
|
||||
|
||||
public MyOrderSummarizer() {
|
||||
_bids = new SortedSetMap<double, OrderRecordImmutable>(
|
||||
OrderDataHelper.CompareUtils.bid_price_comparer,
|
||||
OrderDataHelper.CompareUtils.order_record_id_eq_comparer);
|
||||
_asks = new SortedSetMap<double, OrderRecordImmutable>(
|
||||
OrderDataHelper.CompareUtils.ask_price_comparer,
|
||||
OrderDataHelper.CompareUtils.order_record_id_eq_comparer);
|
||||
}
|
||||
|
||||
// bid
|
||||
public double best_working_bid => _bids.Count > 0 ? _bids.at_index(0).Key : double.NaN;
|
||||
public int best_working_bid_qty => _best_bid_working_qty;
|
||||
public int working_bid_qty => _working_bid_qty;
|
||||
// ask
|
||||
public double best_working_ask => _asks.Count > 0 ? _asks.at_index(0).Key : double.NaN;
|
||||
public int best_working_ask_qty => _best_ask_working_qty;
|
||||
public int working_ask_qty => _working_ask_qty;
|
||||
// pos
|
||||
public int position => _position;
|
||||
|
||||
public void add_order(OrderRecordImmutable o) {
|
||||
// This is a market data form - not an orders form - so just show data for root/non-child orders.
|
||||
if (o.has_parent())
|
||||
return;
|
||||
switch (o.side()) {
|
||||
case Side.Buy:
|
||||
_position += (int)o.filled();
|
||||
if (o.is_active()) {
|
||||
_working_bid_qty += o.working_quantity();
|
||||
_bids.add(o.price(), o);
|
||||
}
|
||||
break;
|
||||
case Side.Sell:
|
||||
_position -= (int)o.filled();
|
||||
if (o.is_active()) {
|
||||
_working_ask_qty += o.working_quantity();
|
||||
_asks.add(o.price(), o);
|
||||
}
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
_refresh();
|
||||
}
|
||||
|
||||
public void remove_order(OrderRecordImmutable o) {
|
||||
// This is a market data form - not an orders form - so just show data for root/non-child orders.
|
||||
if (o.has_parent())
|
||||
return;
|
||||
switch (o.side()) {
|
||||
case Side.Buy:
|
||||
_position -= (int)o.filled();
|
||||
if (o.is_active()) {
|
||||
_working_bid_qty -= o.working_quantity();
|
||||
_bids.remove(o.price(), o);
|
||||
}
|
||||
break;
|
||||
case Side.Sell:
|
||||
_position += (int)o.filled();
|
||||
if (o.is_active()) {
|
||||
_working_ask_qty -= o.working_quantity();
|
||||
_asks.remove(o.price(), o);
|
||||
}
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
_refresh();
|
||||
}
|
||||
|
||||
void _refresh() {
|
||||
_best_bid_working_qty = _bids.Count > 0 ? _bids.at_index(0).Sum(o => o.working_quantity()) : 0;
|
||||
_best_ask_working_qty = _asks.Count > 0 ? _asks.at_index(0).Sum(o => o.working_quantity()) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,118 @@
|
||||
using bts.api;
|
||||
using bts.api;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using System.Reactive.Linq;
|
||||
using static bts.shell.MarketViewWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.MarketViewView;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class MarketViewVM : SecurityMDGrid<MarketViewVM.Row, SecurityMD> {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
|
||||
public MarketViewVM(IBus bus, OrdersModel orders_model, SecurityCriteria security_criteria, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
|
||||
: base(RXCollection.Create(security_criteria.subscribe_md(bus), SecurityDataHelper.EqualityComparer.MDComparer), sorting_criteria) {
|
||||
public MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state)
|
||||
: base(RXCollection.Create(state.SelectMany(s => s.security_criteria.subscribe_md(bus)), SecurityDataHelper.EqualityComparer.MDComparer), null) {
|
||||
_bus = bus;
|
||||
_orders_model = orders_model;
|
||||
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(null), nameof(Row.security));
|
||||
if (filters != null) {
|
||||
security_filter.load_state(filters.security);
|
||||
bid_price_filter.load_state(filters.bid_price);
|
||||
bid_size_filter.load_state(filters.bid_size);
|
||||
ask_price_filter.load_state(filters.ask_price);
|
||||
ask_size_filter.load_state(filters.ask_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);
|
||||
settlement_filter.load_state(filters.settlement);
|
||||
session_low_filter.load_state(filters.session_low);
|
||||
session_high_filter.load_state(filters.session_high);
|
||||
position_filter.load_state(filters.position);
|
||||
working_bid_volume_filter.load_state(filters.working_bid_volume);
|
||||
working_ask_volume_filter.load_state(filters.working_ask_volume);
|
||||
volume_filter.load_state(filters.volume);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
|
||||
// Columns
|
||||
const string size_col_header = "Qty", 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.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_brush);
|
||||
add_hl_size_col(Columns.BidSize, size_col_header, nameof(Row.bid_size), nameof(Row.bid_size_click), bid_size_filter, BTSColors.MarketData.bid_brush);
|
||||
add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text_brush, 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_brush, 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_brush);
|
||||
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_brush);
|
||||
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.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) =>
|
||||
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
|
||||
Header = header,
|
||||
sort_member_path = prop,
|
||||
Width = width,
|
||||
padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding
|
||||
});
|
||||
|
||||
void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Brush foreground, Brush background) =>
|
||||
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price, foreground, background, SelectionFill);
|
||||
|
||||
void add_hl_size_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Brush foreground) =>
|
||||
add_hl_col(col_key, header, new Binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, 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, Brush 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_col(
|
||||
Columns col_key, string header, BindingBase binding, string prop, string command_prop,
|
||||
IDataGridColumnFilter filter, string filter_template, double width, Brush foreground, Brush background, Brush background_overlay) =>
|
||||
columns.Add(new HyperlinkElementColumn<Row, Columns>(
|
||||
col_key, binding, new Binding(command_prop), filter, filter_template) {
|
||||
Header = header,
|
||||
sort_member_path = prop,
|
||||
foreground = foreground,
|
||||
background = background,
|
||||
background_overlay = background_overlay,
|
||||
Width = DefaultColumnWidths.Price
|
||||
});
|
||||
|
||||
BindingBase price_binding(string prop) {
|
||||
var mb = new MultiBinding { Converter = price_converter };
|
||||
mb.Bindings.Add(new Binding(prop) { Mode = BindingMode.OneWay });
|
||||
mb.Bindings.Add(new Binding($"{nameof(Row.security)}.{nameof(SecurityImmutable.ContractSpec)}") { Mode = BindingMode.OneWay });
|
||||
return mb;
|
||||
}
|
||||
_resources.AddRange(DataGridFilterHelper.hook_filters(rows_view,
|
||||
//
|
||||
_resources.AddRange(
|
||||
state.ObserveOnDispatcher().Subscribe(s => {
|
||||
using (rows_view.DeferRefresh()) {
|
||||
rows_view.SetSortingCriteria(s.sorting_criteria);
|
||||
var filters = s.filters;
|
||||
if (filters != null) {
|
||||
security_filter.load_state(filters.security);
|
||||
bid_price_filter.load_state(filters.bid_price);
|
||||
bid_size_filter.load_state(filters.bid_size);
|
||||
ask_price_filter.load_state(filters.ask_price);
|
||||
ask_size_filter.load_state(filters.ask_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);
|
||||
settlement_filter.load_state(filters.settlement);
|
||||
session_low_filter.load_state(filters.session_low);
|
||||
session_high_filter.load_state(filters.session_high);
|
||||
position_filter.load_state(filters.position);
|
||||
working_bid_volume_filter.load_state(filters.working_bid_volume);
|
||||
working_ask_volume_filter.load_state(filters.working_ask_volume);
|
||||
volume_filter.load_state(filters.volume);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
}
|
||||
}), 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,
|
||||
session_low_filter, session_high_filter, position_filter, working_bid_volume_filter, working_ask_volume_filter, volume_filter, security_key_filter),
|
||||
WorkLoop.Start(_refresh)
|
||||
WorkLoop.Start("MarketView", _refresh)
|
||||
);
|
||||
}
|
||||
|
||||
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
|
||||
|
||||
#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));
|
||||
@@ -86,6 +159,6 @@ namespace mbts.spark {
|
||||
|
||||
void _refresh() {
|
||||
foreach (var r in _row_vms) r.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,306 +1,43 @@
|
||||
<UserControl x:Class="mbts.ui.spark.MarketViewView" x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:uc="clr-namespace:mbts.ui"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark">
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.MarketViewView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<Style x:Key="OrderTicketCell" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
|
||||
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_order_ticket}"/>
|
||||
<Setter Property="u:AttachedCommands.DoubleClickParameter" Value="{Binding Path='Column.(c:DataGridEx.CellKey)', RelativeSource={RelativeSource Self}}"/>
|
||||
</Style>
|
||||
<Style x:Key="OrderTicketStaticBackgroundCell" TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellStaticBackgroundStyle}">
|
||||
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_order_ticket}"/>
|
||||
<Setter Property="u:AttachedCommands.DoubleClickParameter" Value="{Binding Path='Column.(c:DataGridEx.CellKey)', RelativeSource={RelativeSource Self}}"/>
|
||||
</Style>
|
||||
<Style x:Key="PositionCell" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
|
||||
<Setter Property="Foreground" Value="{Binding position, Converter={StaticResource PositionSignBrushConverter}}"/>
|
||||
<Setter Property="Padding" Value="8,0,4,0"/>
|
||||
</Style>
|
||||
<Style x:Key="BidPriceCell" TargetType="DataGridCell" BasedOn="{StaticResource OrderTicketStaticBackgroundCell}">
|
||||
<Setter Property="c:DataGridEx.CellStaticBackground" Value="{uc:BTSColor MarketData.bid}"/>
|
||||
<Setter Property="Background" Value="{uc:BTSColor MarketData.bid}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Style>
|
||||
<Style x:Key="AskPriceCell" TargetType="DataGridCell" BasedOn="{StaticResource OrderTicketStaticBackgroundCell}">
|
||||
<Setter Property="c:DataGridEx.CellStaticBackground" Value="{uc:BTSColor MarketData.ask}"/>
|
||||
<Setter Property="Background" Value="{uc:BTSColor MarketData.ask}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Style>
|
||||
<Style x:Key="DayChangeCell" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
|
||||
<Setter Property="Foreground" Value="{Binding day_change, Converter={StaticResource DayChangeSignBrushConverter}}"/>
|
||||
<Setter Property="Padding" Value="8,0,4,0"/>
|
||||
</Style>
|
||||
<Style x:Key="LastPriceElement" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{uc:BTSColor MarketData.last}"/>
|
||||
</Style>
|
||||
<Style x:Key="LastSizeElement" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{uc:BTSColor MarketData.last}"/>
|
||||
</Style>
|
||||
<Style x:Key="HyperlinkElementBase" TargetType="Hyperlink">
|
||||
<Setter Property="CommandParameter" Value="{Binding Path='Column.(c:DataGridEx.CellKey)', RelativeSource={RelativeSource AncestorType=DataGridCell}}"/>
|
||||
</Style>
|
||||
<Style x:Key="HyperlinkBidFG" TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkElementBase}">
|
||||
<Setter Property="Foreground" Value="Transparent"/>
|
||||
<Setter Property="TextDecorations" Value="Underline"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Foreground" Value="{u:BTSColor MarketData.bid}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="True">
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style x:Key="HyperlinkAskFG" TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkElementBase}">
|
||||
<Setter Property="Foreground" Value="Transparent"/>
|
||||
<Setter Property="TextDecorations" Value="Underline"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Foreground" Value="{u:BTSColor MarketData.ask}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="True">
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style x:Key="HyperlinkWhiteFG" TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkElementBase}">
|
||||
<Setter Property="Foreground" Value="Transparent"/>
|
||||
<Setter Property="TextDecorations" Value="Underline"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="True">
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<c:DataGridEx x:Name="grid" ItemsSource="{Binding rows_view}" IsReadOnly="True" CanUserSortColumns="True" GridLinesVisibility="None"
|
||||
CurrentItem="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
u:DataGridBehavior.NotifyColumnsOfSorting="True" usp:GridAnimation.Enableanimation="True" BorderThickness="0">
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="usp:GridAnimation.RowAnimation" Value="{Binding animations}"/>
|
||||
<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="Security" Binding="{Binding security}"
|
||||
c:DataGridEx.CellKey="{u:Enum Security, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.security_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
|
||||
<DataGridTextColumn Header="Volume" Binding="{Binding volume}"
|
||||
c:DataGridEx.CellKey="{u:Enum Volume, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.volume_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.volume_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.volume_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource ULongMinMaxTemplate}"/>
|
||||
<DataGridTextColumn Header="Last" ElementStyle="{StaticResource LastPriceElement}" SortMemberPath="last_price"
|
||||
c:DataGridEx.CellKey="{u:Enum Last, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.last_price_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.last_price_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.last_price_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="last_price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Qty" ElementStyle="{StaticResource LastSizeElement}" Binding="{Binding last_size}"
|
||||
c:DataGridEx.CellKey="{u:Enum LastSize, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.last_size_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.last_size_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.last_size_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource UShortMinMaxTemplate}"/>
|
||||
<DataGridTextColumn Header="Pos" CellStyle="{StaticResource PositionCell}" Binding="{Binding position}"
|
||||
c:DataGridEx.CellKey="{u:Enum Position, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.position_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.position_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.position_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"/>
|
||||
<DataGridTemplateColumn Header="Working" SortMemberPath="working_best_bid"
|
||||
c:DataGridEx.CellKey="{u:Enum WorkingBids, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.working_bid_volume_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.working_bid_volume_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.working_bid_volume_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource UIntMinMaxTemplate}">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<Hyperlink Command="{Binding show_order_book}" Style="{StaticResource HyperlinkBidFG}">
|
||||
<TextBlock Text="{Binding working_bids_text}" Foreground="{u:BTSColor MarketData.bid}"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Header="Qty"
|
||||
c:DataGridEx.CellKey="{u:Enum BidSize, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.bid_size_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.bid_size_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.bid_size_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource UShortMinMaxTemplate}">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<Hyperlink Command="{Binding show_order_ticket}" Style="{StaticResource HyperlinkBidFG}">
|
||||
<TextBlock Text="{Binding bid_size}" Foreground="{u:BTSColor MarketData.bid}"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Header="Bid" CellStyle="{StaticResource BidPriceCell}" SortMemberPath="bid_price"
|
||||
c:DataGridEx.CellKey="{u:Enum Bid, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.bid_price_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.bid_price_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.bid_price_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<Hyperlink Command="{Binding show_order_ticket}" Style="{StaticResource HyperlinkWhiteFG}">
|
||||
<TextBlock Foreground="White">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="bid_price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Header="Ask" CellStyle="{StaticResource AskPriceCell}" SortMemberPath="ask_price"
|
||||
c:DataGridEx.CellKey="{u:Enum Ask, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.ask_price_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.ask_price_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.ask_price_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<Hyperlink Command="{Binding show_order_ticket}" Style="{StaticResource HyperlinkWhiteFG}">
|
||||
<TextBlock Foreground="White">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="ask_price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Header="Qty"
|
||||
c:DataGridEx.CellKey="{u:Enum AskSize, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.ask_size_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.ask_size_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.ask_size_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource UShortMinMaxTemplate}">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<Hyperlink Command="{Binding show_order_ticket}" Style="{StaticResource HyperlinkAskFG}">
|
||||
<TextBlock Text="{Binding ask_size}" Foreground="{u:BTSColor MarketData.ask}"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTemplateColumn Header="Working" SortMemberPath="working_best_ask"
|
||||
c:DataGridEx.CellKey="{u:Enum WorkingAsks, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.working_ask_volume_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.working_ask_volume_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.working_ask_volume_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource UIntMinMaxTemplate}">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
<Hyperlink Command="{Binding show_order_book}" Style="{StaticResource HyperlinkAskFG}">
|
||||
<TextBlock Text="{Binding working_asks_text}" Foreground="{u:BTSColor MarketData.ask}"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Header="Settlement" SortMemberPath="settlement"
|
||||
c:DataGridEx.CellKey="{u:Enum Settlement, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.settlement_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.settlement_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.settlement_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="settlement"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Change" CellStyle="{StaticResource DayChangeCell}" SortMemberPath="day_change"
|
||||
c:DataGridEx.CellKey="{u:Enum DayChange, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.day_change_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.day_change_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.day_change_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="day_change"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Low" SortMemberPath="session_low"
|
||||
c:DataGridEx.CellKey="{u:Enum SessionLow, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.session_low_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.session_low_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.session_low_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="session_low"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="High" SortMemberPath="session_high"
|
||||
c:DataGridEx.CellKey="{u:Enum SessionHigh, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.session_high_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.session_high_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.session_high_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="session_high"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Security Key" Width="*" Binding="{Binding security_key}"
|
||||
c:DataGridEx.CellKey="{u:Enum SecurityKey, Type={u:Nested usp:MarketViewView.Columns}}"
|
||||
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>
|
||||
<AdornerDecorator>
|
||||
<c:DIYGrid
|
||||
x:Name="_grid"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row}"
|
||||
Rows="{Binding rows_view}"
|
||||
>
|
||||
<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:MarketViewRowView usp:GridAnimation.RowAnimation="{Binding animations}"/>
|
||||
</DataTemplate>
|
||||
</c:DIYGrid.RowTemplate>
|
||||
</c:DIYGrid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -1,4 +1,6 @@
|
||||
using mbts.spark.filters;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
using System.Linq;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
@@ -10,7 +12,12 @@ namespace mbts.ui.spark {
|
||||
|
||||
public MarketViewView() {
|
||||
InitializeComponent();
|
||||
DataGridFilterHelper.set_filter_key_event_hooks(this);
|
||||
AdornerHelper.attach_continuously_on_loaded(
|
||||
() => new GridAnimation.DIYGridAnimationAdorner<Columns>(
|
||||
_grid, _grid.descendants_of_type<DIYGridRowsPresenter>().First()),
|
||||
_grid);
|
||||
}
|
||||
}
|
||||
|
||||
class MarketViewRowView : DIYGrid.HighPerformanceRow<MarketViewVM.Row, MarketViewView.Columns> { }
|
||||
}
|
||||
@@ -9,79 +9,62 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>
|
||||
{
|
||||
readonly MarketViewWindowFactory _factory;
|
||||
namespace mbts.spark {
|
||||
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>, ISharedWindow {
|
||||
const string _window_type = "MarketView", _thread_id = "MarketView";
|
||||
readonly OrdersModel _orders_model;
|
||||
|
||||
SingleValuePump<MarketViewVM> _vm = new SingleValuePump<MarketViewVM>();
|
||||
MarketViewWindowFactory.StartupData _state;
|
||||
readonly AllowOnce _init_guard = new AllowOnce("MarketView");
|
||||
MarketViewVM _vm;
|
||||
MarketViewView _view;
|
||||
PartialCommand<SecurityCriteria> _criteria_callback_command;
|
||||
MarketViewWindowFactory.StartupData _state;
|
||||
IReadOnlyList<SparkCommand> _commands;
|
||||
IDisposable _subscriptions;
|
||||
|
||||
MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, ImageSource icon) : base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
|
||||
_factory = factory;
|
||||
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state)
|
||||
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
|
||||
_orders_model = orders_model;
|
||||
ITag<SecurityCriteria> _updated_security_criteria_tag = new SparkTags.SparkTag<SecurityCriteria>("MarketView Callback");
|
||||
_criteria_callback_command = SparkCommand.create("Update Market View", SparkCommands.ShowMarketView.icon, _updated_security_criteria_tag);
|
||||
Commands = new SparkCommand[] {
|
||||
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (_criteria_callback_command, _state.security_criteria)),
|
||||
SparkCommands.ShowLadder.complete(_vm.observable.SelectMany(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault),
|
||||
// TODO: ShowLinkedLadder
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.observable.SelectMany(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault),
|
||||
// TODO: ShowLinkedOrderTicket
|
||||
SparkCommands.ShowFills.complete(_vm.observable.SelectMany(vm => vm.selected_securities_observable()), _ => true,
|
||||
_state = state;
|
||||
Icon.Freeze();
|
||||
}
|
||||
|
||||
event Action<MarketViewWindowFactory.StartupData> _state_changed;
|
||||
|
||||
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 string thread_id => _thread_id;
|
||||
|
||||
public void thread_init() {
|
||||
_init_guard.check_access();
|
||||
Logger.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);
|
||||
_view = new MarketViewView { DataContext = _vm };
|
||||
var update_securities_tag = new SparkTags.SparkTag<SecurityCriteria>("MarketView Callback");
|
||||
var update_securities_cmd = SparkCommand.create("Update Market View", Icon, update_securities_tag);
|
||||
_commands = new List<SparkCommand> {
|
||||
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (update_securities_cmd, _state.security_criteria)),
|
||||
SparkCommands.ShowLadders.complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
|
||||
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ObjectHelper.ValueOrDefault),
|
||||
SparkCommands.ShowFills.complete(_vm.selected_securities_observable(), _ => true,
|
||||
secs => new FillsWindowFactory.StartupData(new FillsWindowFactory.FilterStates {
|
||||
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
|
||||
})
|
||||
),
|
||||
SparkCommands.ShowTimeAndSales.complete(_vm.observable.SelectMany(vm => vm.selected_securities_observable()), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
|
||||
// SpreadMatrix ??
|
||||
// SyntheticCreate
|
||||
SparkCommands.ShowSyntheticCreate.complete(_vm.observable.SelectMany(vm => vm.selected_securities_observable()), secs => secs.Count > 0, secs => new SecurityCriteria(secs))
|
||||
// ModifyOrder - await orders
|
||||
// CancelOrdersRoot ?
|
||||
//SparkCommands.CancelBuyOrders,
|
||||
//SparkCommands.CancelSellOrders,
|
||||
//SparkCommands.CancelOrders,
|
||||
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 < 3, secs => new SecurityCriteria(secs))
|
||||
};
|
||||
_subscriptions = _bus.subscribe(_updated_security_criteria_tag).ObserveOnDispatcher().Subscribe(update_criteria);
|
||||
_subscriptions = _bus.subscribe(update_securities_tag).ObserveOnDispatcher().Subscribe(c =>
|
||||
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states())));
|
||||
}
|
||||
|
||||
public static MarketViewWindow create(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, ImageSource icon) {
|
||||
var window = new MarketViewWindow(factory, bus, orders_model, icon);
|
||||
window._initialize(state);
|
||||
return window;
|
||||
}
|
||||
|
||||
public void update_criteria(SecurityCriteria c) =>
|
||||
_initialize(new MarketViewWindowFactory.StartupData(c, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states()));
|
||||
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands { get; }
|
||||
public override MarketViewWindowFactory.StartupData SaveState => _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
|
||||
public override double MinWidth => 1035 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
|
||||
|
||||
public override void Dispose() {
|
||||
Disposable.dispose(ref _subscriptions);
|
||||
Disposable.dispose(ref _vm);
|
||||
foreach (var c in Commands.OfType<IDisposable>()) c.Dispose();
|
||||
}
|
||||
|
||||
void _initialize(MarketViewWindowFactory.StartupData state) {
|
||||
Logger.notnull(state);
|
||||
_state = state;
|
||||
_vm.value = new MarketViewVM(_bus, _orders_model, state.security_criteria, state.sorting_criteria, state.filters);
|
||||
if (_view == null) {
|
||||
_view = new MarketViewView { DataContext = _vm.value };
|
||||
} else {
|
||||
_view.DataContext = _vm.value;
|
||||
}
|
||||
_vm.Dispose();
|
||||
Commands.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.spark;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
@@ -17,17 +16,16 @@ namespace bts.shell {
|
||||
public MarketViewWindowFactory(MarketShellModule module, IBus bus, OrdersModel orders_model) : base(module, "MarketView") {
|
||||
_bus = bus;
|
||||
_orders_model = orders_model;
|
||||
_dispose = bus.subscribe(SparkTags.ShowMarketViewTag).ObserveOnDispatcher().Subscribe(show);
|
||||
_dispose = bus.subscribe(SparkTags.ShowMarketViewTag).ObserveOnDispatcher().Subscribe(_show);
|
||||
}
|
||||
|
||||
public override Task<IShellWindow> load(StartupData state) =>
|
||||
Task.FromResult(_create(state));
|
||||
|
||||
void show(SecurityCriteria criteria) =>
|
||||
_bus.post(SparkTags.ShowWindow, _create(new StartupData(criteria)));
|
||||
void _show(SecurityCriteria criteria) =>
|
||||
_bus.post(SparkTags.ShowWindow, _create(new StartupData(criteria, new List<SortingCriteria> { new SortingCriteria(nameof(MarketViewVM.Row.security), true) }, null)));
|
||||
|
||||
IShellWindow _create(StartupData state) =>
|
||||
MarketViewWindow.create(this, _bus, _orders_model, state, SparkCommands.PickMarketView.icon);
|
||||
IShellWindow _create(StartupData state) => new MarketViewWindow(this, _bus, _orders_model, state);
|
||||
|
||||
[ProtoContract]
|
||||
public class StartupData {
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
using bts.co2;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using static bts.shell.TimeAndSalesWindowFactory;
|
||||
@@ -17,7 +20,7 @@ namespace mbts.spark {
|
||||
: base(RXCollection.CreateBuffer(model.trades, 1000), sorting_criteria) {
|
||||
_bus = bus;
|
||||
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(null), nameof(Row.security));
|
||||
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
|
||||
if (filters != null) {
|
||||
time_filter.load_state(filters.time);
|
||||
security_filter.load_state(filters.security);
|
||||
@@ -26,6 +29,16 @@ namespace mbts.spark {
|
||||
trade_key_filter.load_state(filters.trade_key);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
// Columns
|
||||
add_column(TimeAndSalesView.Columns.Security, "Security", DIYGrid.DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.Time, "Time", DIYGrid.DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter);
|
||||
add_column(TimeAndSalesView.Columns.Price, "Price", DIYGrid.DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
|
||||
add_column(TimeAndSalesView.Columns.Size, "Size", DIYGrid.DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax);
|
||||
add_column(TimeAndSalesView.Columns.TradeKey, "Trade Key", DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.SecurityKey, "Security Key", DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template) =>
|
||||
columns.Add(new DIYGrid.RenderTextColumn<Row>(r => r[col_key], filter, filter_template) { Header = header, Width = width });
|
||||
//
|
||||
_resources.AddRange(
|
||||
show_ladder,
|
||||
DataGridFilterHelper.hook_filters(
|
||||
@@ -34,6 +47,7 @@ namespace mbts.spark {
|
||||
);
|
||||
}
|
||||
|
||||
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
|
||||
public DelegateCommandParamObserver<Row> show_ladder { get; }
|
||||
|
||||
#region Filters
|
||||
@@ -62,27 +76,61 @@ namespace mbts.spark {
|
||||
protected override Row row_factory(TradeInfo row_model) =>
|
||||
new Row(this, row_model.security, row_model.trade);
|
||||
|
||||
|
||||
async Task _show_ladder_async(Row row) {
|
||||
var smd_lookup = await SparkUtils.get_security_md_async(_bus, row.security.Id);
|
||||
if (smd_lookup.success) _bus.post(SparkTags.ShowLadderTag, smd_lookup.result);
|
||||
}
|
||||
|
||||
public class Row : SecurityRow {
|
||||
public class Row : SecurityRow, DIYGrid.IRowVM {
|
||||
readonly uint _trade_key;
|
||||
|
||||
readonly string
|
||||
_security_text,
|
||||
_time_text,
|
||||
_price_text,
|
||||
_size_text,
|
||||
_trade_key_text,
|
||||
_security_key_text;
|
||||
|
||||
public Row(TimeAndSalesVM parent, SecurityImmutable security, TradeMessageImmutable trade) : base(security) {
|
||||
_trade_key = trade.Key;
|
||||
time = trade.Time.lldatetime();
|
||||
price = trade.Mantissa * Math.Pow(10, trade.Exponent);
|
||||
size = trade.Size;
|
||||
show_ladder = new DelegateCommand(() => parent._show_ladder_async(this).ignore());
|
||||
_security_text = security.ToString();
|
||||
_time_text = time.ToString("M/d h:mm:ss tt");
|
||||
_price_text = security.ContractSpec.format(price);
|
||||
_size_text = size.ToString();
|
||||
_trade_key_text = _trade_key.ToString();
|
||||
_security_key_text = security_key.ToString();
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
|
||||
public uint trade_key => _trade_key;
|
||||
public DateTime time { get; }
|
||||
public double price { get; }
|
||||
public uint size { get; }
|
||||
public ICommand show_ladder { get; }
|
||||
|
||||
public string this[TimeAndSalesView.Columns col] {
|
||||
get {
|
||||
switch (col) {
|
||||
case TimeAndSalesView.Columns.Security: return _security_text;
|
||||
case TimeAndSalesView.Columns.Time: return _time_text;
|
||||
case TimeAndSalesView.Columns.Price: return _price_text;
|
||||
case TimeAndSalesView.Columns.Size: return _size_text;
|
||||
case TimeAndSalesView.Columns.TradeKey: return _trade_key_text;
|
||||
case TimeAndSalesView.Columns.SecurityKey: return _security_key_text;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _invalidate() =>
|
||||
invalidated?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
<UserControl x:Class="mbts.ui.spark.TimeAndSalesView" 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"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark">
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.TimeAndSalesView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
@@ -12,63 +16,26 @@
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<c:DataGridEx x:Name="grid" ItemsSource="{Binding rows_view}" IsReadOnly="True" CanUserSortColumns="True" GridLinesVisibility="None"
|
||||
CurrentItem="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
u:DataGridBehavior.NotifyColumnsOfSorting="True" BorderThickness="0">
|
||||
<DataGrid.InputBindings>
|
||||
<KeyBinding Key="Return" Command="{Binding DataContext.show_ladder, Source={u:DirectReference grid}}"/>
|
||||
</DataGrid.InputBindings>
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_ladder}"/>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Security" Binding="{Binding security}"
|
||||
c:DataGridEx.CellKey="{u:Enum Security, Type={u:Nested usp:TimeAndSalesView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.security_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
|
||||
<DataGridTextColumn Header="Time" Binding="{Binding time, StringFormat='M/d h:mm:ss tt'}"
|
||||
c:DataGridEx.CellKey="{u:Enum Time, Type={u:Nested usp:TimeAndSalesView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.time_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.time_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.time_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource TimeFilterTemplate}"/>
|
||||
<DataGridTextColumn Header="Price" SortMemberPath="price"
|
||||
c:DataGridEx.CellKey="{u:Enum Price, Type={u:Nested usp:TimeAndSalesView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.price_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.price_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.price_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Qty" Binding="{Binding size}"
|
||||
c:DataGridEx.CellKey="{u:Enum Size, Type={u:Nested usp:TimeAndSalesView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.size_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.size_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.size_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource UIntMinMaxTemplate}"/>
|
||||
<DataGridTextColumn Header="Trade Key" Binding="{Binding trade_key}"
|
||||
c:DataGridEx.CellKey="{u:Enum TradeKey, Type={u:Nested usp:TimeAndSalesView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.trade_key_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.trade_key_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.trade_key_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
|
||||
<DataGridTextColumn Header="Security Key" Width="*" Binding="{Binding security_key}"
|
||||
c:DataGridEx.CellKey="{u:Enum SecurityKey, Type={u:Nested usp:TimeAndSalesView.Columns}}"
|
||||
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>
|
||||
<AdornerDecorator>
|
||||
<Grid>
|
||||
<c:DIYGrid Columns="{Binding columns}" Rows="{Binding rows_view}" CurrentRow="{Binding current_row}">
|
||||
<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:TimeAndSalesRowView/>
|
||||
</DataTemplate>
|
||||
</c:DIYGrid.RowTemplate>
|
||||
</c:DIYGrid>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -1,4 +1,5 @@
|
||||
using mbts.spark.filters;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
@@ -8,9 +9,9 @@ namespace mbts.ui.spark {
|
||||
partial class TimeAndSalesView : UserControl {
|
||||
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey }
|
||||
|
||||
public TimeAndSalesView() {
|
||||
public TimeAndSalesView() =>
|
||||
InitializeComponent();
|
||||
DataGridFilterHelper.set_filter_key_event_hooks(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TimeAndSalesRowView : DIYGrid.HighPerformanceRow<TimeAndSalesVM.Row, TimeAndSalesView.Columns> { }
|
||||
}
|
||||
@@ -15,6 +15,7 @@ using static bts.shell.TimeAndSalesWindowFactory;
|
||||
|
||||
namespace mbts.spark {
|
||||
class TimeAndSalesWindow : SparkWindow<StartupData>, ISlowWindow {
|
||||
const string _window_type = "TimeAndSales";
|
||||
readonly MDConsumer _consumer;
|
||||
readonly TimeAndSalesWindowFactory _factory;
|
||||
SingleValuePump<TimeAndSalesVM> _vm;
|
||||
@@ -29,8 +30,10 @@ namespace mbts.spark {
|
||||
_consumer = consumer;
|
||||
_factory = factory;
|
||||
_state = state;
|
||||
Icon.Freeze();
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override UIElement Content => _view;
|
||||
public override double MinWidth => 444 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
|
||||
@@ -42,18 +45,18 @@ namespace mbts.spark {
|
||||
public void thread_init() {
|
||||
_dispose_resources();
|
||||
_vm = new SingleValuePump<TimeAndSalesVM>();
|
||||
var updated_security_criteria_tag = new SparkTags.SparkTag<SecurityCriteria>("TimeAndSales Callback");
|
||||
var criteria_callback_command = SparkCommand.create("Update Time and Sales", SparkCommands.ShowTimeAndSales.icon, updated_security_criteria_tag);
|
||||
var update_securities_tag = new SparkTags.SparkTag<SecurityCriteria>("TimeAndSales Callback");
|
||||
var update_securities_cmd = SparkCommand.create("Update Time and Sales", Icon, update_securities_tag);
|
||||
var current_smd_source = _vm.observable.SelectMany(vm => vm.current_security_md_observable(_bus)).ObserveOnDispatcher();
|
||||
var selected_secs_source = _vm.observable.SelectMany(vm => vm.selected_securities_observable()).ObserveOnDispatcher();
|
||||
_commands = new SparkCommand[] {
|
||||
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (criteria_callback_command, _state.security_criteria)),
|
||||
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (update_securities_cmd, _state.security_criteria)),
|
||||
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
|
||||
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)
|
||||
};
|
||||
_subscriptions = _bus.subscribe(updated_security_criteria_tag).ObserveOnDispatcher().Subscribe(update_criteria);
|
||||
_subscriptions = _bus.subscribe(update_securities_tag).ObserveOnDispatcher().Subscribe(update_criteria);
|
||||
_initialize();
|
||||
|
||||
bool always_true<T>(T _) => true;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<ResourceDictionary Source="/Modules/Spreader/ParameterSectionView.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<sys:Double x:Key="GroupLevelIndent">18</sys:Double>
|
||||
<sys:Double x:Key="RowHeaderWidth">265</sys:Double>
|
||||
<sys:Double x:Key="RowHeaderWidth">285</sys:Double>
|
||||
<sys:Double x:Key="RowHeight">16</sys:Double>
|
||||
<sys:Double x:Key="NegativeRowHeight">-16</sys:Double>
|
||||
<sys:Double x:Key="ItemsHostCanBeDisabledCollapsedHeight">15</sys:Double>
|
||||
@@ -104,7 +104,7 @@
|
||||
</usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
|
||||
<DataTemplate>
|
||||
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}"/>
|
||||
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}" Increment="{Binding tick_increment, FallbackValue={u:Double 1}}"/>
|
||||
</DataTemplate>
|
||||
</usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
|
||||
<usp:DataGridParameterCellTemplateConverter.IntTemplate>
|
||||
@@ -299,6 +299,7 @@
|
||||
ShowExpanderArrow="False" PopupPlacement="Right" Header="Add Leg" Padding="6,0"
|
||||
Background="{StaticResource Button.Static.BackgroundBrush}"
|
||||
IsOpen="{Binding IsNewLegPopupOpen, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}, Mode=TwoWay}"
|
||||
IsEnabled="{Binding CanUserAddNewLeg, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}}"
|
||||
>
|
||||
<msp:ContractPickerView
|
||||
CloseOnOK="False" CloseOnCancel="False"
|
||||
@@ -317,7 +318,7 @@
|
||||
Command="{Binding RemoveLegCommand, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}}"
|
||||
CommandParameter="{Binding}" Style="{StaticResource RoundCloseButton}"
|
||||
/>
|
||||
<TextBlock Grid.Column="1" Margin="4,0,0,0" Text="{Binding}"/>
|
||||
<TextBlock Grid.Column="1" Margin="4,0,0,0" Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace mbts.ui.spark {
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExecutionStrategyEditor), new FrameworkPropertyMetadata(typeof(ExecutionStrategyEditor)));
|
||||
|
||||
public ExecutionStrategyEditor() =>
|
||||
new ExecutionStrategyErrorAdorner(this);
|
||||
AdornerHelper.attach_continuously_on_loaded(() => new ExecutionStrategyErrorAdorner(this), this);
|
||||
|
||||
#region SyntheticName
|
||||
public static readonly DependencyProperty SyntheticNameProperty = DependencyProperty.Register("SyntheticName", typeof(string), typeof(ExecutionStrategyEditor),
|
||||
@@ -52,8 +52,43 @@ namespace mbts.ui.spark {
|
||||
#endregion
|
||||
|
||||
#region IsNew
|
||||
public static readonly DependencyProperty IsNewProperty = DependencyProperty.Register(nameof(IsNew), typeof(bool), typeof(ExecutionStrategyEditor), new PropertyMetadata(false));
|
||||
public bool IsNew { get => (bool)GetValue(IsNewProperty); set => SetValue(IsNewProperty, value); }
|
||||
public static readonly DependencyProperty IsNewProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(IsNew),
|
||||
typeof(bool),
|
||||
typeof(ExecutionStrategyEditor),
|
||||
new PropertyMetadata(false, OnIsNewChanged));
|
||||
|
||||
static void OnIsNewChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
|
||||
d.CoerceValue(CanUserAddNewLegProperty);
|
||||
|
||||
public bool IsNew {
|
||||
get => (bool)GetValue(IsNewProperty);
|
||||
set => SetValue(IsNewProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CanUserAddNewLeg
|
||||
public static readonly DependencyProperty CanUserAddNewLegProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(CanUserAddNewLeg),
|
||||
typeof(bool),
|
||||
typeof(ExecutionStrategyEditor),
|
||||
new PropertyMetadata(false, null, CoerceCanUserAddNewLeg));
|
||||
|
||||
static object CoerceCanUserAddNewLeg(DependencyObject d, object baseValue) {
|
||||
var value = (bool)baseValue;
|
||||
if (value) {
|
||||
if (!((ExecutionStrategyEditor)d).IsNew)
|
||||
return false;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool CanUserAddNewLeg {
|
||||
get => (bool)GetValue(CanUserAddNewLegProperty);
|
||||
set => SetValue(CanUserAddNewLegProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IsNewLegPopupOpen
|
||||
@@ -162,7 +197,7 @@ namespace mbts.ui.spark {
|
||||
var legs = LegTuples;
|
||||
dg.Columns.SetItems(legs.Select(_column_factory));
|
||||
if (IsNew) dg.Columns.Add((DataGridColumn)FindResource("AddColumnPlaceholder"));
|
||||
_dg_line_adorner = new ExecutionStrategyGridLineAdorner(dg);
|
||||
_dg_line_adorner = AdornerHelper.attach_continuously_on_loaded(() => new ExecutionStrategyGridLineAdorner(dg), dg);
|
||||
_legs_collection_changed_sub = (legs as INotifyCollectionChanged)?.Subscribe((s, e) => dg.Columns.ReflectChange(legs, _column_factory, e));
|
||||
}
|
||||
}
|
||||
@@ -174,8 +209,7 @@ namespace mbts.ui.spark {
|
||||
return col;
|
||||
}
|
||||
|
||||
ExecutionStrategyGridLineAdorner _dg_line_adorner;
|
||||
IDisposable _legs_collection_changed_sub;
|
||||
IDisposable _legs_collection_changed_sub, _dg_line_adorner;
|
||||
|
||||
private class DataGridColumnInternal : DataGridDynamicColumn {
|
||||
readonly IValueConverter _cell_template_converter, _cell_editing_template_converter;
|
||||
@@ -231,7 +265,7 @@ namespace mbts.ui.spark {
|
||||
if (_vm != null && _vm.GetErrors(nameof(SyntheticEditorVM.tick_factors)).Cast<string>().Any())
|
||||
this.draw_border(drawing_context, _tick_combo_box.value, null, new Pen(Brushes.Red, 1));
|
||||
string error_text = string.Join(", ", _vm.AllErrors);
|
||||
this.draw_relative_text(RelativePosition.Below, error_text, drawing_context, _name_text_box.value, Brushes.OrangeRed, 0, y_offset);
|
||||
this.draw_relative_text(RelativePosition.Below, error_text, drawing_context, _name_text_box.value, BTSColors.header_error_text_brush, 0, y_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,10 @@ namespace bts.shell {
|
||||
};
|
||||
public static PostHedgePriceBasedForcedCross PostHedgePriceBasedForcedCross => new PostHedgePriceBasedForcedCross();
|
||||
public static PostHedgeTimeBasedForcedCross PostHedgeTimeBasedForcedCross => new PostHedgeTimeBasedForcedCross();
|
||||
public static PostHedgeOppositeMarketVolumeBasedForcedCross PostHedgeOppositeMarketVolumeBasedForcedCross =>
|
||||
new PostHedgeOppositeMarketVolumeBasedForcedCross {
|
||||
QuantityThreshold = OrderDataHelper.Defaults.QuantityMeasure
|
||||
};
|
||||
#endregion
|
||||
|
||||
public static SyntheticContract SyntheticContract(string name, IEnumerable<ContractLeg> legs) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
TickFactors="{Binding tick_factors}"
|
||||
AvailableTickFactors="{Binding available_tick_factors_view}"
|
||||
IsNew="{Binding is_new}"
|
||||
CanUserAddNewLeg="{Binding can_add_leg}"
|
||||
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
|
||||
PriceCalc="{Binding price_calc}"
|
||||
ParameterRows="{Binding rows}"
|
||||
|
||||
@@ -6,9 +6,7 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for SyntheticManagementView.xaml
|
||||
/// </summary>
|
||||
internal partial class SyntheticManagementView : UserControl {
|
||||
public SyntheticManagementView() {
|
||||
public SyntheticManagementView() =>
|
||||
InitializeComponent();
|
||||
DataGridFilterHelper.set_filter_key_event_hooks(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using static bts.shell.SyntheticManagementWindowFactory;
|
||||
|
||||
namespace mbts.spark {
|
||||
class SyntheticManagementWindow : SparkWindow<StartupData> {
|
||||
const string _window_type = "SyntheticManagement";
|
||||
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
|
||||
StartupData _state;
|
||||
OrdersModel _orders_model;
|
||||
@@ -60,6 +61,7 @@ namespace mbts.spark {
|
||||
else _view.DataContext = _vm.value;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands { get; }
|
||||
public override StartupData SaveState => _state = new StartupData(_vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
|
||||
|
||||
@@ -10,6 +10,15 @@ namespace mbts.spark {
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, double value) =>
|
||||
leg_tup.leg_params.QuoteParams.LeanQuantityRatio = value;
|
||||
|
||||
public override ICell create_cell(LegSpreaderParamsTuple leg_tup) => new HedgeRatioCell(this, leg_tup);
|
||||
|
||||
class HedgeRatioCell : Cell<double> {
|
||||
public HedgeRatioCell(RowModelBase<double> model, LegSpreaderParamsTuple leg_tup)
|
||||
: base(model, leg_tup) { }
|
||||
|
||||
public double tick_increment => 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel : RowModelBase<uint> {
|
||||
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
|
||||
|
||||
public override string display_name { get; } = "Cross Ticks";
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.PostHedge, Subsections.OppositeMarketVolumeBasedCross };
|
||||
|
||||
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_omv_cross(leg_tup).CrossTicks = _value_from_store(leg_tup);
|
||||
|
||||
public override uint get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross ?
|
||||
omv_cross.CrossTicks :
|
||||
_value_from_store(leg_tup);
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
|
||||
_value_store[leg_tup.SecurityKey] = value;
|
||||
if (_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross)
|
||||
omv_cross.CrossTicks = value;
|
||||
}
|
||||
|
||||
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_omv_cross(leg_tup) != null;
|
||||
|
||||
PostHedgeOppositeMarketVolumeBasedForcedCross _get_omv_cross(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross;
|
||||
|
||||
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
|
||||
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.CrossTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using bts.shell;
|
||||
using mbts.ui.spark;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class PostHedgeOppositeMarketVolumeBasedCrossEnabledModel : RowModelBase<bool> {
|
||||
readonly PostHedgeOppositeMarketVolumeBasedCrossThresholdModel _threshold;
|
||||
readonly PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel _ticks;
|
||||
|
||||
public PostHedgeOppositeMarketVolumeBasedCrossEnabledModel(PostHedgeOppositeMarketVolumeBasedCrossThresholdModel threshold, PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel ticks) {
|
||||
_threshold = threshold;
|
||||
_ticks = ticks;
|
||||
}
|
||||
|
||||
public override string display_name { get; } = string.Empty;
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.PostHedge, Subsections.OppositeMarketVolumeBasedCross };
|
||||
|
||||
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross != null;
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
|
||||
if (value) {
|
||||
if (leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross == null) {
|
||||
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross = SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross;
|
||||
_threshold.apply_value(leg_tup);
|
||||
_ticks.apply_value(leg_tup);
|
||||
}
|
||||
} else {
|
||||
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross = null;
|
||||
}
|
||||
_threshold.set_enabled(leg_tup, value);
|
||||
_ticks.set_enabled(leg_tup, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using mbts.collections.extensions;
|
||||
using mbts.ui.spark;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class SyntheticEditorVM {
|
||||
class PostHedgeOppositeMarketVolumeBasedCrossThresholdModel : RowModelBase<QuantityMeasure> {
|
||||
IDictionary<uint, QuantityMeasure> _value_store = new Dictionary<uint, QuantityMeasure>();
|
||||
|
||||
public override string display_name { get; } = "Threshold";
|
||||
public override ParameterSection[] section_path { get; } = new[] { Sections.PostHedge, Subsections.OppositeMarketVolumeBasedCross };
|
||||
|
||||
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_omv_cross(leg_tup).QuantityThreshold = _value_from_store(leg_tup);
|
||||
|
||||
public override QuantityMeasure get_value(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross ?
|
||||
omv_cross.QuantityThreshold :
|
||||
_value_from_store(leg_tup);
|
||||
|
||||
public override void set_value(LegSpreaderParamsTuple leg_tup, QuantityMeasure value) {
|
||||
_value_store[leg_tup.SecurityKey] = value;
|
||||
if (_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross)
|
||||
omv_cross.QuantityThreshold = value;
|
||||
}
|
||||
|
||||
public override ICell create_cell(LegSpreaderParamsTuple leg_tup) => new QuantityMeasureCell(this, leg_tup);
|
||||
|
||||
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
|
||||
_get_omv_cross(leg_tup) != null;
|
||||
|
||||
PostHedgeOppositeMarketVolumeBasedForcedCross _get_omv_cross(LegSpreaderParamsTuple leg_tup) =>
|
||||
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross;
|
||||
|
||||
QuantityMeasure _value_from_store(LegSpreaderParamsTuple leg_tup) =>
|
||||
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.QuantityThreshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -123,6 +123,12 @@ namespace mbts.spark {
|
||||
"forces legged hedge orders to automatically cross the market, if they are still " +
|
||||
"working beyond a certain time period.If a hedge order is legged for 'Time(ms)', " +
|
||||
"it is re-priced to cross the opposite inside market by 'Cross Ticks.'");
|
||||
|
||||
public static ParameterSection OppositeMarketVolumeBasedCross =>
|
||||
new ParameterSection("Size Based Cross", true,
|
||||
"forces legged hedging orders to automatically cross the market if the volume " +
|
||||
"of the opposite inside market drops below the threhold. If opposite inside market volume drops below 'Threshold,' " +
|
||||
"it is re-priced to cross the opposite inside market by 'Cross Ticks.'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace mbts.spark {
|
||||
readonly QuantityModel _quantity_model;
|
||||
readonly PriceMultiplierModel _price_mult_model;
|
||||
readonly List<IDisposable> _resources = new List<IDisposable>();
|
||||
readonly Property<bool> _has_working_orders;
|
||||
readonly Property<bool> _has_working_orders, _can_add_leg;
|
||||
Security _synthetic;
|
||||
bool _set_name_automatically, _set_ticks_automatically, _set_quantities_automatically, _set_price_mults_automatically, _is_setting_quantities, _is_setting_price_mults;
|
||||
TickFactors _tick_factors;
|
||||
@@ -173,6 +173,9 @@ namespace mbts.spark {
|
||||
}
|
||||
_is_setting_price_mults = false;
|
||||
};
|
||||
_can_add_leg = backing(nameof(can_add_leg), leg_tuples.ObserveSelf().Select(_ => leg_count < 2), leg_count < 2);
|
||||
} else {
|
||||
_can_add_leg = backing(nameof(can_add_leg), false);
|
||||
}
|
||||
var quote_throttle_outside_ticks = new QuoteThrottleOutsideTicksModel();
|
||||
var quote_throttle_inside_ticks = new QuoteThrottleInsideTicksModel();
|
||||
@@ -191,6 +194,9 @@ namespace mbts.spark {
|
||||
|
||||
var post_hedge_time_based_cross_ticks = new PostHedgeTimeBasedCrossMarketTicksModel();
|
||||
var post_hedge_time_based_cross_time = new PostHedgeTimeBasedCrossTimeModel();
|
||||
|
||||
var post_hedge_opposite_market_based_cross_threshold = new PostHedgeOppositeMarketVolumeBasedCrossThresholdModel();
|
||||
var post_hedge_opposite_market_based_cross_cross_ticks = new PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel();
|
||||
rows = new IRowModel[] {
|
||||
_quantity_model,
|
||||
_price_mult_model,
|
||||
@@ -214,7 +220,10 @@ namespace mbts.spark {
|
||||
post_hedge_price_based_cross_market_ticks,
|
||||
new PostHedgeTimeBasedCrossEnabledModel(post_hedge_time_based_cross_time, post_hedge_time_based_cross_ticks),
|
||||
post_hedge_time_based_cross_time,
|
||||
post_hedge_time_based_cross_ticks
|
||||
post_hedge_time_based_cross_ticks,
|
||||
new PostHedgeOppositeMarketVolumeBasedCrossEnabledModel(post_hedge_opposite_market_based_cross_threshold, post_hedge_opposite_market_based_cross_cross_ticks),
|
||||
post_hedge_opposite_market_based_cross_threshold,
|
||||
post_hedge_opposite_market_based_cross_cross_ticks
|
||||
}.Select(m => new RowVM(m, leg_tuples)).ToList();
|
||||
set_synthetic(synthetic, strategy);
|
||||
}
|
||||
@@ -243,6 +252,7 @@ namespace mbts.spark {
|
||||
public bool has_working_orders => _has_working_orders.Value;
|
||||
public bool is_new { get; }
|
||||
public bool is_new_leg_popup_open { get; set; }
|
||||
public bool can_add_leg => _can_add_leg.Value;
|
||||
public SyntheticContract.Types.PriceCalc price_calc { get => _synthetic.Synthetic.PriceCalc; set => _synthetic.Synthetic.PriceCalc = value; }
|
||||
public ObservableCollection<LegSpreaderParamsTuple> leg_tuples { get; } = new ObservableCollection<LegSpreaderParamsTuple>();
|
||||
public int leg_count => leg_tuples.Count;
|
||||
@@ -291,7 +301,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
void _deduce_name() {
|
||||
_synthetic.Synthetic.Name = SecurityFormatter.FormatSyntheticDesc(leg_tuples.Select(l => l.leg));
|
||||
_synthetic.Synthetic.Name = SecurityFormatter.format_contract_legs(leg_tuples.Select(l => new ContractLegImmutable(l.leg)));
|
||||
RaisePropertyChanged(nameof(name));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using static bts.api.SecurityFormatter;
|
||||
using static bts.shell.SyntheticManagementWindowFactory;
|
||||
|
||||
namespace mbts.spark {
|
||||
@@ -70,7 +69,7 @@ namespace mbts.spark {
|
||||
).ObserveOnDispatcher());
|
||||
name = security.Synthetic.Name;
|
||||
n_legs = security.Synthetic.Legs.Count();
|
||||
description = FormatSyntheticDesc(security);
|
||||
description = SecurityFormatter.format_contract_legs(security.Synthetic.Legs);
|
||||
show_ladder = new DelegateCommand(_show_ladder);
|
||||
show_order_book = new DelegateCommand(_show_order_book);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace bts.shell
|
||||
{
|
||||
public static class ActivityLogCategory
|
||||
{
|
||||
namespace bts.shell {
|
||||
public static class ActivityLogCategory {
|
||||
public static ILogViewerCategory order = new LogViewerCategory("Order", BTSColors.Orders.sent_brush);
|
||||
public static ILogViewerCategory fill = new LogViewerCategory("Fill", BTSColors.Orders.filled_dark);
|
||||
public static ILogViewerCategory cancel => new LogViewerCategory("Cancel", BTSColors.Orders.working_cancel);
|
||||
public static ILogViewerCategory modify = new LogViewerCategory("Modify", BTSColors.Orders.modified);
|
||||
public static ILogViewerCategory fill = new LogViewerCategory("Fill", BTSColors.Orders.filled_dark_brush);
|
||||
public static ILogViewerCategory cancel = new LogViewerCategory("Cancel", BTSColors.Orders.working_cancel_brush);
|
||||
public static ILogViewerCategory modify = new LogViewerCategory("Modify", BTSColors.Orders.modified_brush);
|
||||
public static ILogViewerCategory warn = new LogViewerCategory("Warn", Brushes.Gold);
|
||||
|
||||
public static IEnumerable<ILogViewerCategory> all_categories {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using bts.api;
|
||||
using bts.spark;
|
||||
using mbts;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -13,27 +14,36 @@ using System.Windows.Media;
|
||||
|
||||
namespace bts.shell
|
||||
{
|
||||
class ActivityLogWindow : SparkWindow<ActivityWindowState> {
|
||||
class ActivityLogWindow : SparkWindow<ActivityWindowState>, ISharedWindow {
|
||||
const string _window_type = "ActivityLog";
|
||||
readonly IObservable<LogViewerMessage> _message_src;
|
||||
readonly LogViewerMessageCollection _messages = new LogViewerMessageCollection();
|
||||
IDisposable _dispose;
|
||||
LogViewerMessageCollection _messages = new LogViewerMessageCollection();
|
||||
Lazy<UIElement> _content;
|
||||
LogViewer _content;
|
||||
|
||||
public ActivityLogWindow(IObservable<LogViewerMessage> messages, IBus bus, ActivityWindowFactory isw)
|
||||
: base("Activity Log", bus, isw, SparkCommands.ShowActivityLog.icon) {
|
||||
_dispose = messages.ObserveOnDispatcher().Subscribe(_messages.AddLog);
|
||||
_content = new Lazy<UIElement>(() =>
|
||||
new LogViewer {
|
||||
ItemsSource = _messages,
|
||||
Categories = ActivityLogCategory.all_categories }
|
||||
);
|
||||
_message_src = messages;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override ActivityWindowState SaveState => new ActivityWindowState();
|
||||
|
||||
public override UIElement Content => _content.Value;
|
||||
public override UIElement Content => _content;
|
||||
|
||||
public string thread_id => nameof(ActivityLogWindow);
|
||||
|
||||
public override void Dispose() {
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
|
||||
public void thread_init() {
|
||||
_dispose = _message_src.ObserveOnDispatcher().Subscribe(_messages.AddLog);
|
||||
_content =
|
||||
new LogViewer {
|
||||
ItemsSource = _messages,
|
||||
Categories = ActivityLogCategory.all_categories
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.spark.filters;
|
||||
@@ -9,38 +8,34 @@ using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Data;
|
||||
using static bts.shell.FillsWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.FillsView;
|
||||
using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
|
||||
namespace mbts.spark {
|
||||
class FillsVM : SecurityGrid<FillsVM.Row, FillTuple> {
|
||||
readonly OrdersModel _model;
|
||||
readonly uint[] _orders;
|
||||
|
||||
public FillsVM(IBus bus, OrdersModel model, StartupData state)
|
||||
: this(bus, model, _get_observable(bus, model, state), state.sorting_criteria, state.filters) { }
|
||||
|
||||
public FillsVM(IBus bus, OrdersModel model, uint[] orders, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
|
||||
: this(bus, model, orders != null && orders.Length > 0 ? model.on_order_fill_tuples(orders) : model.on_fill_tuples, sorting_criteria, filters) { }
|
||||
|
||||
public FillsVM(IBus bus, OrdersModel model, IObservable<TableUpdate<FillTuple, string>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
|
||||
FillsVM(IBus bus, OrdersModel model, IObservable<ITableUpdateTransform<FillTuple, string>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
|
||||
: base(RXCollection.CreateTableUpdate(fills_source), sorting_criteria) {
|
||||
_model = model;
|
||||
_orders = orders;
|
||||
account_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.accounts, r => r.account, nameof(Row.account));
|
||||
user_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.users, r => r.user, nameof(Row.user));
|
||||
var securities_source = fills_source
|
||||
.Select(t => new HashSet<SecurityImmutable>(
|
||||
t.state.Values
|
||||
.Where(ft => ft.security.HasValue)
|
||||
.Select(ft => ft.security.Value))
|
||||
);
|
||||
account_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_accounts(), r => r.account, nameof(Row.account));
|
||||
user_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_users(), r => r.user, nameof(Row.user));
|
||||
security_filter = DiscreteValuesFilterVM.Create<Row, SecurityImmutable, SecurityCriteria>(
|
||||
securities_source,
|
||||
fills_source.with_respect_to(ft => ft.security ?? SecurityDataHelper.Defaults.SecurityImmutable, s => s.Id, ft => ft.key).Select(t => (t.added, t.removed)),
|
||||
r => r.security,
|
||||
SecurityCriteria.from_securities,
|
||||
SecurityCriteria.get_to_securities(bus),
|
||||
nameof(Row.security),
|
||||
EqualityComparer.Create<SecurityImmutable, uint>(s => s.Id),
|
||||
SecurityDataHelper.EqualityComparer.IDComparer,
|
||||
Comparer<SecurityImmutable>.Default
|
||||
);
|
||||
if (filters != null) {
|
||||
@@ -57,15 +52,45 @@ namespace mbts.spark {
|
||||
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))
|
||||
)
|
||||
);
|
||||
}
|
||||
// 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);
|
||||
add_col(Columns.User, DefaultColumnHeaders.User, r => r.user, nameof(Row.user), user_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.User);
|
||||
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) });
|
||||
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.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) {
|
||||
Header = header,
|
||||
sort_member_path = prop,
|
||||
Width = width
|
||||
});
|
||||
//
|
||||
_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_key_filter, order_key_filter, security_key_filter
|
||||
));
|
||||
}
|
||||
|
||||
public uint[] orders => _orders;
|
||||
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
|
||||
|
||||
#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));
|
||||
public IDataGridColumnPersistentFilter<Row, OrderType[]> order_type_filter { get; } = DiscreteValuesFilterVM.Create<Row, OrderType>(EnumHelper.get_values<OrderType>().ToLArray(), r => r.order_type, nameof(Row.order_type));
|
||||
@@ -96,11 +121,19 @@ namespace mbts.spark {
|
||||
order_key = order_key_filter.get_state(),
|
||||
security_key = security_key_filter.get_state()
|
||||
};
|
||||
#endregion
|
||||
|
||||
protected override Row row_factory(FillTuple row_model) => new Row(row_model, _model);
|
||||
protected override IEnumerable<SortingCriteria> get_default_sorting() { yield return new SortingCriteria(nameof(Row.fill_time), false); }
|
||||
|
||||
public class Row : SecurityRow, IEquatable<Row> {
|
||||
static IObservable<ITableUpdateTransform<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, StartupData state) {
|
||||
if (state.orders != null && state.orders.Length == 0) {
|
||||
var orders_hash = new HashSet<uint>(state.orders);
|
||||
return model.on_fill_tuples.filter(f => orders_hash.Contains(f.fill.OrderKey));
|
||||
} else return model.on_fill_tuples;
|
||||
}
|
||||
|
||||
public class Row : SecurityRow, IEquatable<Row>, IRowVM {
|
||||
readonly OrdersModel _model;
|
||||
readonly FillImmutable _fill;
|
||||
readonly OrderRecordImmutable _order;
|
||||
@@ -116,7 +149,7 @@ namespace mbts.spark {
|
||||
public OrderType order_type => _order.Def.CustomDef.CustomCase;
|
||||
public OrderMetadata order_metadata => _model.factory.get_metadata(_order.Def.CustomDef.CustomCase);
|
||||
public string account => _order.Def.Account;
|
||||
public string user => _order.Def.Login;
|
||||
public string user => _order.Def.User;
|
||||
public Venue exchange => security.Exchange;
|
||||
public Side side => _fill.Side;
|
||||
public double price => _fill.LastPrice;
|
||||
@@ -127,6 +160,9 @@ namespace mbts.spark {
|
||||
public bool Equals(Row other) => other != null && other._fill.Equals(_fill) && other._order.Equals(_order);
|
||||
public override bool Equals(object obj) => Equals(obj as Row);
|
||||
public override int GetHashCode() => _fill.FillKey.GetHashCode() ^ _order.Key.GetHashCode();
|
||||
|
||||
public event Action invalidated;
|
||||
void _raise_invalidated() => invalidated?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +1,47 @@
|
||||
<UserControl x:Class="mbts.ui.spark.FillsView" 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"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters">
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.FillsView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
|
||||
<Style x:Key="SideColorCodedTextElement" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{u:BTSColor MarketData.bid}"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding side}" Value="Sell">
|
||||
<Setter Property="Foreground" Value="{u:BTSColor MarketData.ask}"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style x:Key="OrderTypeElement" TargetType="usp:OrderTypeItem">
|
||||
<Setter Property="OrderType" Value="{Binding order_type}"/>
|
||||
<Setter Property="Metadata" Value="{Binding order_metadata}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<DataGrid x:Name="grid" ItemsSource="{Binding rows_view}" IsReadOnly="True" CanUserSortColumns="True" GridLinesVisibility="None"
|
||||
CurrentItem="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
u:DataGridBehavior.NotifyColumnsOfSorting="True" BorderThickness="0">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Security" Binding="{Binding security}"
|
||||
c:DataGridEx.CellKey="{u:Enum Security, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.security_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
|
||||
<DataGridTextColumn Header="Fill Time" Binding="{Binding fill_time, StringFormat='M/d h:mm:ss tt'}"
|
||||
c:DataGridEx.CellKey="{u:Enum FillTime, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.fill_time_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.fill_time_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.fill_time_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource TimeFilterTemplate}"/>
|
||||
<DataGridTextColumn Header="User" Binding="{Binding user}"
|
||||
c:DataGridEx.CellKey="{u:Enum User, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.user_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.user_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.user_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
|
||||
<DataGridTextColumn Header="Account" Binding="{Binding account}"
|
||||
c:DataGridEx.CellKey="{u:Enum Account, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.account_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.account_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.account_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
|
||||
<DataGridTextColumn Header="Exchange" Binding="{Binding exchange}"
|
||||
c:DataGridEx.CellKey="{u:Enum Exchange, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.exchange_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.exchange_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.exchange_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
|
||||
<DataGridTextColumn Header="TIF" Binding="{Binding time_in_force}"
|
||||
c:DataGridEx.CellKey="{u:Enum TimeInForce, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding time_in_force_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.time_in_force_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.time_in_force_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
|
||||
<usp:DataGridOrderTypeColumn Header="Order Type" ElementStyle="{StaticResource OrderTypeElement}" SortMemberPath="order_type"
|
||||
c:DataGridEx.CellKey="{u:Enum OrderType, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.order_type_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.order_type_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.order_type_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
|
||||
<DataGridTextColumn Header="B/S" ElementStyle="{StaticResource SideColorCodedTextElement}" Binding="{Binding side}"
|
||||
c:DataGridEx.CellKey="{u:Enum Side, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.side_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.side_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.side_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"/>
|
||||
<DataGridTextColumn Header="Qty" Binding="{Binding size}"
|
||||
c:DataGridEx.CellKey="{u:Enum Size, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.size_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.size_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.size_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource UIntMinMaxTemplate}"/>
|
||||
<DataGridTextColumn Header="Price" SortMemberPath="price"
|
||||
c:DataGridEx.CellKey="{u:Enum Price, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.price_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.price_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.price_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Fill Key" Binding="{Binding fill_key}"
|
||||
c:DataGridEx.CellKey="{u:Enum FillKey, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.fill_key_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.fill_key_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.fill_key_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
|
||||
<DataGridTextColumn Header="Order Key" Binding="{Binding order_key}"
|
||||
c:DataGridEx.CellKey="{u:Enum OrderKey, Type={u:Nested usp:FillsView.Columns}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.order_key_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.order_key_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.order_key_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"/>
|
||||
<DataGridTextColumn Header="Security Key" Width="*" Binding="{Binding security_key}"
|
||||
c:DataGridEx.CellKey="{u:Enum SecurityKey, Type={u:Nested usp:FillsView.Columns}}"
|
||||
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>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
<AdornerDecorator>
|
||||
<Grid>
|
||||
<c:DIYGrid
|
||||
x:Name="_grid"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row}"
|
||||
Rows="{Binding rows_view}"
|
||||
>
|
||||
<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:FillsRowView/>
|
||||
</DataTemplate>
|
||||
</c:DIYGrid.RowTemplate>
|
||||
</c:DIYGrid>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -1,4 +1,5 @@
|
||||
using mbts.spark.filters;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
@@ -8,9 +9,9 @@ namespace mbts.ui.spark {
|
||||
partial class FillsView : UserControl {
|
||||
public enum Columns { FillTime, User, Account, Exchange, Security, TimeInForce, OrderType, Side, Price, Size, FillKey, OrderKey, SecurityKey, ExchangeOrderID }
|
||||
|
||||
public FillsView() {
|
||||
public FillsView() =>
|
||||
InitializeComponent();
|
||||
DataGridFilterHelper.set_filter_key_event_hooks(this);
|
||||
}
|
||||
}
|
||||
|
||||
class FillsRowView : DIYGrid.HighPerformanceRow<FillsVM.Row, FillsView.Columns> { }
|
||||
}
|
||||
@@ -5,35 +5,35 @@ using mbts.ui;
|
||||
using mbts.ui.spark;
|
||||
using mbts.utils;
|
||||
using System.Windows;
|
||||
using StartupData = bts.shell.FillsWindowFactory.StartupData;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
class FillsWindow : SparkWindow<FillsWindowFactory.StartupData> {
|
||||
namespace mbts.spark {
|
||||
class FillsWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
const string _window_type = "Fills", _thread_id = "Fills";
|
||||
readonly AllowOnce _init_guard = new AllowOnce("Fills");
|
||||
readonly OrdersModel _orders_model;
|
||||
SingleValuePump<FillsVM> _vm = new SingleValuePump<FillsVM>();
|
||||
FillsWindowFactory.StartupData _state;
|
||||
FillsVM _vm;
|
||||
FillsView _view;
|
||||
StartupData _state;
|
||||
|
||||
public static FillsWindow create(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, FillsWindowFactory.StartupData state) {
|
||||
var window = new FillsWindow(factory, bus, orders_model);
|
||||
window.initialize(state);
|
||||
return window;
|
||||
}
|
||||
|
||||
FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model) : base("Fills", bus, factory, SparkCommands.ShowFills.icon) =>
|
||||
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state)
|
||||
: base("Fills", bus, factory, SparkCommands.ShowFills.icon) {
|
||||
_orders_model = orders_model;
|
||||
|
||||
public void initialize(FillsWindowFactory.StartupData state) {
|
||||
Logger.notnull(state);
|
||||
_state = state;
|
||||
_vm.value = new FillsVM(_bus,_orders_model, state.orders, state.sorting_criteria, state.filters);
|
||||
if (_view == null) _view = new FillsView { DataContext = _vm.value };
|
||||
else _view.DataContext = _vm.value;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override FillsWindowFactory.StartupData SaveState => _state = new FillsWindowFactory.StartupData(_vm.value.orders, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filters());
|
||||
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 string thread_id => _thread_id;
|
||||
|
||||
public void thread_init() {
|
||||
_init_guard.check_access();
|
||||
Logger.notnull(_state);
|
||||
_vm = new FillsVM(_bus, _orders_model, _state);
|
||||
_view = new FillsView { DataContext = _vm };
|
||||
}
|
||||
|
||||
public override void Dispose() => Disposable.dispose(ref _vm);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace bts.shell {
|
||||
}
|
||||
|
||||
public override Task<IShellWindow> load(StartupData state) =>
|
||||
Task.FromResult<IShellWindow>(FillsWindow.create(this, _bus, _orders_model, state));
|
||||
Task.FromResult<IShellWindow>(new FillsWindow(this, _bus, _orders_model, state));
|
||||
|
||||
public async void show(StartupData state) =>
|
||||
_bus.post(SparkTags.ShowWindow, await load(state));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using bts.api;
|
||||
using bts.api;
|
||||
using mbts;
|
||||
using mbts.config;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using ProtoBuf;
|
||||
@@ -7,69 +9,112 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using LadderSettings = mbts.spark.LadderSettings;
|
||||
|
||||
namespace bts.shell {
|
||||
class LadderWindowFactory : ShellWindowFactory<LadderSaveState> {
|
||||
const string _window_type = "ladder";
|
||||
readonly IBus _bus;
|
||||
readonly IWindowManager _wm;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly int _max_ladders_open;
|
||||
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
|
||||
|
||||
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, OrdersModel orders_model)
|
||||
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model)
|
||||
: base(owner, "SparkLadder") {
|
||||
_bus = bus;
|
||||
_wm = wm;
|
||||
_settings_hub = settings_hub;
|
||||
_orders_model = orders_model;
|
||||
var window_props = Configuration.get<bts.config.IShellWindowProps>();
|
||||
_max_ladders_open = (int)(window_props?.ladder?.max_total_open ?? 75);
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
|
||||
bus.subscribe(SparkTags.ShowLadderTag).ObserveOnDispatcher().Subscribe(show_window),
|
||||
bus.subscribe(SparkTags.ShowLinkedLadder).ObserveOnDispatcher().Subscribe(show_linked)
|
||||
bus.subscribe(SparkTags.ShowLadderTag).ObserveOnDispatcher().Subscribe(s => show_windows(new[] { s })),
|
||||
bus.subscribe(SparkTags.ShowLaddersTag).ObserveOnDispatcher().Subscribe(show_windows)
|
||||
);
|
||||
}
|
||||
|
||||
public async override Task<IShellWindow> load(LadderSaveState state) {
|
||||
await Wait.until_true(() => _securities.ContainsKey(state.security_key), TimeSpan.FromSeconds(15));
|
||||
if (_securities.TryGetValue(state.security_key, out SecurityMD security)) {
|
||||
return create_ladder_window(security, Observable.Return(state.settings));
|
||||
return create_ladder_window(security, state.settings);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static LadderSaveState _get_state(LadderVM vm) =>
|
||||
new LadderSaveState { security_key = vm.security.Id, settings = vm.current_settings };
|
||||
new LadderSaveState { security_key = vm.security.Id, settings = vm.get_save_state() };
|
||||
|
||||
IShellWindow create_ladder_window(SecurityMD md, IObservable<mbts.spark.LadderSettings> settings) {
|
||||
var model = new LadderModel(md, _orders_model);
|
||||
var vm = new LadderVM(_bus, model, settings);
|
||||
var view = new LadderView { DataContext = vm };
|
||||
var state = new LadderSaveState { security_key = md.security.Id, settings = vm.current_settings };
|
||||
return ShellWindow.create(module, md.security.ToString(), view, vm, s => save(_get_state(vm), s), Icons.Ladder);
|
||||
int _ladder_count = 0;
|
||||
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 settings_manager = new WindowSettingsManager<LadderSettings>(
|
||||
_settings_hub, state_settings,
|
||||
us => sanitize_ladder_settings(us.ladder),
|
||||
(us, v) => us.with(sanitize_ladder_settings(v)));
|
||||
var vm = new LadderVM(_bus, model, settings_manager);
|
||||
return new LadderView(vm);
|
||||
});
|
||||
string thread = Configuration.get<bts.config.toggles.LaddersOnThreads>().enabled ? $"Ladder{id:00}" : "";
|
||||
return ShellWindow.create(
|
||||
module, md.security.ToString(), _window_type, view,
|
||||
(v,s) => save(_get_state(((LadderView)v).vm), s),
|
||||
icon: Icons.Ladder,
|
||||
thread_id: thread);
|
||||
// if there are new props, make sure they are not null, invalid, etc.
|
||||
LadderSettings sanitize_ladder_settings(LadderSettings s) {
|
||||
// these MarketBreakLines are new settings props, ensure they are not null
|
||||
if (s.bid_break_line == null)
|
||||
s.bid_break_line = LadderSettings.Default.bid_break_line;
|
||||
if (s.ask_break_line == null)
|
||||
s.ask_break_line = LadderSettings.Default.ask_break_line;
|
||||
if (s.mid_break_line == null)
|
||||
s.mid_break_line = LadderSettings.Default.mid_break_line;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
void show_window(SecurityMD smd) =>
|
||||
_wm.Windows.Add(create_ladder_window(smd, Observable.Return(_orders_model.factory.default_ladder_settings(smd.security))));
|
||||
void show_windows(IReadOnlyCollection<SecurityMD> securities) {
|
||||
int n_allowed = _max_ladders_open - _wm.list_windows().Where(w => w.window_type == _window_type).Count();
|
||||
int n_opened = 0;
|
||||
foreach (var s in securities) {
|
||||
if (++n_opened > n_allowed) {
|
||||
BTSMessageBox.Show($"The maximum number of ladders are already opened ({_max_ladders_open}).", "Ladders",
|
||||
BTSMessageBoxButtons.OK, MessageBoxImage.Exclamation, true, false, true);
|
||||
break;
|
||||
} else {
|
||||
_bus.post(SparkTags.ShowWindow, create_ladder_window(s, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: whenever we re-implement the linked ladder, need to add some field to the LadderSaveState to indicate the owning window
|
||||
void show_linked(IObservable<SecurityMD> keystream) {
|
||||
var view = new LadderView();
|
||||
IDisposable sub = null;
|
||||
var window =
|
||||
ShellWindow.create(module, "loading", view,
|
||||
Disposable.Create(() => {
|
||||
(view.DataContext as LadderVM)?.Dispose();
|
||||
sub?.Dispose();
|
||||
}), s => {
|
||||
var vm = view.DataContext as LadderVM;
|
||||
if (vm != null) save(_get_state(vm), s);
|
||||
}, Icons.Ladder);
|
||||
sub =
|
||||
keystream
|
||||
.Select(smd => new LadderVM(_bus, new LadderModel(smd, _orders_model),
|
||||
Observable.Return(_orders_model.factory.default_ladder_settings(smd.security)))
|
||||
).Aggregate((old, vm) => { old.Dispose(); return vm; })
|
||||
.Subscribe(vm => { view.DataContext = vm; window.Title = vm.security.ToString(); });
|
||||
}
|
||||
//void show_linked(IObservable<SecurityMD> keystream) {
|
||||
// var view = new LadderView();
|
||||
// IDisposable sub = null;
|
||||
// var window =
|
||||
// ShellWindow.create(module, "loading", view,
|
||||
// Disposable.Create(() => {
|
||||
// (view.DataContext as LadderVM)?.Dispose();
|
||||
// sub?.Dispose();
|
||||
// }), s => {
|
||||
// var vm = view.DataContext as LadderVM;
|
||||
// if (vm != null) save(_get_state(vm), s);
|
||||
// }, Icons.Ladder);
|
||||
// sub =
|
||||
// keystream
|
||||
// .Select(smd => new LadderVM(_bus, new LadderModel(smd, _orders_model),
|
||||
// Observable.Return(_orders_model.factory.default_ladder_settings(smd.security)))
|
||||
// ).Aggregate((old, vm) => { old.Dispose(); return vm; })
|
||||
// .Subscribe(vm => { view.DataContext = vm; window.Title = vm.security.ToString(); });
|
||||
//}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using bts.api;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using System.Collections.Generic;
|
||||
@@ -9,9 +10,7 @@ using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using static mbts.ui.shell.ProtoBufSerializationHelper;
|
||||
|
||||
namespace bts.shell
|
||||
{
|
||||
|
||||
namespace bts.shell {
|
||||
[Export(typeof(IShellModule))]
|
||||
class TradingShellModule : IShellModule {
|
||||
LadderWindowFactory _lwf;
|
||||
@@ -20,8 +19,8 @@ namespace bts.shell
|
||||
PAndLWindowFactory _plwf;
|
||||
|
||||
[ImportingConstructor]
|
||||
public TradingShellModule(IBus bus, IWindowManager wm, OrdersModel orders_model, PAndLModel pandl_model) {
|
||||
_lwf = new LadderWindowFactory(this, bus, wm, orders_model);
|
||||
public TradingShellModule(IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model) {
|
||||
_lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model);
|
||||
_obwf = new OrderBookWindowFactory(this, bus, orders_model);
|
||||
_fwf = new FillsWindowFactory(this, bus, orders_model);
|
||||
_plwf = new PAndLWindowFactory(this, bus, pandl_model);
|
||||
@@ -34,7 +33,7 @@ namespace bts.shell
|
||||
public int priority => 20;
|
||||
|
||||
public IEnumerable<SparkCommand> actions => new [] {
|
||||
SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData()),
|
||||
SparkCommands.ShowOrders.complete(() => OrderBookWindow.default_startup_data),
|
||||
SparkCommands.ShowFills.complete(() => new FillsWindowFactory.StartupData()),
|
||||
SparkCommands.ShowPAndL.complete(() => new PAndLWindowFactory.PAndLStartupData()),
|
||||
SparkCommands.PickLadder,
|
||||
@@ -57,5 +56,4 @@ namespace bts.shell
|
||||
if (i != null) i.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -87,11 +87,8 @@ namespace mbts.spark {
|
||||
key = ort.order.Key;
|
||||
if (DoubleHelper.IsNaNOrInfinity(price))
|
||||
price = ort.order.State.Price;
|
||||
if (!size_calculator.value.HasValue || size_calculator.value.Value == 0) {
|
||||
int wq = ort.order.working_quantity();
|
||||
if (wq > 0)
|
||||
size_calculator.value = (uint)wq;
|
||||
}
|
||||
if (!size_calculator.value.HasValue || !size_calculator.value_setter_called)
|
||||
size_calculator.bypass_value_setter(ort.order.State.Quantity);
|
||||
}),
|
||||
Observable.Merge(_price_dirty, _size_dirty, _account_dirty, _time_in_force_dirty, _order_type_dirty).ObserveOnDispatcher().Subscribe(_ => RaisePropertyChanged(nameof(dirty_items_text))),
|
||||
_price_dirty.observe_current(true).CombineLatest(_size_dirty.observe_current(true), (pd, sd) => pd || sd).DistinctUntilChanged().ObserveOnDispatcher().Subscribe(modify_order));
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using mbts.collections;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace mbts.spark {
|
||||
class FillTupleSource : IDisposable {
|
||||
public static IObservable<ITableUpdateTransform<FillTuple, string>> create_observable(
|
||||
IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities,
|
||||
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders,
|
||||
IObservable<TableUpdate<FillImmutable, string>> fills
|
||||
) {
|
||||
FillTupleSource source = null;
|
||||
int ref_count = 0;
|
||||
return Observable.Create<TableUpdateTransform<FillTuple, string>>(observer => {
|
||||
if (Interlocked.Increment(ref ref_count) == 1) {
|
||||
SparkUtils.lazy_assert(source == null, () => fmt_assert_msg("source should be null, there must be a leak."));
|
||||
source = new FillTupleSource(securities, orders, fills);
|
||||
}
|
||||
IDisposable sub = source.fill_tuples.Subscribe(observer);
|
||||
return Disposable.Create(() => {
|
||||
Disposable.dispose_once(ref sub);
|
||||
if (Interlocked.Decrement(ref ref_count) == 0)
|
||||
Disposable.dispose_once(ref source);
|
||||
});
|
||||
});
|
||||
string fmt_assert_msg(string msg) => $"{nameof(FillTupleSource)}.{nameof(create_observable)}: {msg}";
|
||||
}
|
||||
|
||||
IDisposable _dispose;
|
||||
|
||||
FillTupleSource(
|
||||
IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities,
|
||||
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders,
|
||||
IObservable<TableUpdate<FillImmutable, string>> fills) {
|
||||
State state = new State();
|
||||
var src = Observable.FromEvent<Action<TableUpdateTransform<FillTuple, string>>, TableUpdateTransform<FillTuple, string>>(cb => _updated += cb, cb => _updated -= cb);
|
||||
fill_tuples = Observable.Create<TableUpdateTransform<FillTuple, string>>(observer => {
|
||||
var init_state = state.fill_tuples.state.ToDictionary();
|
||||
observer.OnNext(new TableUpdateTransform<FillTuple, string>(init_state, init_state.Values.ToList(), new List<FillTuple>(0)));
|
||||
return src.Subscribe(observer);
|
||||
});
|
||||
_dispose = Disposable.Create(
|
||||
securities.ObserveOnDispatcher().Subscribe(s => update(state.update_securities(s))),
|
||||
orders.ObserveOnDispatcher().Subscribe(o => update(state.update_orders(o))),
|
||||
fills.ObserveOnDispatcher().Subscribe(f => update(state.update_fills(f)))
|
||||
);
|
||||
void update(State new_state) {
|
||||
state = new_state;
|
||||
_updated?.Invoke(state.fill_tuples);
|
||||
}
|
||||
}
|
||||
|
||||
event Action<TableUpdateTransform<FillTuple, string>> _updated;
|
||||
|
||||
IObservable<TableUpdateTransform<FillTuple, string>> fill_tuples { get; }
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
|
||||
class State {
|
||||
readonly IReadOnlyDictionary<string, FillImmutable> _fills;
|
||||
readonly IReadOnlyDictionary<uint, OrderRecordImmutable> _orders;
|
||||
readonly IReadOnlyDictionary<uint, SecurityMD> _securities;
|
||||
readonly TableUpdateTransform<FillTuple, string> _fill_tuples;
|
||||
readonly SetMap<uint, string> _order_fills;
|
||||
readonly SetMap<uint, string> _security_fills;
|
||||
|
||||
public State() : this(
|
||||
new Dictionary<string, FillImmutable>(),
|
||||
new Dictionary<uint, OrderRecordImmutable>(),
|
||||
new Dictionary<uint, SecurityMD>(),
|
||||
new TableUpdateTransform<FillTuple, string>(new Dictionary<string, FillTuple>(), new List<FillTuple>(), new List<FillTuple>()),
|
||||
new SetMap<uint, string>(),
|
||||
new SetMap<uint, string>()
|
||||
) { }
|
||||
|
||||
State(
|
||||
IReadOnlyDictionary<string, FillImmutable> fills,
|
||||
IReadOnlyDictionary<uint, OrderRecordImmutable> orders,
|
||||
IReadOnlyDictionary<uint, SecurityMD> securities,
|
||||
TableUpdateTransform<FillTuple, string> fill_tuples,
|
||||
SetMap<uint, string> order_fill_tuples,
|
||||
SetMap<uint, string> security_fill_tuples
|
||||
) {
|
||||
_fills = fills;
|
||||
_orders = orders;
|
||||
_securities = securities;
|
||||
_fill_tuples = fill_tuples;
|
||||
_order_fills = order_fill_tuples;
|
||||
_security_fills = security_fill_tuples;
|
||||
}
|
||||
|
||||
public TableUpdateTransform<FillTuple, string> fill_tuples => _fill_tuples;
|
||||
|
||||
public State update_fills(TableUpdate<FillImmutable, string> fills) {
|
||||
var removed = fills.removed.Select(get_fill_tuple).ToList();
|
||||
var added = fills.added.Select(get_fill_tuple).ToList();
|
||||
var order_fills = _order_fills.clone();
|
||||
var security_fills = _security_fills.clone();
|
||||
HashSet<string> added_keys = new HashSet<string>(fills.added.Select(f => f.FillKey)),
|
||||
removed_keys = new HashSet<string>(fills.removed.Select(f => f.FillKey));
|
||||
foreach (var ft in removed) {
|
||||
string k = ft.fill.FillKey;
|
||||
if (!added_keys.Contains(k)) {
|
||||
order_fills.remove(ft.fill.OrderKey, k);
|
||||
security_fills.remove(ft.fill.SecurityKey, k);
|
||||
}
|
||||
}
|
||||
foreach (var ft in added) {
|
||||
string k = ft.fill.FillKey;
|
||||
if (!removed_keys.Contains(k)) {
|
||||
order_fills.add(ft.fill.OrderKey, k);
|
||||
security_fills.add(ft.fill.SecurityKey, k);
|
||||
}
|
||||
}
|
||||
return new State(fills.state, _orders, _securities, _fill_tuples.update(removed, added, f => f.key), order_fills, security_fills);
|
||||
//
|
||||
FillTuple get_fill_tuple(FillImmutable fill) =>
|
||||
new FillTuple(fill, _orders.TryGetValue(fill.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(),
|
||||
_securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
|
||||
}
|
||||
|
||||
public State update_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
|
||||
List<FillTuple> added = new List<FillTuple>(), removed = new List<FillTuple>();
|
||||
orders.detect_changed_keys(o => o.Key, out HashSet<uint> removed_keys, out HashSet<uint> added_keys, out HashSet<uint> changed_keys);
|
||||
foreach (var k in removed_keys) {
|
||||
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(_orders[k], fill_keys));
|
||||
added.AddRange(get_fill_tuples(null, fill_keys));
|
||||
}
|
||||
}
|
||||
foreach (var k in added_keys) {
|
||||
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(null, fill_keys));
|
||||
added.AddRange(get_fill_tuples(orders.get(k), fill_keys));
|
||||
}
|
||||
}
|
||||
foreach (var k in changed_keys) {
|
||||
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(_orders[k], fill_keys));
|
||||
added.AddRange(get_fill_tuples(orders.get(k), fill_keys));
|
||||
}
|
||||
}
|
||||
return new State(_fills, orders.state, _securities, _fill_tuples.update(removed, added, ft => ft.key), _order_fills, _security_fills);
|
||||
//
|
||||
IEnumerable<FillTuple> get_fill_tuples(OrderRecordImmutable? o, ISet<string> fill_keys) =>
|
||||
fill_keys.Select(k => {
|
||||
var fill = _fills[k];
|
||||
return new FillTuple(fill, o, _securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
|
||||
});
|
||||
}
|
||||
|
||||
public State update_securities(IReadOnlyDictionary<uint, SecurityMD> securities) {
|
||||
List<FillTuple> added = new List<FillTuple>(), removed = new List<FillTuple>();
|
||||
_securities.Keys.difference(securities.Keys, out IEnumerable<uint> added_keys, out IEnumerable<uint> removed_keys);
|
||||
foreach (var k in removed_keys) {
|
||||
if (_security_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(_securities[k].security, fill_keys));
|
||||
added.AddRange(get_fill_tuples(null, fill_keys));
|
||||
}
|
||||
}
|
||||
foreach (var k in added_keys) {
|
||||
if (_security_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(null, fill_keys));
|
||||
added.AddRange(get_fill_tuples(securities[k].security, fill_keys));
|
||||
}
|
||||
}
|
||||
return new State(_fills, _orders, securities, _fill_tuples.update(removed, added, f => f.key), _order_fills, _security_fills);
|
||||
//
|
||||
IEnumerable<FillTuple> get_fill_tuples(SecurityImmutable? s, ISet<string> fill_keys) =>
|
||||
fill_keys.Select(k => {
|
||||
var fill = _fills[k];
|
||||
return new FillTuple(fill, _orders.TryGetValue(fill.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(), s);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ namespace bts.spark {
|
||||
readonly IBus _bus;
|
||||
IReadOnlyDictionary<uint, SecurityMD> _security = new Dictionary<uint, SecurityMD>();
|
||||
IDisposable _dispose;
|
||||
static readonly IEqualityComparer<OrderRecordImmutable> _id_comparer =
|
||||
mbts.utils.EqualityComparer.Create<OrderRecordImmutable, uint>(a => a.Key);
|
||||
|
||||
public OrderActivitySummarizer(IBus bus, IObservable<TableUpdate<OrderRecordImmutable, uint>> orders) {
|
||||
_bus = bus;
|
||||
@@ -92,8 +94,8 @@ namespace bts.spark {
|
||||
void orders_updated(TableUpdate<OrderRecordImmutable, uint> update) {
|
||||
|
||||
// Go though added and changed orders
|
||||
var added = new HashSet<OrderRecordImmutable>(update.added);
|
||||
var changed = new HashSet<OrderRecordImmutable>(update.added);
|
||||
var added = new HashSet<OrderRecordImmutable>(update.added, _id_comparer);
|
||||
var changed = new HashSet<OrderRecordImmutable>(update.added, _id_comparer);
|
||||
var removed = update.removed.ToDictionary(o => o.Key);
|
||||
added.ExceptWith(update.removed);
|
||||
changed.ExceptWith(added);
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace mbts.ui.spark
|
||||
_ask_pen.Freeze();
|
||||
}
|
||||
private static readonly Pen _bid_pen = new Pen(BTSColors.MarketData.bid_brush, 4), _ask_pen = new Pen(BTSColors.MarketData.ask_brush, 4);
|
||||
private static readonly ConcurrentDictionary<uint, Brush> _algo_pallete = new ConcurrentDictionary<uint, Brush>();
|
||||
private static readonly ConcurrentDictionary<uint, Brush> _algo_palette = new ConcurrentDictionary<uint, Brush>();
|
||||
|
||||
public static ImageSource Create(OrderMetadata metadata, Side side) {
|
||||
Brush brush = _algo_pallete.GetOrAdd(metadata.Color, c => {
|
||||
Brush brush = _algo_palette.GetOrAdd(metadata.Color, c => {
|
||||
Brush b = new SolidColorBrush(c.rgb_color());
|
||||
b.Freeze();
|
||||
return b;
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="2"
|
||||
usp:OrderModificationControl.IsDirty="{Binding order_type_dirty, FallbackValue={u:False}}"
|
||||
HasEditableProperties="{Binding selected_order_type.has_editable_properties, FallbackValue={u:False}}"
|
||||
HasEditableProperties="{Binding properties_vm.selected_order_type.has_editable_properties, FallbackValue={u:False}}"
|
||||
IsEnabled="{Binding can_change_order_type, FallbackValue={u:True}}"
|
||||
IsOpen="{Binding properties_vm.selected_order_type.editable_properties_open, FallbackValue={u:False}}"
|
||||
ItemsSource="{Binding properties_vm.order_types}"
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace mbts.spark {
|
||||
namespace mbts.spark {
|
||||
class InlineOrderProperties {
|
||||
public readonly InlineUserProperty<string> account;
|
||||
public readonly InlineUserProperty<TimeInForce> time_in_force;
|
||||
public readonly InlineUserProperty<CustomOrderDefinition> order_type_def;
|
||||
|
||||
public InlineOrderProperties(OrdersModel model, LadderSettings settings) : this(
|
||||
new InlineUserProperty<string>(model.factory.default_account, true),
|
||||
new InlineUserProperty<string>(model.factory.default_account, false),
|
||||
new InlineUserProperty<TimeInForce>(settings.default_time_in_force, true),
|
||||
new InlineUserProperty<CustomOrderDefinition>(model.factory.custom_order(settings.default_order_type), true)) { }
|
||||
|
||||
@@ -39,148 +38,104 @@ namespace mbts.spark {
|
||||
|
||||
class OrderPropertiesEditorVM : DisposableViewModelBase {
|
||||
readonly OrdersModel _model;
|
||||
readonly ObservableCollection<CustomOrderDefinition.CustomOneofCase> _order_types_model =
|
||||
new ObservableCollection<CustomOrderDefinition.CustomOneofCase>(EnumHelper.get_values_except(CustomOrderDefinition.CustomOneofCase.None));
|
||||
Property<string> _selected_account;
|
||||
Property<TimeInForce> _selected_time_in_force;
|
||||
IOrderTypeVM _selected_order_type;
|
||||
readonly Property<SecurityImmutable> _security;
|
||||
readonly SelectorProperty<string> _accounts;
|
||||
readonly SelectorProperty<TimeInForce> _time_in_force;
|
||||
readonly SelectorProperty<IOrderTypeVM> _order_type;
|
||||
IDisposable _order_type_param_errors_sub;
|
||||
|
||||
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityImmutable> security_source, IObservable<InlineOrderProperties> order_properties) {
|
||||
_model = model;
|
||||
accounts = model.factory.accounts;
|
||||
order_types = new ObservableAutoWrapper<IOrderTypeVM, CustomOrderDefinition.CustomOneofCase>(_order_types_model, ot => OrderTypeVMFactory.create(ot, model, security_source));
|
||||
_selected_account = backing(nameof(selected_account), string.Empty, val => val != null && accounts.Contains(val));
|
||||
_selected_time_in_force = backing(nameof(selected_time_in_force), TimeInForce.Undefined, time_in_forces.Contains);
|
||||
InlineOrderProperties props = new InlineOrderProperties();
|
||||
_dispose = Disposable.Create(
|
||||
security_source.ObserveOnDispatcher().Subscribe(s => {
|
||||
security = s;
|
||||
_order_types_model.sync_with(model.factory.default_order_types(security));
|
||||
if (props.order_type_def.has_value) {
|
||||
if (props.order_type_def.force_override || selected_order_type == null || !order_types.Contains(selected_order_type)) {
|
||||
if (order_types.TryGetFirst(ot => ot.order_type == props.order_type_def.value.CustomCase, out IOrderTypeVM match)) {
|
||||
match.load_from(props.order_type_def.value);
|
||||
selected_order_type = match;
|
||||
}
|
||||
} else if (selected_order_type == null || !order_types.Contains(selected_order_type))
|
||||
selected_order_type = order_types.FirstOrDefault();
|
||||
}
|
||||
time_in_forces.sync_with(model.factory.default_time_in_forces(security));
|
||||
auto_select_time_in_force();
|
||||
security_changed?.Invoke(s);
|
||||
RaisePropertyChanged(nameof(security));
|
||||
}),
|
||||
order_properties.ObserveOnDispatcher().Subscribe(p => {
|
||||
props = p;
|
||||
auto_select_account();
|
||||
auto_select_time_in_force();
|
||||
auto_select_order_type();
|
||||
})
|
||||
);
|
||||
|
||||
#region Auto Select
|
||||
void auto_select_account() =>
|
||||
auto_select_prop(props.account, _selected_account, accounts, () => model.factory.default_account);
|
||||
|
||||
void auto_select_time_in_force() =>
|
||||
auto_select_prop(props.time_in_force, _selected_time_in_force, time_in_forces, () => model.factory.default_time_in_force(security));
|
||||
|
||||
void auto_select_prop<T>(InlineUserProperty<T> inline_prop, Property<T> prop, IReadOnlyList<T> choices, Func<T> get_default) {
|
||||
if (inline_prop.has_value && choices.Contains(inline_prop.value)) {
|
||||
if (inline_prop.force_override || has_errors(prop.name)) {
|
||||
prop.Value = inline_prop.value;
|
||||
}
|
||||
} else if (has_errors(prop.name)) {
|
||||
prop.Value = get_default();
|
||||
}
|
||||
_security = backing(nameof(security), security_source.ObserveOnDispatcher(), SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
// guarentee that "this.security" will always be set before observers are notified
|
||||
// this is important bc some observers will reference "this.security" in their callbacks
|
||||
// - namely the [SelectorProperty]order_type's "get_default" function,
|
||||
// which may be called in response to its items changing, which are driven by the observable security
|
||||
// (see 'get_default_order_type_vm')
|
||||
security_source = _security.observe_current(true).Replay(1).RefCount();
|
||||
_accounts = backing(
|
||||
nameof(selected_account),
|
||||
model.factory.subscribe_accounts(),
|
||||
order_properties.Select(ops => ops.account),
|
||||
() => string.Empty,
|
||||
val => (!String.IsNullOrWhiteSpace(val) && accounts.Contains(val), "Please select an account"));
|
||||
_time_in_force = backing(
|
||||
nameof(selected_time_in_force),
|
||||
security_source.Select(s => model.factory.default_time_in_forces(s)),
|
||||
order_properties.Select(ops => ops.time_in_force),
|
||||
() => model.factory.default_time_in_force(security),
|
||||
tif => time_in_forces.Contains(tif));
|
||||
_order_type = backing(
|
||||
nameof(selected_order_type),
|
||||
security_source.Select(s => model.factory.default_order_type_defs(s).Select(create_order_type_vm)),
|
||||
order_properties.Select(get_order_type_vm_prop),
|
||||
get_default_order_type_vm,
|
||||
(a, b) => a.order_type == b.order_type);
|
||||
_order_type.Changed += on_selected_order_type_changed;
|
||||
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
|
||||
// local methods
|
||||
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) {
|
||||
var vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_source);
|
||||
vm.load_from(cd);
|
||||
return vm;
|
||||
}
|
||||
|
||||
void auto_select_order_type() {
|
||||
bool invalid = has_errors(nameof(selected_order_type));
|
||||
if (props.order_type_def.has_value) {
|
||||
if (invalid) {
|
||||
if (try_select_type(props.order_type_def.value.CustomCase)) {
|
||||
selected_order_type.load_from(props.order_type_def.value);
|
||||
} else {
|
||||
select_default_value();
|
||||
}
|
||||
} else if (props.order_type_def.force_override) {
|
||||
try_select_type(props.order_type_def.value.CustomCase);
|
||||
}
|
||||
} else if (invalid) {
|
||||
select_default_value();
|
||||
}
|
||||
|
||||
void select_default_value() {
|
||||
var ot = model.factory.default_order_type(security);
|
||||
if (!try_select_type(ot))
|
||||
selected_order_type = order_types.FirstOrDefault();
|
||||
}
|
||||
|
||||
bool try_select_type(CustomOrderDefinition.CustomOneofCase type) {
|
||||
if (order_types.TryGetFirst(ot => ot.order_type == type, out IOrderTypeVM result)) {
|
||||
selected_order_type = result;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public event Action<SecurityImmutable> security_changed;
|
||||
event Action<IOrderTypeVM> _selected_order_type_changed;
|
||||
|
||||
public SecurityImmutable security { get; private set; } = SecurityDataHelper.Defaults.SecurityImmutable;
|
||||
public string[] accounts { get; }
|
||||
public string selected_account { get => _selected_account.Value; set => _selected_account.Value = value; }
|
||||
public ObservableCollection<TimeInForce> time_in_forces { get; } = new ObservableCollection<TimeInForce>();
|
||||
public TimeInForce selected_time_in_force { get => _selected_time_in_force.Value; set => _selected_time_in_force.Value = value; }
|
||||
public ObservableAutoWrapper<IOrderTypeVM, CustomOrderDefinition.CustomOneofCase> order_types { get; }
|
||||
public IOrderTypeVM selected_order_type {
|
||||
get => _selected_order_type;
|
||||
set {
|
||||
Disposable.dispose(ref _order_type_param_errors_sub);
|
||||
_selected_order_type = value;
|
||||
if (value == null) {
|
||||
IOrderTypeVM get_default_order_type_vm() {
|
||||
var ot = model.factory.default_order_type(security);
|
||||
if (order_types.TryGetFirst(vm => vm.order_type == ot, out IOrderTypeVM result))
|
||||
return result;
|
||||
return order_types.FirstOrDefault();
|
||||
}
|
||||
void on_selected_order_type_changed(IOrderTypeVM old_val, IOrderTypeVM new_val) {
|
||||
if (new_val == null) {
|
||||
assert(false);
|
||||
} else {
|
||||
assert(order_types.Contains(value));
|
||||
value.init();
|
||||
if (value is INotifyDataErrorInfo ndei) {
|
||||
assert(order_types.Contains(new_val));
|
||||
new_val.init();
|
||||
if (new_val is INotifyDataErrorInfo ndei) {
|
||||
assert_no_param_errors();
|
||||
_order_type_param_errors_sub = ndei.subscribe_errors_changed((s, e) => assert_no_param_errors());
|
||||
void assert_no_param_errors() => assert(!ndei.HasErrors, "order type pararmeters invalid.");
|
||||
}
|
||||
}
|
||||
RaisePropertyChanged();
|
||||
_selected_order_type_changed?.Invoke(_selected_order_type);
|
||||
}
|
||||
}
|
||||
public ICommand set_price_command { get; }
|
||||
|
||||
public SecurityImmutable security => _security.Value;
|
||||
public ReadOnlyObservableCollection<string> accounts => _accounts.items;
|
||||
public string 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<string> observe_account(bool start_immediately = false) =>
|
||||
_selected_account.observe_current(start_immediately);
|
||||
_accounts.observe_current(start_immediately);
|
||||
|
||||
public IObservable<TimeInForce> observe_time_in_force(bool start_immediately = false) =>
|
||||
_selected_time_in_force.observe_current(start_immediately);
|
||||
_time_in_force.observe_current(start_immediately);
|
||||
|
||||
public IObservable<IOrderTypeVM> observe_order_type(bool start_immediately = false) {
|
||||
var source = Observable.FromEvent<Action<IOrderTypeVM>, IOrderTypeVM>(cb => _selected_order_type_changed += cb, cb => _selected_order_type_changed -= cb);
|
||||
if (start_immediately)
|
||||
source = source.StartWith(_selected_order_type);
|
||||
return source;
|
||||
}
|
||||
public IObservable<IOrderTypeVM> observe_order_type(bool start_immediately = false) =>
|
||||
_order_type.observe_current(start_immediately);
|
||||
|
||||
public OrderDefinition create(Side side) {
|
||||
if (HasErrors) throw new InvalidOperationException("Cannot create order when errors are present.");
|
||||
var order = _model.factory.order(security);
|
||||
order.Account = selected_account;
|
||||
order.AccountId = _model.factory.get_account_id(selected_account);
|
||||
order.TimeInForce = selected_time_in_force;
|
||||
order.Side = side;
|
||||
_selected_order_type.assign_to(order.CustomDef);
|
||||
selected_order_type.assign_to(order.CustomDef);
|
||||
return order;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
base.Dispose(disposing);
|
||||
Disposable.dispose(ref _order_type_param_errors_sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,89 @@
|
||||
<sys:Double x:Key="HorizontalSpacing">8.0</sys:Double>
|
||||
<sys:Double x:Key="VerticalSpacing">4.0</sys:Double>
|
||||
<sys:Double x:Key="ButtonHeight">22.0</sys:Double>
|
||||
</ResourceDictionary>
|
||||
<Style x:Key="SellButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.ask}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="MinHeight" Value="40" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center" Foreground="{u:BTSColor UI.alternate_text}">Sell</TextBlock>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
CornerRadius="0"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{u:BTSColor MarketData.ask}"
|
||||
Padding="2"
|
||||
Height="Auto"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content"
|
||||
/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="#99c00000" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{u:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="BuyButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{u:BTSColor MarketData.bid}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="MinHeight" Value="40" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center" Foreground="{u:BTSColor UI.alternate_text}">Buy</TextBlock>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
CornerRadius="0"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{u:BTSColor UI.Button.normal_border}"
|
||||
Background="{u:BTSColor MarketData.bid}"
|
||||
Padding="2"
|
||||
Height="Auto"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content"
|
||||
/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="#99167ed3" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{u:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -31,9 +113,9 @@
|
||||
<ColumnDefinition MinWidth="120"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<usp:OrderModificationControl Grid.ColumnSpan="3" />
|
||||
<Button Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="40" Background="{u:BTSColor MarketData.bid}" Foreground="White" FontWeight="Bold" Command="{Binding send_order}" CommandParameter="{u:Enum Buy, Type=msys:Side}">Buy</Button>
|
||||
<Button Grid.Row="2" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="40" Background="{u:BTSColor MarketData.ask}" Foreground="White" FontWeight="Bold" Command="{Binding send_order}" CommandParameter="{u:Enum Sell, Type=msys:Side}">Sell</Button>
|
||||
</Grid>
|
||||
<Button Grid.Row="2" Style="{StaticResource BuyButton}" Command="{Binding send_order}" CommandParameter="{u:Enum Buy, Type=msys:Side}" />
|
||||
<Button Grid.Row="2" Grid.Column="2" Style="{StaticResource SellButton}" Command="{Binding send_order}" CommandParameter="{u:Enum Sell, Type=msys:Side}" />
|
||||
</Grid>
|
||||
<u:WindowStyle.TitleContentTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock>
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace mbts.spark {
|
||||
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);
|
||||
show_quantity = new InlineUserProperty<double>(0, true);
|
||||
show_quantity = new InlineUserProperty<double>(1, true);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
|
||||
@@ -27,12 +27,16 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
class TimeBoxOrderVM : OrderTypeVMBase<TimeBoxOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
|
||||
const double _start_immediately_delay_ms = 500;
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineTimeBoxOrderProperties _inline_props;
|
||||
TimeTextTuple _start = TimeTextTuple.FromDateTime(DateTime.Now), _stop = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(5));
|
||||
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);
|
||||
StartMode _start_mode = StartMode.In;
|
||||
StopMode _stop_mode = StopMode.After;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
@@ -57,12 +61,12 @@ namespace mbts.spark {
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (p.start.force_override) {
|
||||
start = TimeTextTuple.FromDateTime(p.start.value);
|
||||
RaisePropertyChanged(nameof(start));
|
||||
absolute_start = TimeTextTuple.FromDateTime(p.start.value);
|
||||
RaisePropertyChanged(nameof(absolute_start));
|
||||
}
|
||||
if (p.stop.force_override) {
|
||||
stop = TimeTextTuple.FromDateTime(p.stop.value);
|
||||
RaisePropertyChanged(nameof(stop));
|
||||
absolute_stop = TimeTextTuple.FromDateTime(p.stop.value);
|
||||
RaisePropertyChanged(nameof(absolute_stop));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
@@ -71,6 +75,22 @@ namespace mbts.spark {
|
||||
_validate();
|
||||
}
|
||||
|
||||
public enum StartMode {
|
||||
At,
|
||||
In,
|
||||
[Description("Immediately")]
|
||||
Now
|
||||
}
|
||||
public enum StopMode {
|
||||
At,
|
||||
After,
|
||||
[Description("When Completed")]
|
||||
WhenComleted
|
||||
}
|
||||
|
||||
public IEnumerable<StartMode> start_modes => EnumHelper.get_values<StartMode>();
|
||||
public IEnumerable<StopMode> stop_modes => EnumHelper.get_values<StopMode>();
|
||||
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null) {
|
||||
@@ -100,30 +120,40 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public TimeTextTuple start {
|
||||
get => _start;
|
||||
public StartMode start_mode {
|
||||
get => _start_mode;
|
||||
set {
|
||||
_start = value;
|
||||
_start_mode = value;
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(show_absolute_start), nameof(show_relative_start));
|
||||
}
|
||||
}
|
||||
public TimeTextTuple stop {
|
||||
get => _stop;
|
||||
public StopMode stop_mode {
|
||||
get => _stop_mode;
|
||||
set {
|
||||
_stop = value;
|
||||
_stop_mode = value;
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(show_absolute_stop), nameof(show_relative_stop));
|
||||
}
|
||||
}
|
||||
|
||||
public TimeTextTuple absolute_start { get => _absolute_start; set { _absolute_start = value; _validate(); } }
|
||||
public TimeTextTuple absolute_stop { get => _absolute_stop; set { _absolute_stop = value; _validate(); } }
|
||||
public TimeSpan? relative_start { get => _relative_start; set { _relative_start = value; _validate(); } }
|
||||
public TimeSpan? relative_stop { get => _relative_stop; set { _relative_stop = value; _validate(); } }
|
||||
public bool show_absolute_start => _start_mode == StartMode.At;
|
||||
public bool show_relative_start => _start_mode == StartMode.In;
|
||||
public bool show_absolute_stop => _stop_mode == StopMode.At;
|
||||
public bool show_relative_stop => _stop_mode == StopMode.After;
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value) {
|
||||
if (_child_order == null)
|
||||
_select_child_order(model.factory.default_order_type(_security.Value, true));
|
||||
_validate();
|
||||
var (start_time, stop_time) = _validate();
|
||||
if (!HasErrors) {
|
||||
DateTime? start_result = start.result, stop_result = stop.result;
|
||||
var tb = factory.time_box_order();
|
||||
tb.Start = start.result.Value.ToUniversalTime().ToGoogleTime();
|
||||
tb.Stop = stop.result.Value.ToUniversalTime().ToGoogleTime();
|
||||
tb.Start = start_time.Value.ToUniversalTime().ToGoogleTime();
|
||||
tb.Stop = stop_time.Value.ToUniversalTime().ToGoogleTime();
|
||||
child_order.assign_to(tb.Child);
|
||||
value.TimeBox = tb;
|
||||
}
|
||||
@@ -155,19 +185,57 @@ namespace mbts.spark {
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
|
||||
void _validate() {
|
||||
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
|
||||
set_error(nameof(child_order), string.Empty, true);
|
||||
else clear_error(nameof(child_order));
|
||||
if (!start.result.HasValue)
|
||||
set_error(nameof(start), string.Empty, true);
|
||||
else clear_error(nameof(start));
|
||||
if (!stop.result.HasValue)
|
||||
set_error(nameof(stop), string.Empty, true);
|
||||
else clear_error(nameof(stop));
|
||||
if (!HasErrors) {
|
||||
if (start.result.Value > stop.result.Value)
|
||||
set_error(nameof(stop), string.Empty, true);
|
||||
(DateTime? start_time, DateTime? stop_time) _validate() {
|
||||
assert(_child_order != null && !(_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors), nameof(child_order));
|
||||
// ti.start_name is null if mode is Now, stop_name if mode is WhenCompleted
|
||||
var now = DateTime.Now;
|
||||
var ti = get_time_info();
|
||||
if (ti.start_name != null) {
|
||||
if (!ti.start_time.HasValue || ti.start_time.Value < now)
|
||||
set_error(ti.start_name, string.Empty, false);
|
||||
else clear_error(ti.start_name);
|
||||
}
|
||||
if (ti.stop_name != null) {
|
||||
if (!ti.stop_time.HasValue || ti.stop_time.Value <= now || ti.start_time.HasValue && ti.start_time.Value >= ti.stop_time.Value)
|
||||
set_error(ti.stop_name, string.Empty, false);
|
||||
else clear_error(ti.stop_name);
|
||||
}
|
||||
return (ti.start_time, ti.stop_time);
|
||||
//
|
||||
(DateTime? start_time, string start_name, DateTime? stop_time, string stop_name) get_time_info() {
|
||||
DateTime? start_time = null, stop_time = null;
|
||||
string start_name = null, stop_name = null;
|
||||
switch (_start_mode) {
|
||||
case StartMode.At:
|
||||
start_time = _absolute_start.result;
|
||||
start_name = nameof(absolute_start);
|
||||
break;
|
||||
case StartMode.In:
|
||||
if (_relative_start.HasValue)
|
||||
start_time = now + _relative_start.Value;
|
||||
start_name = nameof(relative_start);
|
||||
break;
|
||||
case StartMode.Now:
|
||||
default:
|
||||
start_time = now.AddMilliseconds(_start_immediately_delay_ms);
|
||||
break;
|
||||
}
|
||||
switch (_stop_mode) {
|
||||
case StopMode.At:
|
||||
stop_time = _absolute_stop.result;
|
||||
stop_name = nameof(absolute_stop);
|
||||
break;
|
||||
case StopMode.After:
|
||||
if (_relative_stop.HasValue)
|
||||
stop_time = (start_time ?? now) + _relative_stop;
|
||||
stop_name = nameof(relative_stop);
|
||||
break;
|
||||
case StopMode.WhenComleted:
|
||||
default:
|
||||
stop_time = DateTime.MaxValue;
|
||||
break;
|
||||
}
|
||||
return (start_time, start_name, stop_time, stop_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -22,7 +21,7 @@ namespace mbts.spark {
|
||||
readonly SpreaderModel _spreader;
|
||||
readonly ErrorReporter _error;
|
||||
readonly OrderActivitySummarizer _activity;
|
||||
readonly UpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_demuxer;
|
||||
readonly TableUpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_demuxer;
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
@@ -30,12 +29,18 @@ namespace mbts.spark {
|
||||
_bus = bus;
|
||||
_factory = factory;
|
||||
_service = service;
|
||||
on_orders = _service.subscribei_orders().Table();
|
||||
_spreader = spreader;
|
||||
_error = error;
|
||||
on_orders = _service.subscribei_orders().Table().Replay(1).RefCount();
|
||||
IReadOnlyDictionary<uint, OrderRecordImmutable> empty_orders = new Dictionary<uint, OrderRecordImmutable>();
|
||||
working_orders =
|
||||
on_orders.Scan(empty_orders, (worders, table) => _update_working_orders(worders, table)).Replay(1).RefCount();
|
||||
on_fills = _service.subscribei_fills().Table().Replay(1).RefCount();
|
||||
_activity = new OrderActivitySummarizer(bus, on_orders);
|
||||
on_fill_tuples = FillTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders, on_fills);
|
||||
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
|
||||
_dispose = Disposable.Create(
|
||||
_security_orders_demuxer = new UpdateDemuxer<OrderRecordImmutable, uint, uint>(service.subscribei_orders(), o => o.Key, o => o.Def.SecurityKey),
|
||||
on_orders.Subscribe(_security_orders_demuxer),
|
||||
_bus.subscribe(SparkTags.CreateOrder).Subscribe(a => send_order(a.definition, a.price, a.size).ignore()),
|
||||
_bus.subscribe(SparkTags.CancelOrders).Subscribe(x => cancel_orders(x).ignore()),
|
||||
_bus.subscribe(SparkTags.CrackOrders).Subscribe(x => crack_orders(x).ignore()),
|
||||
@@ -46,18 +51,13 @@ namespace mbts.spark {
|
||||
#region Output
|
||||
public OrderFactory factory => _factory;
|
||||
public SpreaderModel spreader => _spreader;
|
||||
|
||||
public IObservable<TableUpdate<OrderRecordImmutable, uint>> on_orders { get; }
|
||||
public IObservable<TableUpdate<FillImmutable, string>> on_fills { get; }
|
||||
public IObservable<ITableUpdateTransform<FillTuple, string>> on_fill_tuples { get; }
|
||||
public IObservable<IReadOnlyDictionary<uint,OrderRecordImmutable>> working_orders { get; }
|
||||
|
||||
public IObservable<TableUpdate<OrderRecordImmutable, uint>> on_security_orders(uint security) =>
|
||||
_security_orders_demuxer.subscribe(security).Table();
|
||||
|
||||
public IObservable<TableUpdate<FillTuple, string>> on_fill_tuples =>
|
||||
_on_fill_tuples();
|
||||
|
||||
public IObservable<TableUpdate<FillTuple, string>> on_order_fill_tuples(IReadOnlyCollection<uint> order_keys) =>
|
||||
_on_order_fill_tuples(_on_key_orders(order_keys));
|
||||
|
||||
public IObservable<ITableUpdateTransform<OrderRecordImmutable, uint>> on_security_orders(uint security) =>
|
||||
_security_orders_demuxer.subscribe(security);
|
||||
#endregion
|
||||
|
||||
#region Input
|
||||
@@ -118,41 +118,18 @@ namespace mbts.spark {
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
|
||||
#region Private Methods
|
||||
IObservable<Update<OrderRecordImmutable, uint>> _on_key_orders(IReadOnlyCollection<uint> order_keys) =>
|
||||
_service.subscribei_orders().Select(u => u.Filter(order_keys));
|
||||
|
||||
IObservable<TableUpdate<FillTuple, string>> _on_fill_tuples() => _on_fill_tuples(
|
||||
_service.subscribei_fills()
|
||||
.CombineLatest(on_orders, _bus.subscribe(SparkTags.SecuritiesTag), (fills, orders, md_map) => (
|
||||
_get_fill_tuples(fills.removed, orders.state, md_map),
|
||||
_get_fill_tuples(fills.added, orders.state, md_map)
|
||||
)));
|
||||
|
||||
IObservable<TableUpdate<FillTuple, string>> _on_order_fill_tuples(IObservable<Update<OrderRecordImmutable, uint>> orders_source) => _on_fill_tuples(
|
||||
_service.subscribei_fills()
|
||||
.CombineLatest(orders_source.Table(), _bus.subscribe(SparkTags.SecuritiesTag), (fills, orders, md_map) => {
|
||||
fills = fills.Filter(f => orders.state.ContainsKey(f.OrderKey));
|
||||
return (
|
||||
_get_fill_tuples(fills.removed, orders.state, md_map),
|
||||
_get_fill_tuples(fills.added, orders.state, md_map)
|
||||
);
|
||||
}));
|
||||
|
||||
IObservable<TableUpdate<FillTuple, string>> _on_fill_tuples(IObservable<(IEnumerable<FillTuple> raw_added, IEnumerable<FillTuple> raw_removes)> source) => source
|
||||
.PairWithPrevious((Enumerable.Empty<FillTuple>(), Enumerable.Empty<FillTuple>()))
|
||||
.Select(vals => (
|
||||
vals.curr.Item1.Except(vals.prev.Item1).ToReadOnlyList(),
|
||||
vals.curr.Item2.Except(vals.prev.Item2).ToReadOnlyList()
|
||||
)).Where(vals => vals.Item1.Count > 0 || vals.Item2.Count > 0)
|
||||
.Table(f => f.key);
|
||||
|
||||
static IEnumerable<FillTuple> _get_fill_tuples(IEnumerable<FillImmutable> fills, IReadOnlyDictionary<uint, OrderRecordImmutable> orders, IReadOnlyDictionary<uint, SecurityMD> md_map) =>
|
||||
fills.Select(f => new FillTuple(f,
|
||||
orders.TryGetValue(f.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(),
|
||||
md_map.TryGetValue(f.SecurityKey, out SecurityMD smd) ? smd.security : new SecurityImmutable?()
|
||||
));
|
||||
#endregion
|
||||
static IReadOnlyDictionary<uint, OrderRecordImmutable> _update_working_orders(IReadOnlyDictionary<uint, OrderRecordImmutable> target, TableUpdate<OrderRecordImmutable, uint> table) {
|
||||
var clone = new Dictionary<uint, OrderRecordImmutable>((IDictionary<uint, OrderRecordImmutable>)target);
|
||||
foreach (var o in table.removed) {
|
||||
if (o.is_active())
|
||||
clone.Remove(o.Key);
|
||||
}
|
||||
foreach (var o in table.added) {
|
||||
if (o.is_active())
|
||||
clone.Add(o.Key, o);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
// usinig new OrdersModel extensions to reduce the amount of code change needed to switch uis over to the new OrdersModel,
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<StartupObject>mbts.ui.shell.App</StartupObject>
|
||||
<RootNamespace>bts</RootNamespace>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../spark_api/spark_api.csproj" />
|
||||
@@ -34,7 +35,7 @@
|
||||
<PackageReference Include="IronPython" Version="2.7.7" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.8.0" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
|
||||
<PackageReference Include="System.Reactive" Version="3.1.1" />
|
||||
<PackageReference Include="protobuf-net" Version="2.3.2" />
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
using mbts.collections;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.tests {
|
||||
[TestFixture]
|
||||
class SortedSetMapTests {
|
||||
class TestParams {
|
||||
public readonly IComparer<int> key_comparer = Comparer<int>.Default;
|
||||
public readonly IEqualityComparer<int> value_eq_comparer = EqualityComparer<int>.Default;
|
||||
public readonly Dictionary<int, int[]> unsorted_key_vals = new Dictionary<int, int[]> {
|
||||
{ 3, new[] { 1, 4, 2, 5, 4, 2, 7 } },
|
||||
{ 5, new[] { 7, 9, 3, 5, 1 } },
|
||||
{ 4, new[] { 5, 5, 1, 2, 8, 6 } },
|
||||
{ 1, new[] { 4, 3, 2, 1 } },
|
||||
{ 2, new[] { 8, 8, 8, 8 } }
|
||||
};
|
||||
public readonly List<int> sorted_keys;
|
||||
|
||||
public TestParams() {
|
||||
sorted_keys = new List<int>(unsorted_key_vals.Keys);
|
||||
sorted_keys.Sort(key_comparer);
|
||||
}
|
||||
|
||||
public SortedSetMap<int, int> get_populated_sorted_set_map() {
|
||||
var ssm = new SortedSetMap<int, int>(Comparer<int>.Default, EqualityComparer<int>.Default);
|
||||
foreach (var kv in unsorted_key_vals) {
|
||||
foreach (int val in kv.Value)
|
||||
ssm.add(kv.Key, val);
|
||||
}
|
||||
return ssm;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void test_add_values() {
|
||||
var tps = new TestParams();
|
||||
var ssm = new SortedSetMap<int, int>(tps.key_comparer, tps.value_eq_comparer);
|
||||
foreach (var kv in tps.unsorted_key_vals) {
|
||||
foreach (int val in kv.Value)
|
||||
ssm.add(kv.Key, val);
|
||||
}
|
||||
|
||||
Assert.That(ssm.Count == tps.unsorted_key_vals.Count, () => $"Count is incorrect: expected({tps.unsorted_key_vals.Count}), actual({ssm.Count}).");
|
||||
|
||||
// test key-sorting, validate corresponding value-sets
|
||||
for (int i = 0; i < tps.unsorted_key_vals.Count; i++) {
|
||||
int expected_key = tps.sorted_keys[i];
|
||||
var actual_kv = ssm.at_index(i);
|
||||
Assert.That(expected_key == actual_kv.Key, "Keys are not sorted correctly.");
|
||||
var expected_vals = new HashSet<int>(tps.unsorted_key_vals[expected_key]);
|
||||
Assert.That(actual_kv.SetEquals(expected_vals), () => $"Value set is incorrect: expected({string.Join(", ", expected_vals)}), actual({string.Join(", ", actual_kv)}).");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void test_remove_value() {
|
||||
var tps = new TestParams();
|
||||
var ssm = tps.get_populated_sorted_set_map();
|
||||
// test remove value
|
||||
int middle_key = tps.sorted_keys[(int)(0.5 * tps.sorted_keys.Count)];
|
||||
int[] middle_values = tps.unsorted_key_vals[middle_key];
|
||||
int middle_middle_value = middle_values[(int)(0.5 * middle_values.Length)];
|
||||
// this should not remove a key-node, just a value from the value-set
|
||||
int pre_remove_count = ssm.Count;
|
||||
var pre_remove_kv = ssm[middle_key];
|
||||
Assert.That(ssm.remove(middle_key, middle_middle_value), "Failed to remove value.");
|
||||
Assert.That(ssm.Count == pre_remove_count, "Key-node should not have been removed. ");
|
||||
var post_remove_kv = ssm[middle_key];
|
||||
Assert.That(ReferenceEquals(pre_remove_kv, post_remove_kv), "Key-value-set should remain the same object.");
|
||||
Assert.That(!post_remove_kv.Contains(middle_middle_value), "Value was not removed from set.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void test_remove_key() {
|
||||
var tps = new TestParams();
|
||||
var ssm = tps.get_populated_sorted_set_map();
|
||||
int middle_key = tps.sorted_keys[(int)(0.5 * tps.sorted_keys.Count)];
|
||||
int pre_remove_count = ssm.Count;
|
||||
var pre_remove_kv = ssm[middle_key];
|
||||
Assert.That(ssm.remove(middle_key), "Failed to remove key.");
|
||||
Assert.That(ssm.Count == pre_remove_count - 1, "Count was not decremented.");
|
||||
Assert.That(!ssm.contains_key(middle_key), "Still contains key after it was supposedly removed.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void test_resize() {
|
||||
const int initial_capacity = 4, ultimate_length = 10;
|
||||
var ssm = new SortedSetMap<int, int>(initial_capacity);
|
||||
for (int i = 0; i < ultimate_length; i++)
|
||||
ssm.add(i, 1);
|
||||
Assert.That(ssm.Count == ultimate_length, $"Count should be {ultimate_length}, actually {ssm.Count}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void test_copy() {
|
||||
var tps = new TestParams();
|
||||
var ssm = tps.get_populated_sorted_set_map();
|
||||
// test CopyTo
|
||||
var dest_array = new IGroupingSet<int, int>[ssm.Count];
|
||||
ssm.CopyTo(dest_array, 0);
|
||||
for (int i = 0; i < dest_array.Length; i++) {
|
||||
var dkv = dest_array[i];
|
||||
var kv = ssm.at_index(i);
|
||||
Assert.That(kv.Key == dkv.Key, () => $"CopyTo destination array key doesn't match: expectd({dkv.Key}), actual({kv.Key}).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ namespace mbts.ui.tests
|
||||
public event Action TitleChanged = () => { };
|
||||
public System.Windows.Media.ImageSource Icon { get { return null; } }
|
||||
public event Action IconChanged = () => { };
|
||||
public string window_type { get; }
|
||||
public IShellModule Owner { get; set; }
|
||||
public void GetState(System.IO.Stream save_here) { }
|
||||
#endregion
|
||||
|
||||
@@ -74,20 +74,19 @@ namespace mbts.ui.tests
|
||||
Application.Current.Dispatcher.Invoke(() => {
|
||||
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_windows_disposed");
|
||||
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
|
||||
ObservableCollection<IShellWindow> windows = new ObservableCollection<IShellWindow>();
|
||||
var thread_factory = new ShellThreadFactory();
|
||||
IWindowManager wm = new WindowManager(windows, thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
|
||||
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
|
||||
Assert.IsNotNull(Application.Current);
|
||||
Assert.IsNotNull(Application.Current.Windows);
|
||||
Assert.AreEqual(Application.Current.Windows.Count, 0);
|
||||
|
||||
TestShellWindow test_window = new TestShellWindow();
|
||||
wm.Windows.Add(test_window);
|
||||
wm.add(test_window);
|
||||
// Check that no new dispatcher was created for this window
|
||||
Assert.AreEqual(Dispatcher.CurrentDispatcher, test_window.Dispatcher);
|
||||
Assert.AreEqual(Application.Current.Windows.Count, 1);
|
||||
|
||||
wm.Windows.Clear();
|
||||
wm.clear();
|
||||
Assert.AreEqual(Application.Current.Windows.Count, 0);
|
||||
Assert.IsTrue(test_window.IsDisposed);
|
||||
thread_factory.emergency_shutdown();
|
||||
@@ -100,19 +99,18 @@ namespace mbts.ui.tests
|
||||
Application.Current.Dispatcher.Invoke(() => {
|
||||
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_slow_windows_disposed");
|
||||
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
|
||||
ObservableCollection<IShellWindow> windows = new ObservableCollection<IShellWindow>();
|
||||
var thread_factory = new ShellThreadFactory();
|
||||
IWindowManager wm = new WindowManager(windows, thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
|
||||
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
|
||||
Assert.AreEqual(Application.Current.Windows.Count, 0);
|
||||
|
||||
// an ISlowWindow gets its own thread
|
||||
var slow_window = new TestSlowWindow();
|
||||
Assert.IsTrue(slow_window is ISlowWindow);
|
||||
wm.Windows.Add(slow_window);
|
||||
wm.add(slow_window);
|
||||
Assert.AreNotEqual(Dispatcher.CurrentDispatcher, slow_window.Dispatcher);
|
||||
Assert.AreEqual(Application.Current.Windows.Count, 1);
|
||||
|
||||
wm.Windows.Clear();
|
||||
wm.clear();
|
||||
Assert.AreEqual(Application.Current.Windows.Count, 0);
|
||||
DateTime now = DateTime.UtcNow;
|
||||
while (!slow_window.IsDisposed && (DateTime.UtcNow - now) < TimeSpan.FromSeconds(120)) { Thread.Sleep(100); }
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<ResourceDictionary Source="/view/Controls/DragDropItemsControl/DraggableContentControl.xaml" />
|
||||
<ResourceDictionary Source="/view/Controls/DragDropItemsControl/DragDropListBox.xaml" />
|
||||
<ResourceDictionary Source="/view/Controls/DragDropItemsControl/DraggableListBoxItem.xaml" />
|
||||
<ResourceDictionary Source="/view/Controls/DialogSkirts/DialogSkirt.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/DialogSkirts/DialogSkirtOKCancel.xaml" />
|
||||
<ResourceDictionary Source="/view/Controls/DialogSkirts/DialogSkirtOKApplyCancel.xaml" />
|
||||
<ResourceDictionary Source="/view/Controls/ComboCheckBox/ComboCheckBox.xaml" />
|
||||
@@ -165,9 +166,9 @@
|
||||
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Popup
|
||||
x:Name="PART_Popup" Grid.ColumnSpan="2" Margin="1" Placement="Bottom" AllowsTransparency="true"
|
||||
x:Name="PART_Popup" Grid.ColumnSpan="2" Margin="1" Placement="Bottom" AllowsTransparency="false"
|
||||
IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
|
||||
PopupAnimation="None"
|
||||
>
|
||||
<Border
|
||||
x:Name="dropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
|
||||
@@ -33,6 +33,10 @@ namespace bts.spark
|
||||
|
||||
public static PartialCommand<SecurityMD> ShowLadder =>
|
||||
SparkCommand.create("Ladder", Icons.Ladder, SparkTags.ShowLadderTag);
|
||||
|
||||
public static PartialCommand<IReadOnlyList<SecurityMD>> ShowLadders =>
|
||||
SparkCommand.create("Ladder", Icons.Ladder, SparkTags.ShowLaddersTag);
|
||||
|
||||
public static PartialCommand<IObservable<SecurityMD>> ShowLinkedLadder =>
|
||||
SparkCommand.create("Linked Ladder", Icons.Ladder, SparkTags.ShowLinkedLadder);
|
||||
public static SparkCommand PickLadder =>
|
||||
|
||||
@@ -48,7 +48,11 @@ namespace mbts.spark
|
||||
|
||||
#region Spark Window
|
||||
public static ITag<FillsWindowFactory.StartupData> ShowFillsTag { get; } = new SparkTag<FillsWindowFactory.StartupData>(nameof(ShowFillsTag));
|
||||
// TODO: maybe get rid of the singlular ShowLadderTag, and just use the plural ShowLaddersTag.
|
||||
// The owners of the various SparkWindows may remove dependencies on the former at their leasure.
|
||||
// For now, both tags go through the same logic in the LadderWindowFactory anyways.
|
||||
public static ITag<SecurityMD> ShowLadderTag { get; } = new SparkTag<SecurityMD>("Show Ladder");
|
||||
public static ITag<IReadOnlyList<SecurityMD>> ShowLaddersTag { get; } = new SparkTag<IReadOnlyList<SecurityMD>>(nameof(ShowLaddersTag));
|
||||
public static ITag<IObservable<SecurityMD>> ShowLinkedLadder { get; } = new SparkTag<IObservable<SecurityMD>>("Linked Ladder");
|
||||
public static ITag<SecurityCriteria> ShowMarketViewTag { get; } = new SparkTag<SecurityCriteria>("Show MarketView");
|
||||
public static ITag<OrderBookWindowFactory.OrderBookStartupData> ShowOrderBookTag { get; } = new SparkTag<OrderBookWindowFactory.OrderBookStartupData>(nameof(ShowOrderBookTag));
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,80 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
class ObservableAggregateCollection<T> : ObservableCollection<T> {
|
||||
readonly List<ObservableCollection<T>> _collections = new List<ObservableCollection<T>>();
|
||||
|
||||
public ObservableAggregateCollection(params ObservableCollection<T>[] collections) {
|
||||
foreach (var c in collections)
|
||||
add_collection(c);
|
||||
}
|
||||
|
||||
public void add_collection(ObservableCollection<T> collection) {
|
||||
this.AddRange(collection);
|
||||
_collections.Add(collection);
|
||||
collection.CollectionChanged += _on_collection_changed;
|
||||
}
|
||||
|
||||
public void remove_collection(ObservableCollection<T> collection) {
|
||||
_remove_collection_items(collection, out int _, out int collection_index);
|
||||
_collections.RemoveAt(collection_index);
|
||||
collection.CollectionChanged -= _on_collection_changed;
|
||||
}
|
||||
|
||||
public void clear_collections() {
|
||||
foreach (var c in _collections)
|
||||
c.CollectionChanged -= _on_collection_changed;
|
||||
Clear();
|
||||
}
|
||||
|
||||
void _on_collection_changed(object sender, NotifyCollectionChangedEventArgs e) {
|
||||
int offset = _get_collection_start(sender);
|
||||
switch (e.Action) {
|
||||
case NotifyCollectionChangedAction.Add: this.InsertRange(e.NewStartingIndex + offset, e.NewItems); break;
|
||||
case NotifyCollectionChangedAction.Remove: this.RemoveRange(e.OldStartingIndex + offset, e.OldItems.Count); break;
|
||||
case NotifyCollectionChangedAction.Move: Move(e.OldStartingIndex + offset, e.NewStartingIndex + offset); break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
this.RemoveRange(e.OldStartingIndex + offset, e.OldItems.Count);
|
||||
this.InsertRange(e.NewStartingIndex + offset, e.NewItems);
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
var c = (ObservableCollection<T>)sender;
|
||||
_remove_collection_items(c, out int starting_index, out int _);
|
||||
_add_items(c, starting_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _add_items(IList items, int index) {
|
||||
foreach (T item in items)
|
||||
Insert(index++, item);
|
||||
}
|
||||
|
||||
void _remove_items(IList items, int index) {
|
||||
foreach (var item in items)
|
||||
RemoveAt(index);
|
||||
}
|
||||
|
||||
void _remove_collection_items(ObservableCollection<T> collection, out int starting_index, out int collection_index) {
|
||||
int ex_count = 0;
|
||||
int i = 0;
|
||||
for (; i < _collections.Count; i++) {
|
||||
if (_collections[i] == collection)
|
||||
break;
|
||||
else ex_count += _collections[i].Count;
|
||||
}
|
||||
starting_index = ex_count;
|
||||
collection_index = i;
|
||||
for (++i; i < _collections.Count; i++)
|
||||
ex_count += _collections[i].Count;
|
||||
this.RemoveRange(starting_index, Count - ex_count);
|
||||
}
|
||||
|
||||
int _get_collection_start(object sender) =>
|
||||
_collections.TakeWhile(c => c != sender).Sum(c => c.Count);
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.collections {
|
||||
interface IGroupingSet<TKey, TElement> : IGrouping<TKey, TElement>, ISet<TElement> { }
|
||||
|
||||
class SetMap<TKey, TElement> : IEnumerable<IGroupingSet<TKey, TElement>>, ICollection {
|
||||
readonly IEqualityComparer<TKey> _key_comparer;
|
||||
readonly IEqualityComparer<TElement> _element_comparer;
|
||||
readonly Dictionary<TKey, InternalGroupSet> _dictionary;
|
||||
|
||||
public SetMap()
|
||||
: this(EqualityComparer<TKey>.Default) { }
|
||||
|
||||
public SetMap(IEqualityComparer<TKey> key_comparer)
|
||||
: this(key_comparer, EqualityComparer<TElement>.Default) { }
|
||||
|
||||
public SetMap(IEqualityComparer<TKey> key_comparer, IEqualityComparer<TElement> element_comparer)
|
||||
: this(new Dictionary<TKey, InternalGroupSet>(), key_comparer, element_comparer) { }
|
||||
|
||||
SetMap(Dictionary<TKey, InternalGroupSet> dictionary, IEqualityComparer<TKey> key_comparer, IEqualityComparer<TElement> element_comparer) {
|
||||
_dictionary = dictionary;
|
||||
_key_comparer = key_comparer;
|
||||
_element_comparer = element_comparer;
|
||||
}
|
||||
|
||||
public int Count => _dictionary.Count;
|
||||
public object SyncRoot => null;
|
||||
public bool IsSynchronized => false;
|
||||
public ISet<TElement> this[TKey key] => _dictionary[key];
|
||||
|
||||
public void CopyTo(Array array, int index) =>
|
||||
((ICollection)_dictionary).CopyTo(array, index);
|
||||
|
||||
public IEnumerator<IGroupingSet<TKey, TElement>> GetEnumerator() =>
|
||||
_dictionary.Values.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() =>
|
||||
GetEnumerator();
|
||||
|
||||
public bool add(TKey key, TElement value) {
|
||||
if (!_dictionary.TryGetValue(key, out InternalGroupSet values))
|
||||
_dictionary.Add(key, values = new InternalGroupSet(key, _element_comparer));
|
||||
return values.Add(value);
|
||||
}
|
||||
|
||||
public bool remove(TKey key, TElement value) {
|
||||
if (_dictionary.TryGetValue(key, out InternalGroupSet values)) {
|
||||
bool removed = values.Remove(value);
|
||||
if (values.Count == 0)
|
||||
_dictionary.Remove(key);
|
||||
|
||||
return removed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool remove(TKey key) =>
|
||||
_dictionary.Remove(key);
|
||||
|
||||
public void clear() => _dictionary.Clear();
|
||||
|
||||
public bool try_pop(TKey key, out ISet<TElement> value) {
|
||||
if (try_lookup(key, out value)) {
|
||||
remove(key);
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
public bool try_lookup(TKey key, out ISet<TElement> value) {
|
||||
if (_dictionary.TryGetValue(key, out InternalGroupSet result)) {
|
||||
value = result;
|
||||
return true;
|
||||
}
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public SetMap<TKey, TElement> clone() =>
|
||||
new SetMap<TKey, TElement>(
|
||||
_dictionary.ToDictionary(
|
||||
kv => kv.Key,
|
||||
kv => new InternalGroupSet(kv.Key, kv.Value, _element_comparer)
|
||||
), _key_comparer,
|
||||
_element_comparer);
|
||||
|
||||
class InternalGroupSet : HashSet<TElement>, IGroupingSet<TKey, TElement> {
|
||||
public InternalGroupSet(TKey key, IEqualityComparer<TElement> comparer)
|
||||
: base(comparer) => Key = key;
|
||||
|
||||
public InternalGroupSet(TKey key, IEnumerable<TElement> values, IEqualityComparer<TElement> comparer)
|
||||
: base(values, comparer) => Key = key;
|
||||
|
||||
public TKey Key { get; }
|
||||
}
|
||||
}
|
||||
|
||||
static class SetMapExtensions {
|
||||
public static bool removes_key<TKey, TElement>(this SetMap<TKey, TElement> set_map, TKey key, TElement element) {
|
||||
int count_before = set_map.Count;
|
||||
if (set_map.remove(key, element))
|
||||
return set_map.Count < count_before;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool adds_key<TKey, TElement>(this SetMap<TKey, TElement> set_map, TKey key, TElement element) {
|
||||
int count_before = set_map.Count;
|
||||
if (set_map.add(key, element))
|
||||
return set_map.Count > count_before;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.collections {
|
||||
/// <summary>
|
||||
/// Similar to a SortedList, but supports both key and index read-access, the latter is achieved by the at_index() method.
|
||||
/// It also allows for multiple values per key, by internally creating a HashSet<TValue> for each key.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
class SortedSetMap<TKey, TValue> : IEnumerable<IGroupingSet<TKey, TValue>>, ICollection {
|
||||
readonly IComparer<TKey> _key_comparer;
|
||||
readonly IEqualityComparer<TValue> _value_eq_comparer;
|
||||
InternalGroupSet[] _key_values;
|
||||
int _count;
|
||||
|
||||
public SortedSetMap(int capacity = 4)
|
||||
: this(Comparer<TKey>.Default, EqualityComparer<TValue>.Default, capacity) { }
|
||||
|
||||
public SortedSetMap(IComparer<TKey> key_comparer, IEqualityComparer<TValue> value_eq_comparer, int capacity = 4) {
|
||||
_key_comparer = key_comparer;
|
||||
_value_eq_comparer = value_eq_comparer;
|
||||
_key_values = new InternalGroupSet[capacity];
|
||||
}
|
||||
|
||||
public int Count => _count;
|
||||
public bool IsReadOnly => false;
|
||||
public object SyncRoot => null;
|
||||
public bool IsSynchronized => false;
|
||||
public IEnumerator<IGroupingSet<TKey, TValue>> GetEnumerator() {
|
||||
for (int x = 0; x < _count; ++x)
|
||||
yield return _key_values[x];
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public void CopyTo(Array array, int index) => Array.Copy(_key_values, 0, array, index, _count);
|
||||
|
||||
public void add(TKey key, TValue value) {
|
||||
int pos = _binary_search(key);
|
||||
if (pos < 0) {
|
||||
pos = ~pos;
|
||||
_count++;
|
||||
if (_count + 1 >= _key_values.Length)
|
||||
_expand();
|
||||
Array.Copy(_key_values, pos, _key_values, pos + 1, _count - pos);
|
||||
_key_values[pos] = new InternalGroupSet(key, _value_eq_comparer) { value };
|
||||
} else {
|
||||
_key_values[pos].Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool remove(TKey key, TValue value) {
|
||||
int index = _binary_search(key);
|
||||
if (index >= 0 && index <= _count) {
|
||||
var set = _key_values[index];
|
||||
if (set.Remove(value)) {
|
||||
if (set.Count == 0) {
|
||||
_count--;
|
||||
Array.Copy(_key_values, index + 1, _key_values, index, _count - index);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool remove(TKey key) {
|
||||
int index = _binary_search(key);
|
||||
if (index >= 0 && index <= _count) {
|
||||
_count--;
|
||||
Array.Copy(_key_values, index + 1, _key_values, index, _count - index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool contains_key(TKey key) =>
|
||||
_binary_search(key) >= 0;
|
||||
|
||||
public ISet<TValue> this[TKey key] {
|
||||
get {
|
||||
int index = _binary_search(key);
|
||||
if (index < 0)
|
||||
throw new KeyNotFoundException();
|
||||
return _key_values[index];
|
||||
}
|
||||
}
|
||||
|
||||
public IGroupingSet<TKey, TValue> at_index(int index) =>
|
||||
_key_values[index];
|
||||
|
||||
int _binary_search(TKey key) {
|
||||
int low = 0;
|
||||
int high = _count - 1;
|
||||
while (low <= high) {
|
||||
int i = low + ((high - low) >> 1);
|
||||
int c = _key_comparer.Compare(_key_values[i].Key, key);
|
||||
if (c == 0) {
|
||||
return i;
|
||||
} else if (c < 0) {
|
||||
low = i + 1;
|
||||
} else high = i - 1;
|
||||
}
|
||||
return ~low;
|
||||
}
|
||||
|
||||
void _expand() {
|
||||
var expanded_values = new InternalGroupSet[_key_values.Length * 2];
|
||||
Array.Copy(_key_values, expanded_values, _count);
|
||||
_key_values = expanded_values;
|
||||
}
|
||||
|
||||
class InternalGroupSet : HashSet<TValue>, IGroupingSet<TKey, TValue> {
|
||||
public InternalGroupSet(TKey key, IEqualityComparer<TValue> comparer)
|
||||
: base(comparer) => Key = key;
|
||||
|
||||
public InternalGroupSet(TKey key, IEnumerable<TValue> values, IEqualityComparer<TValue> comparer)
|
||||
: base(values, comparer) => Key = key;
|
||||
|
||||
public TKey Key { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -6,21 +6,27 @@ namespace mbts.ui {
|
||||
// Value = "{mbts:BTSColor X.y}"
|
||||
// Some colos are also available as static resources in colors.xaml
|
||||
public static class BTSColors {
|
||||
static Brush _header_brush, _header_text_brush, _header_alternate_brush;
|
||||
|
||||
static Brush _header_brush, _header_text_brush, _header_alternate_brush, _header_error_text_brush;
|
||||
|
||||
static BTSColors() {
|
||||
_header_brush = new SolidColorBrush(header);
|
||||
_header_text_brush = new SolidColorBrush(header_text);
|
||||
_header_alternate_brush = new SolidColorBrush(header_alternate);
|
||||
_header_brush.Freeze();
|
||||
_header_text_brush.Freeze();
|
||||
_header_alternate_brush.Freeze();
|
||||
_init_brush(header, out _header_brush);
|
||||
_init_brush(header_text, out _header_text_brush);
|
||||
_init_brush(header_alternate, out _header_alternate_brush);
|
||||
_init_brush(header_error_text, out _header_error_text_brush);
|
||||
}
|
||||
|
||||
// freezing brushes makes them thread-safe
|
||||
static void _init_brush(BTSColor color, out Brush brush) {
|
||||
brush = color;
|
||||
brush.Freeze();
|
||||
}
|
||||
|
||||
[DisplayInfo(name = "Default Header")]
|
||||
public static BTSColor header { get { return BTSCoreColors.base_blue; } }
|
||||
public static Brush header_brush { get { return _header_brush; } }
|
||||
public static BTSColor header_text { get { return BTSCoreColors.white; } }
|
||||
public static BTSColor header_error_text { get; } = new BTSColor(0xFE, 0x6A, 0x6E);
|
||||
public static Brush header_error_text_brush => _header_error_text_brush;
|
||||
public static BTSColor header_border { get { return new BTSColor(0x63,0x8A,0xCC); } }
|
||||
public static Brush header_text_brush { get { return _header_text_brush; } }
|
||||
public static BTSColor header_alternate { get { return BTSCoreColors.blue_grey; } }
|
||||
@@ -143,15 +149,22 @@ namespace mbts.ui {
|
||||
// 'sent' and 'filled_dark' are used in the ladder log viewer to indicate sent/filled orders (both buys and sells)
|
||||
// 'default_background' and 'filled' seemed too pale against the log viewer's current style
|
||||
// as we restyle the ladder, and perhaps the log viewer, maybe this will change, in the meantime, here they are.
|
||||
static readonly BTSColor _sent = new BTSColor(0x92, 0x5B, 0xBE);
|
||||
static readonly Brush _sent_brush = _sent, _filled_dark_brush = _sent;
|
||||
static readonly BTSColor _sent = new BTSColor(0x92, 0x5B, 0xBE), _working_order_background = new BTSColor(0xffedcdu);
|
||||
|
||||
static Orders() {
|
||||
_init_brush(_sent, out sent_brush);
|
||||
_init_brush(filled_dark, out filled_dark_brush);
|
||||
_init_brush(working_cancel, out working_cancel_brush);
|
||||
_init_brush(working_order_background, out working_order_background_brush);
|
||||
_init_brush(modified, out modified_brush);
|
||||
}
|
||||
|
||||
// Base Colors
|
||||
[DisplayInfo(name = "Orders")]
|
||||
public static BTSColor default_background { get { return BTSCoreColors.blue_light_lighter; } }
|
||||
public static BTSColor default_text { get { return BTSCoreColors.black; } }
|
||||
public static BTSColor sent => _sent;
|
||||
public static Brush sent_brush => _sent_brush;
|
||||
public static readonly Brush sent_brush;
|
||||
|
||||
[DisplayInfo(name = "Price")]
|
||||
public static BTSColor price_background { get { return BTSCoreColors.blue_light_dark; } }
|
||||
@@ -161,11 +174,16 @@ namespace mbts.ui {
|
||||
// Alerts
|
||||
public static BTSColor working_status_summary { get { return BTSCoreColors.green; } }
|
||||
public static BTSColor working_cancel { get { return BTSCoreColors.red; } }
|
||||
public static readonly Brush working_cancel_brush;
|
||||
public static BTSColor working_cancel_text { get { return BTSCoreColors.white; } }
|
||||
public static BTSColor working_order_background => _working_order_background;
|
||||
public static readonly Brush working_order_background_brush;
|
||||
|
||||
public static BTSColor filled { get { return BTSCoreColors.green_light_light_saturated; } }
|
||||
public static BTSColor filled_dark => BTSCoreColors.green; // used in ladder log viewer
|
||||
public static BTSColor modified => orange; // used in the ladder log viewer
|
||||
public static BTSColor filled_dark => BTSCoreColors.green;
|
||||
public static readonly Brush filled_dark_brush; // used in ladder log viewer
|
||||
public static BTSColor modified => orange;
|
||||
public static readonly Brush modified_brush; // used in the ladder log viewer
|
||||
|
||||
[DisplayInfo(name = "Order Buy Text")]
|
||||
public static BTSColor order_buy_text { get { return BTSCoreColors.black; } }
|
||||
@@ -179,6 +197,12 @@ namespace mbts.ui {
|
||||
|
||||
public static BTSColor straddle_theovsmarket_negative { get { return BTSCoreColors.red_light; } }
|
||||
public static BTSColor straddle_theovsmarket_positive { get { return BTSCoreColors.green_light; } }
|
||||
|
||||
// Order Types
|
||||
public static BTSColor tick_order { get { return new BTSColor(31, 4, 57);}}
|
||||
public static BTSColor lean_order { get { return new BTSColor(110, 77, 143);}}
|
||||
public static BTSColor join_order { get { return new BTSColor(15, 1, 29);}}
|
||||
public static BTSColor leader_order { get { return new BTSColor(40, 9, 72);}}
|
||||
}
|
||||
|
||||
public static class AW2 {
|
||||
|
||||
Loaded 100 of 665 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user