Compare commits
463
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbb4dd6e1f | ||
|
|
f977b0fdd0 | ||
|
|
a3968bd35b | ||
|
|
2b94a29963 | ||
|
|
4b8abc2005 | ||
|
|
2627264eea | ||
|
|
2c7186275f | ||
|
|
a4d8cd9dfc | ||
|
|
ed4be2f1dd | ||
|
|
cd6d504f06 | ||
|
|
e68d37f24d | ||
|
|
43cba548f4 | ||
|
|
772910ffe5 | ||
|
|
767179caf7 | ||
|
|
ee4a9150b8 | ||
|
|
58914fa703 | ||
|
|
ba40e7044a | ||
|
|
010d0073fe | ||
|
|
40210ce42e | ||
|
|
59cdafba21 | ||
|
|
fe6226baa5 | ||
|
|
45987c7652 | ||
|
|
a18ce97fb4 | ||
|
|
13d5080bbc | ||
|
|
9f07a36d1d | ||
|
|
bc2e7f428a | ||
|
|
53bfbdd349 | ||
|
|
174eac9a59 | ||
|
|
3775b86d9e | ||
|
|
5e1c6d6209 | ||
|
|
0d0fb036ab | ||
|
|
f8668802ab | ||
|
|
c22a1382a1 | ||
|
|
941a0d91cc | ||
|
|
60456e4225 | ||
|
|
440e28489a | ||
|
|
3da0f4eedc | ||
|
|
497abfb443 | ||
|
|
7b04a7849b | ||
|
|
e14b1e0b07 | ||
|
|
2121ce2841 | ||
|
|
c22e99941e | ||
|
|
3da646dc8a | ||
|
|
fca675d43f | ||
|
|
a30523744d | ||
|
|
80f5117c34 | ||
|
|
f77f73ec36 | ||
|
|
2385cf0d1b | ||
|
|
bc778ad5bb | ||
|
|
acb9c35d3b | ||
|
|
b8473e2632 | ||
|
|
61b4696574 | ||
|
|
91a79a5d96 | ||
|
|
23927edf46 | ||
|
|
6c03e605b8 | ||
|
|
2e201159d8 | ||
|
|
5d56a8e440 | ||
|
|
7097c85555 | ||
|
|
bf4c8e252f | ||
|
|
866cf33cd4 | ||
|
|
4561560b46 | ||
|
|
bbadbf8e16 | ||
|
|
68d8659115 | ||
|
|
dd008962a7 | ||
|
|
e856e7e58f | ||
|
|
634d8e1aeb | ||
|
|
eaa9376577 | ||
|
|
3fb70fd645 | ||
|
|
b793c75aa8 | ||
|
|
cb26b9af39 | ||
|
|
f8059cc87e | ||
|
|
07fe19ecdc | ||
|
|
a292fe87e1 | ||
|
|
03d915b55c | ||
|
|
cc318535e9 | ||
|
|
9d245f3243 | ||
|
|
6d7f95ac8e | ||
|
|
9d9df76f4e | ||
|
|
a850082f44 | ||
|
|
521150a73b | ||
|
|
ed5f3fe740 | ||
|
|
fd6fdd9768 | ||
|
|
73bf104e9f | ||
|
|
24640e65b1 | ||
|
|
eadfc38a69 | ||
|
|
098ee36c3a | ||
|
|
f4ed4d38f1 | ||
|
|
2ed3eaa325 | ||
|
|
3a7a02244b | ||
|
|
79258b9404 | ||
|
|
6376802d04 | ||
|
|
2784e7652d | ||
|
|
5b3925fae0 | ||
|
|
077f6050f3 | ||
|
|
791dd62b67 | ||
|
|
2364cda362 | ||
|
|
aca4365e8d | ||
|
|
cac857a3a7 | ||
|
|
ed38ce958d | ||
|
|
ed4b9b7426 | ||
|
|
e4468f056d | ||
|
|
4b580b407e | ||
|
|
b575612b07 | ||
|
|
b506b971a4 | ||
|
|
c50f322dc1 | ||
|
|
3a61f4d81d | ||
|
|
a2b2de9783 | ||
|
|
b2a6927b4b | ||
|
|
80dec4e1e2 | ||
|
|
77c7802496 | ||
|
|
cec2687085 | ||
|
|
ac118993d5 | ||
|
|
7634ecf730 | ||
|
|
309d1fa287 | ||
|
|
9231a91fb4 | ||
|
|
f6f928a693 | ||
|
|
467216439b | ||
|
|
c63e73ab7c | ||
|
|
74fcacd800 | ||
|
|
c1c46f01ab | ||
|
|
77083a1fa6 | ||
|
|
5303f290b1 | ||
|
|
895871c94b | ||
|
|
3bff50b764 | ||
|
|
86b7802b3b | ||
|
|
4ff9aaf037 | ||
|
|
99f7827e7e | ||
|
|
b67ac5c849 | ||
|
|
0777f01607 | ||
|
|
222dbf5443 | ||
|
|
9434f61030 | ||
|
|
9199594756 | ||
|
|
fc9318a831 | ||
|
|
e3fc28f082 | ||
|
|
9cb7b84f57 | ||
|
|
2689ed806d | ||
|
|
5999e7856f | ||
|
|
96ad67b020 | ||
|
|
47d5b95e13 | ||
|
|
12108e6046 | ||
|
|
021bdb8c23 | ||
|
|
470f6c0988 | ||
|
|
c8b87b4f79 | ||
|
|
9d7e16fbf7 | ||
|
|
5a45d5e12a | ||
|
|
c793ec78a2 | ||
|
|
7b2b7c5854 | ||
|
|
327615c9ac | ||
|
|
8e5d94b5e7 | ||
|
|
bd76a1f260 | ||
|
|
1df2695f50 | ||
|
|
67d33a3315 | ||
|
|
c49cd3a06e | ||
|
|
6c0545f566 | ||
|
|
f0eee365f9 | ||
|
|
274db506a9 | ||
|
|
1e5c56f3a7 | ||
|
|
871c7a131f | ||
|
|
7642d73f39 | ||
|
|
ef69dfa646 | ||
|
|
377be5030f | ||
|
|
896af55e79 | ||
|
|
b9a4a2c148 | ||
|
|
e9f6d096cf | ||
|
|
25d4c54284 | ||
|
|
e4291e1c51 | ||
|
|
5c11708728 | ||
|
|
73a301192f | ||
|
|
d972564d34 | ||
|
|
e66a2d4663 | ||
|
|
f92e13d898 | ||
|
|
3d0698b326 | ||
|
|
1e0ab86a24 | ||
|
|
caa29b9513 | ||
|
|
c793768918 | ||
|
|
0132a0cff6 | ||
|
|
f83b6ee898 | ||
|
|
dc071dfa56 | ||
|
|
fbfd601270 | ||
|
|
9b5aea2458 | ||
|
|
904ef92ece | ||
|
|
74e950d459 | ||
|
|
e99caef2dc | ||
|
|
855e4a6ed8 | ||
|
|
311b956461 | ||
|
|
b3b308cd9d | ||
|
|
a8480a34ba | ||
|
|
3312637b8d | ||
|
|
e6f7a82fb1 | ||
|
|
5b934de69a | ||
|
|
0f78982f57 | ||
|
|
18abbfc502 | ||
|
|
c3ae4c6dc5 | ||
|
|
9b012649cd | ||
|
|
c126091155 | ||
|
|
c2fee56496 | ||
|
|
5461dc77aa | ||
|
|
1b2a1c3fe3 | ||
|
|
a83a4fdd4f | ||
|
|
4d4ee1f57c | ||
|
|
2bec4ae09f | ||
|
|
e3b91909a6 | ||
|
|
116ba3f629 | ||
|
|
0ce6f8e6a2 | ||
|
|
9447cb67d2 | ||
|
|
92dacc5c3c | ||
|
|
766dfe9dd3 | ||
|
|
84ecb54efd | ||
|
|
6ff8855dba | ||
|
|
7f349cfa71 | ||
|
|
948b23addc | ||
|
|
7031bb6fbe | ||
|
|
15a04a1870 | ||
|
|
741c6d360a | ||
|
|
c4f3ca26ef | ||
|
|
3403adc1ff | ||
|
|
410d3aaf20 | ||
|
|
34905f6053 | ||
|
|
6a16d419af | ||
|
|
cd23c1a5a1 | ||
|
|
18793f8cd9 | ||
|
|
c008147674 | ||
|
|
47323c51c3 | ||
|
|
91f58620ac | ||
|
|
91aeccb2ae | ||
|
|
82864a609d | ||
|
|
828b1ca0c2 | ||
|
|
6b740ece39 | ||
|
|
84f18288e0 | ||
|
|
cf9e5862ed | ||
|
|
4ca2b5f4d2 | ||
|
|
da2894512d | ||
|
|
55e9487203 | ||
|
|
19fb08e7ff | ||
|
|
193b1025ad | ||
|
|
8fa2a925cd | ||
|
|
526daa9647 | ||
|
|
43bcc9fe02 | ||
|
|
51d6066420 | ||
|
|
0b11b83eb5 | ||
|
|
a53dfd14b1 | ||
|
|
cc0380d8f1 | ||
|
|
0a01d22e72 | ||
|
|
e9f1c72198 | ||
|
|
14af8823ed | ||
|
|
5900ce7d12 | ||
|
|
33e0a6bb46 | ||
|
|
ca3bc5059e | ||
|
|
bd108dfe35 | ||
|
|
8a96c7441e | ||
|
|
4eea3194fd | ||
|
|
dace332ac5 | ||
|
|
af816bb905 | ||
|
|
195c24ffef | ||
|
|
92ad17aa19 | ||
|
|
eabc4bee4c | ||
|
|
0ed9142cf0 | ||
|
|
1639683b43 | ||
|
|
05afb08c5d | ||
|
|
c674e44c60 | ||
|
|
e104c78471 | ||
|
|
3d8ec63bd7 | ||
|
|
99670c9d28 | ||
|
|
56126e19a7 | ||
|
|
05e8363504 | ||
|
|
884741062b | ||
|
|
63c77966fa | ||
|
|
31745d9f0d | ||
|
|
78897ed608 | ||
|
|
5712bcdc57 | ||
|
|
8e0a027584 | ||
|
|
12f88b5303 | ||
|
|
b119cad8a9 | ||
|
|
f30210e4e1 | ||
|
|
a8d6a104c3 | ||
|
|
13df0c8638 | ||
|
|
fb70aa6514 | ||
|
|
37fd1d424e | ||
|
|
bdbbcb05dc | ||
|
|
9cd5d0890f | ||
|
|
7b1fa849ef | ||
|
|
a8b5cf81ee | ||
|
|
bc62d4b359 | ||
|
|
4413580212 | ||
|
|
8b653ef133 | ||
|
|
b1f8ac7e22 | ||
|
|
a11c23d718 | ||
|
|
9feceb9fdb | ||
|
|
bad6464fe9 | ||
|
|
a6007ca362 | ||
|
|
05348b01ae | ||
|
|
abfabe3e21 | ||
|
|
83e0da446a | ||
|
|
85a48e71d1 | ||
|
|
0671117660 | ||
|
|
654c5b3074 | ||
|
|
aee5e54c14 | ||
|
|
e5df037992 | ||
|
|
06b9586eb6 | ||
|
|
79716dfdba | ||
|
|
02b6b5d078 | ||
|
|
403e8d2947 | ||
|
|
25e9c37987 | ||
|
|
99b58cf2bf | ||
|
|
78155cfbdf | ||
|
|
1ba980d69f | ||
|
|
ef155aaedf | ||
|
|
22c4b528cb | ||
|
|
fd71c6ac7a | ||
|
|
019fa134cb | ||
|
|
4a40bd3364 | ||
|
|
68229b2474 | ||
|
|
8727ed6ccd | ||
|
|
8ee3d5b8ad | ||
|
|
98ee43aa27 | ||
|
|
92291e3f9f | ||
|
|
1abb41a0d5 | ||
|
|
0993195f03 | ||
|
|
c2a7c6f994 | ||
|
|
48900cf5a0 | ||
|
|
57fbb36808 | ||
|
|
5c45384c34 | ||
|
|
f9aa162e2a | ||
|
|
013d17c386 | ||
|
|
ad250c3883 | ||
|
|
2c9e0012e2 | ||
|
|
2516f23e37 | ||
|
|
d3a7bd48c2 | ||
|
|
4aeb7490e3 | ||
|
|
265beef715 | ||
|
|
a58bf83a9a | ||
|
|
9da1a4a2de | ||
|
|
8a98e38e73 | ||
|
|
3044f4a0b1 | ||
|
|
5104ae1742 | ||
|
|
987ec14da2 | ||
|
|
24c6e0df97 | ||
|
|
23d5d18b28 | ||
|
|
0a38c241af | ||
|
|
5b0d79ad17 | ||
|
|
833187559d | ||
|
|
527066e4c8 | ||
|
|
bad30a49bf | ||
|
|
24f40dd652 | ||
|
|
dce61183dd | ||
|
|
aeb81a9e86 | ||
|
|
81aea15988 | ||
|
|
3e393208ab | ||
|
|
0674accd05 | ||
|
|
0f0d8df572 | ||
|
|
0afd06be9c | ||
|
|
1539bfa8b1 | ||
|
|
a2336be143 | ||
|
|
7d65befc7e | ||
|
|
916b28bc16 | ||
|
|
fb3447a6b4 | ||
|
|
2fa9b2e0e5 | ||
|
|
a66d3fb28a | ||
|
|
6be017d35b | ||
|
|
ef24e5164f | ||
|
|
a7f36b2361 | ||
|
|
3d404002a0 | ||
|
|
1922a1b49c | ||
|
|
ca4ec7025c | ||
|
|
7dcfa709d5 | ||
|
|
aa254142e8 | ||
|
|
b00b12bed2 | ||
|
|
dd7168a687 | ||
|
|
97b665733a | ||
|
|
4a6d3e3042 | ||
|
|
f65cf43e21 | ||
|
|
f971d4db51 | ||
|
|
8cd79c0372 | ||
|
|
9bc346e84a | ||
|
|
88d2e1756f | ||
|
|
dc09c45330 | ||
|
|
47e5b57789 | ||
|
|
c1ad8ed4b2 | ||
|
|
3589b5c9a8 | ||
|
|
fbaf15ad34 | ||
|
|
ee1d02991c | ||
|
|
2450cb028e | ||
|
|
ec1182f059 | ||
|
|
7797d8255f | ||
|
|
a4780ecac7 | ||
|
|
32643990ee | ||
|
|
5746e6267a | ||
|
|
501ec81405 | ||
|
|
714f4fe995 | ||
|
|
ead71a5618 | ||
|
|
eaf6e4ba28 | ||
|
|
bef2c9310e | ||
|
|
7b3e0d4a59 | ||
|
|
ea0705e7e4 | ||
|
|
7b9310f595 | ||
|
|
a31f775de8 | ||
|
|
f061630789 | ||
|
|
5d442125b0 | ||
|
|
e600f444f8 | ||
|
|
a8ae4d6655 | ||
|
|
a885fa7065 | ||
|
|
65665dc384 | ||
|
|
a8992edf88 | ||
|
|
1b7dd28c2f | ||
|
|
ae692432a2 | ||
|
|
c779b31a90 | ||
|
|
cb4bf8f5f4 | ||
|
|
96cd807720 | ||
|
|
07c3150d2a | ||
|
|
c66a405e6d | ||
|
|
0558381bcb | ||
|
|
1db47684b7 | ||
|
|
e16abd2e8b | ||
|
|
f7adc84f0e | ||
|
|
3acd51b247 | ||
|
|
6caab9c017 | ||
|
|
97c61d2a4f | ||
|
|
4f6a0061fe | ||
|
|
5b201069e8 | ||
|
|
8b5d79847f | ||
|
|
2d112a86af | ||
|
|
8806a62af7 | ||
|
|
01748ee847 | ||
|
|
6f1fb24a31 | ||
|
|
c30459115e | ||
|
|
7cdfcbf6dc | ||
|
|
acf140e670 | ||
|
|
df4a18e887 | ||
|
|
dc3836065a | ||
|
|
3df7ab194c | ||
|
|
c0af8460a2 | ||
|
|
85d94a84a8 | ||
|
|
6ad6a361f9 | ||
|
|
2a5dcab60f | ||
|
|
02b97d594f | ||
|
|
5e1cac251b | ||
|
|
3639638bf2 | ||
|
|
e255e054d4 | ||
|
|
8c9d8e7089 | ||
|
|
f0e069d348 | ||
|
|
4f32c8469e | ||
|
|
b2679ecb60 | ||
|
|
e0537267a2 | ||
|
|
3440afd0d6 | ||
|
|
cab65a0aaf | ||
|
|
910908b43e | ||
|
|
cf1c1e6df1 | ||
|
|
ff9b4f5b2f | ||
|
|
01899428e3 | ||
|
|
ff75ade140 | ||
|
|
25d5f36645 | ||
|
|
5c5f042cac | ||
|
|
7da0b32838 | ||
|
|
389896be07 | ||
|
|
f55323afca | ||
|
|
0d5313d263 | ||
|
|
bc676b9731 | ||
|
|
0313a90196 | ||
|
|
fca1c62a55 | ||
|
|
2dcb0d6177 | ||
|
|
e2eea4bca7 | ||
|
|
9218184ef1 | ||
|
|
a9ec9e87be |
No files matched your search
+2
-3
@@ -1,5 +1,6 @@
|
||||
nohup.out
|
||||
build/
|
||||
build/**
|
||||
install/**
|
||||
.build_proxy/
|
||||
cmake-build-debug/
|
||||
.cache/
|
||||
@@ -40,8 +41,6 @@ trace_data/
|
||||
.ionide/*
|
||||
/.ignore
|
||||
*.rej
|
||||
/build
|
||||
/build.*/*
|
||||
.clangd/
|
||||
*CMakeDeps.cmake
|
||||
/sniper.ipynb
|
||||
|
||||
+13
-1
@@ -13,6 +13,7 @@ set(SPARK_BIN "${EXTERNALS_ROOT}/bin")
|
||||
set(SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
|
||||
set(SPARK_LIB "${EXTERNALS_ROOT}/lib/${CMAKE_BUILD_TYPE}")
|
||||
set(BUILD_SDK OFF CACHE BOOL "Build the sdk")
|
||||
set(BUILD_WEB ON CACHE BOOL "Build the web server")
|
||||
|
||||
include_directories(BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
|
||||
link_directories(${SPARK_LIB})
|
||||
@@ -47,6 +48,11 @@ else()
|
||||
# make sparkd look for libspark.so in its directory
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set(CMAKE_INSTALL_RPATH ".")
|
||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install/${CMAKE_BUILD_TYPE}/sparkd")
|
||||
add_compile_definitions(SPARK_ENABLE_CCAPI)
|
||||
add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA)
|
||||
add_compile_definitions(CCAPI_ENABLE_EXCHANGE_FTX)
|
||||
add_compile_definitions(CCAPI_ENABLE_LOG_WARN)
|
||||
|
||||
# flags
|
||||
set(
|
||||
@@ -67,7 +73,9 @@ else()
|
||||
# subdirs
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/lib)
|
||||
add_subdirectory(src/web)
|
||||
if (BUILD_WEB)
|
||||
add_subdirectory(src/web)
|
||||
endif()
|
||||
add_subdirectory(doc/user)
|
||||
if(BUILD_SDK)
|
||||
add_subdirectory(src/sdk)
|
||||
@@ -91,4 +99,8 @@ else()
|
||||
DEPENDS sparkd data_files_dir)
|
||||
|
||||
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
|
||||
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libACE-5.7.5.so" DESTINATION bin)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0" DESTINATION bin)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/externals/src/emumba/sparkd.lic" DESTINATION bin)
|
||||
endif()
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,941 @@
|
||||
{
|
||||
"$schema": "vscode://schemas/color-theme",
|
||||
"type": "dark",
|
||||
"colors": {
|
||||
"activityBar.background": "#111111",
|
||||
"activityBar.border": "#111111",
|
||||
"activityBar.foreground": "#888888",
|
||||
"activityBarBadge.background": "#88cc44",
|
||||
"activityBarBadge.foreground": "#222222",
|
||||
"badge.background": "#88cc44",
|
||||
"badge.foreground": "#222222",
|
||||
"button.background": "#333333",
|
||||
"button.foreground": "#888888",
|
||||
"button.hoverBackground": "#222222",
|
||||
"debugExceptionWidget.background": "#333333",
|
||||
"debugExceptionWidget.border": "#222222",
|
||||
"debugToolBar.background": "#111111",
|
||||
"diffEditor.insertedTextBackground": "#ffdd440c",
|
||||
"diffEditor.insertedTextBorder": "#ffdd44",
|
||||
"diffEditor.removedTextBackground": "#88bbff0c",
|
||||
"diffEditor.removedTextBorder": "#88bbff",
|
||||
"dropdown.background": "#222222",
|
||||
"dropdown.border": "#222222",
|
||||
"dropdown.foreground": "#888888",
|
||||
"editor.background": "#222222",
|
||||
"editor.findMatchBackground": "#44446655",
|
||||
"editor.findMatchHighlightBackground": "#444466aa",
|
||||
"editor.foreground": "#ffffff",
|
||||
"editor.hoverHighlightBackground": "#44446655",
|
||||
"editor.inactiveSelectionBackground": "#44446655",
|
||||
"editor.lineHighlightBackground": "#00000066",
|
||||
"editor.lineHighlightBorder": "#00000000",
|
||||
"editor.rangeHighlightBackground": "#333333",
|
||||
"editor.selectionBackground": "#444466",
|
||||
"editor.selectionHighlightBackground": "#44446655",
|
||||
"editor.wordHighlightBackground": "#44446655",
|
||||
"editor.wordHighlightStrongBackground": "#44446655",
|
||||
"editorBracketMatch.background": "#222222",
|
||||
"editorBracketMatch.border": "#666666",
|
||||
"editorCodeLens.foreground": "#666666",
|
||||
"editorCursor.foreground": "#ffffff",
|
||||
"editorError.foreground": "#ee5500",
|
||||
"editorGroup.border": "#111111",
|
||||
"editorGroup.dropBackground": "#111111bb",
|
||||
"editorGroupHeader.noTabsBackground": "#111111",
|
||||
"editorGroupHeader.tabsBackground": "#111111",
|
||||
"editorGroupHeader.tabsBorder": "#222222",
|
||||
"editorGutter.addedBackground": "#ffdd44",
|
||||
"editorGutter.background": "#222222",
|
||||
"editorGutter.deletedBackground": "#88bbff",
|
||||
"editorGutter.modifiedBackground": "#88bbff",
|
||||
"editorHoverWidget.background": "#333333",
|
||||
"editorHoverWidget.border": "#222222",
|
||||
"editorIndentGuide.background": "#333333",
|
||||
"editorLineNumber.foreground": "#555555",
|
||||
"editorLink.activeForeground": "#bba0ff",
|
||||
"editorMarkerNavigation.background": "#333333",
|
||||
"editorMarkerNavigationError.background": "#88bbff",
|
||||
"editorMarkerNavigationWarning.background": "#88bbff",
|
||||
"editorOverviewRuler.addedForeground": "#ffdd44",
|
||||
"editorOverviewRuler.border": "#222222",
|
||||
"editorOverviewRuler.currentContentForeground": "#333333",
|
||||
"editorOverviewRuler.deletedForeground": "#88bbff",
|
||||
"editorOverviewRuler.errorForeground": "#88bbff",
|
||||
"editorOverviewRuler.findMatchForeground": "#555555",
|
||||
"editorOverviewRuler.incomingContentForeground": "#333333",
|
||||
"editorOverviewRuler.infoForeground": "#bba0ff",
|
||||
"editorOverviewRuler.modifiedForeground": "#88bbff",
|
||||
"editorOverviewRuler.rangeHighlightForeground": "#333333",
|
||||
"editorOverviewRuler.selectionHighlightForeground": "#555555",
|
||||
"editorOverviewRuler.warningForeground": "#88bbff",
|
||||
"editorOverviewRuler.wordHighlightForeground": "#333333",
|
||||
"editorOverviewRuler.wordHighlightStrongForeground": "#555555",
|
||||
"editorRuler.foreground": "#ffffff",
|
||||
"editorSuggestWidget.background": "#333333",
|
||||
"editorSuggestWidget.border": "#333333",
|
||||
"editorSuggestWidget.foreground": "#888888",
|
||||
"editorSuggestWidget.highlightForeground": "#ffffff",
|
||||
"editorSuggestWidget.selectedBackground": "#666666",
|
||||
"editorWarning.border": "#00000000",
|
||||
"editorWarning.foreground": "#88bbff",
|
||||
"editorWhitespace.foreground": "#333333",
|
||||
"editorWidget.background": "#333333",
|
||||
"editorWidget.border": "#333333",
|
||||
"errorForeground": "#88bbff",
|
||||
"extensionButton.prominentBackground": "#333333",
|
||||
"extensionButton.prominentForeground": "#ffffff",
|
||||
"extensionButton.prominentHoverBackground": "#555555",
|
||||
"focusBorder": "#666666",
|
||||
"foreground": "#ffffff",
|
||||
"gitDecoration.modifiedResourceForeground": "#cccccc",
|
||||
"gitDecoration.untrackedResourceForeground": "#cccccc",
|
||||
"input.background": "#333333",
|
||||
"input.border": "#333333",
|
||||
"input.foreground": "#ffffff",
|
||||
"input.placeholderForeground": "#555555",
|
||||
"inputOption.activeBorder": "#555555",
|
||||
"inputValidation.errorBackground": "#333333",
|
||||
"inputValidation.errorBorder": "#88bbff",
|
||||
"inputValidation.infoBackground": "#333333",
|
||||
"inputValidation.infoBorder": "#bba0ff",
|
||||
"inputValidation.warningBackground": "#333333",
|
||||
"inputValidation.warningBorder": "#88bbff",
|
||||
"list.activeSelectionBackground": "#111111",
|
||||
"list.activeSelectionForeground": "#ffffff",
|
||||
"list.dropBackground": "#111111bf",
|
||||
"list.focusBackground": "#222222",
|
||||
"list.focusForeground": "#ffffff",
|
||||
"list.highlightForeground": "#ffffff",
|
||||
"list.hoverBackground": "#333333",
|
||||
"list.hoverForeground": "#ffffff",
|
||||
"list.inactiveSelectionBackground": "#000000",
|
||||
"list.inactiveSelectionForeground": "#ffffff",
|
||||
"menu.background": "#333333",
|
||||
"menu.foreground": "#ffffff",
|
||||
"merge.border": "#222222",
|
||||
"merge.currentContentBackground": "#333333",
|
||||
"merge.currentHeaderBackground": "#555555",
|
||||
"merge.incomingContentBackground": "#555555",
|
||||
"merge.incomingHeaderBackground": "#666666",
|
||||
"panel.background": "#111111",
|
||||
"panel.border": "#111111",
|
||||
"panelTitle.activeBorder": "#ffffff",
|
||||
"panelTitle.activeForeground": "#ffffff",
|
||||
"panelTitle.inactiveForeground": "#888888",
|
||||
"peekView.border": "#222222",
|
||||
"peekViewEditor.background": "#333333",
|
||||
"peekViewEditor.matchHighlightBackground": "#555555",
|
||||
"peekViewEditorGutter.background": "#333333",
|
||||
"peekViewResult.background": "#333333",
|
||||
"peekViewResult.fileForeground": "#888888",
|
||||
"peekViewResult.lineForeground": "#888888",
|
||||
"peekViewResult.matchHighlightBackground": "#555555",
|
||||
"peekViewResult.selectionBackground": "#333333",
|
||||
"peekViewResult.selectionForeground": "#ffffff",
|
||||
"peekViewTitle.background": "#333333",
|
||||
"peekViewTitleDescription.foreground": "#888888",
|
||||
"peekViewTitleLabel.foreground": "#ffffff",
|
||||
"pickerGroup.border": "#222222",
|
||||
"pickerGroup.foreground": "#555555",
|
||||
"progressBar.background": "#333333",
|
||||
"scrollbar.shadow": "#191919",
|
||||
"scrollbarSlider.activeBackground": "#ffffff12",
|
||||
"scrollbarSlider.background": "#ffffff12",
|
||||
"scrollbarSlider.hoverBackground": "#ffffff12",
|
||||
"selection.background": "#2255aa",
|
||||
"sideBar.background": "#111111",
|
||||
"sideBar.border": "#111111",
|
||||
"sideBar.foreground": "#888888",
|
||||
"sideBarSectionHeader.background": "#111111",
|
||||
"sideBarSectionHeader.foreground": "#666666",
|
||||
"sideBarTitle.foreground": "#555555",
|
||||
"statusBar.background": "#111111",
|
||||
"statusBar.border": "#111111",
|
||||
"statusBar.debuggingBackground": "#666666",
|
||||
"statusBar.debuggingForeground": "#ffffff",
|
||||
"statusBar.foreground": "#666666",
|
||||
"statusBar.noFolderBackground": "#111111",
|
||||
"statusBar.noFolderForeground": "#666666",
|
||||
"statusBarItem.activeBackground": "#222222",
|
||||
"statusBarItem.hoverBackground": "#222222",
|
||||
"statusBarItem.prominentBackground": "#333333",
|
||||
"statusBarItem.prominentHoverBackground": "#444444",
|
||||
"tab.activeBackground": "#222222",
|
||||
"tab.border": "#111111",
|
||||
"tab.inactiveBackground": "#111111",
|
||||
"tab.inactiveForeground": "#888888",
|
||||
"tab.unfocusedActiveBorder": "#888888",
|
||||
"tab.unfocusedActiveForeground": "#888888",
|
||||
"tab.unfocusedInactiveForeground": "#888888",
|
||||
"terminal.ansiBlack": "#333333",
|
||||
"terminal.ansiBlue": "#88bbff",
|
||||
"terminal.ansiBrightBlack": "#666666",
|
||||
"terminal.ansiBrightBlue": "#88bbff",
|
||||
"terminal.ansiBrightCyan": "#bba0ff",
|
||||
"terminal.ansiBrightGreen": "#ffdd44",
|
||||
"terminal.ansiBrightMagenta": "#88bbff",
|
||||
"terminal.ansiBrightRed": "#88bbff",
|
||||
"terminal.ansiBrightWhite": "#ffffff",
|
||||
"terminal.ansiBrightYellow": "#88cc44",
|
||||
"terminal.ansiCyan": "#bba0ff",
|
||||
"terminal.ansiGreen": "#ffdd44",
|
||||
"terminal.ansiMagenta": "#88bbff",
|
||||
"terminal.ansiRed": "#88bbff",
|
||||
"terminal.ansiWhite": "#ffffff",
|
||||
"terminal.ansiYellow": "#88cc44",
|
||||
"terminal.background": "#151515",
|
||||
"terminal.foreground": "#ffffff",
|
||||
"titleBar.activeBackground": "#111111",
|
||||
"titleBar.activeForeground": "#888888",
|
||||
"titleBar.inactiveBackground": "#111111",
|
||||
"titleBar.inactiveForeground": "#555555",
|
||||
"walkThrough.embeddedEditorBackground": "#111111",
|
||||
"widget.shadow": "#111111",
|
||||
//"activityBar.activeBorder": "#888888",
|
||||
//"activityBar.dropBorder": "#888888",
|
||||
//"activityBar.inactiveForeground": "#88888866",
|
||||
//"banner.background": "#111111",
|
||||
//"banner.foreground": "#ffffff",
|
||||
//"banner.iconForeground": "#3794ff",
|
||||
//"bookmarks.lineBackground": "#00000000",
|
||||
//"bookmarks.lineBorder": "#00000000",
|
||||
//"bookmarks.overviewRuler": "#157efb88",
|
||||
//"breadcrumb.activeSelectionForeground": "#ffffff",
|
||||
//"breadcrumb.background": "#222222",
|
||||
//"breadcrumb.focusForeground": "#ffffff",
|
||||
//"breadcrumb.foreground": "#ffffffcc",
|
||||
//"breadcrumbPicker.background": "#333333",
|
||||
//"button.secondaryBackground": "#3a3d41",
|
||||
//"button.secondaryForeground": "#ffffff",
|
||||
//"button.secondaryHoverBackground": "#45494e",
|
||||
//"charts.blue": "#3794ff",
|
||||
//"charts.foreground": "#ffffff",
|
||||
//"charts.green": "#89d185",
|
||||
//"charts.lines": "#ffffff80",
|
||||
//"charts.orange": "#d18616",
|
||||
//"charts.purple": "#b180d7",
|
||||
//"charts.red": "#ee5500",
|
||||
//"charts.yellow": "#88bbff",
|
||||
//"checkbox.background": "#222222",
|
||||
//"checkbox.border": "#222222",
|
||||
//"checkbox.foreground": "#888888",
|
||||
//"debugConsole.errorForeground": "#88bbff",
|
||||
//"debugConsole.infoForeground": "#3794ff",
|
||||
//"debugConsole.sourceForeground": "#ffffff",
|
||||
//"debugConsole.warningForeground": "#88bbff",
|
||||
//"debugConsoleInputIcon.foreground": "#ffffff",
|
||||
//"debugIcon.breakpointCurrentStackframeForeground": "#ffcc00",
|
||||
//"debugIcon.breakpointDisabledForeground": "#848484",
|
||||
//"debugIcon.breakpointForeground": "#e51400",
|
||||
//"debugIcon.breakpointStackframeForeground": "#89d185",
|
||||
//"debugIcon.breakpointUnverifiedForeground": "#848484",
|
||||
//"debugIcon.continueForeground": "#75beff",
|
||||
//"debugIcon.disconnectForeground": "#f48771",
|
||||
//"debugIcon.pauseForeground": "#75beff",
|
||||
//"debugIcon.restartForeground": "#89d185",
|
||||
//"debugIcon.startForeground": "#89d185",
|
||||
//"debugIcon.stepBackForeground": "#75beff",
|
||||
//"debugIcon.stepIntoForeground": "#75beff",
|
||||
//"debugIcon.stepOutForeground": "#75beff",
|
||||
//"debugIcon.stepOverForeground": "#75beff",
|
||||
//"debugIcon.stopForeground": "#f48771",
|
||||
//"debugTokenExpression.boolean": "#4e94ce",
|
||||
//"debugTokenExpression.error": "#f48771",
|
||||
//"debugTokenExpression.name": "#c586c0",
|
||||
//"debugTokenExpression.number": "#b5cea8",
|
||||
//"debugTokenExpression.string": "#ce9178",
|
||||
//"debugTokenExpression.value": "#cccccc99",
|
||||
//"debugView.exceptionLabelBackground": "#6c2022",
|
||||
//"debugView.exceptionLabelForeground": "#ffffff",
|
||||
//"debugView.stateLabelBackground": "#88888844",
|
||||
//"debugView.stateLabelForeground": "#ffffff",
|
||||
//"debugView.valueChangedHighlight": "#569cd6",
|
||||
//"descriptionForeground": "#ffffffb3",
|
||||
//"diffEditor.diagonalFill": "#cccccc33",
|
||||
//"editor.findRangeHighlightBackground": "#3a3d4166",
|
||||
//"editor.focusedStackFrameHighlightBackground": "#7abd7a4d",
|
||||
//"editor.foldBackground": "#4444664d",
|
||||
//"editor.inlineValuesBackground": "#ffc80033",
|
||||
//"editor.inlineValuesForeground": "#ffffff80",
|
||||
//"editor.linkedEditingBackground": "#ff00004d",
|
||||
//"editor.snippetFinalTabstopHighlightBorder": "#525252",
|
||||
//"editor.snippetTabstopHighlightBackground": "#7c7c7c4d",
|
||||
//"editor.stackFrameHighlightBackground": "#ffff0033",
|
||||
//"editor.symbolHighlightBackground": "#444466aa",
|
||||
//"editorActiveLineNumber.foreground": "#c6c6c6",
|
||||
//"editorBracketHighlight.foreground1": "#ffd700",
|
||||
//"editorBracketHighlight.foreground2": "#da70d6",
|
||||
//"editorBracketHighlight.foreground3": "#179fff",
|
||||
//"editorBracketHighlight.foreground4": "#00000000",
|
||||
//"editorBracketHighlight.foreground5": "#00000000",
|
||||
//"editorBracketHighlight.foreground6": "#00000000",
|
||||
//"editorBracketHighlight.unexpectedBracket.foreground": "#ff1212cc",
|
||||
//"editorBracketPairGuide.activeBackground1": "#00000000",
|
||||
//"editorBracketPairGuide.activeBackground2": "#00000000",
|
||||
//"editorBracketPairGuide.activeBackground3": "#00000000",
|
||||
//"editorBracketPairGuide.activeBackground4": "#00000000",
|
||||
//"editorBracketPairGuide.activeBackground5": "#00000000",
|
||||
//"editorBracketPairGuide.activeBackground6": "#00000000",
|
||||
//"editorBracketPairGuide.background1": "#00000000",
|
||||
//"editorBracketPairGuide.background2": "#00000000",
|
||||
//"editorBracketPairGuide.background3": "#00000000",
|
||||
//"editorBracketPairGuide.background4": "#00000000",
|
||||
//"editorBracketPairGuide.background5": "#00000000",
|
||||
//"editorBracketPairGuide.background6": "#00000000",
|
||||
//"editorGhostText.foreground": "#ffffff56",
|
||||
//"editorGutter.commentRangeForeground": "#c5c5c5",
|
||||
//"editorGutter.foldingControlForeground": "#c5c5c5",
|
||||
//"editorHint.foreground": "#eeeeeeb3",
|
||||
//"editorHoverWidget.foreground": "#ffffff",
|
||||
//"editorHoverWidget.highlightForeground": "#ffffff",
|
||||
//"editorHoverWidget.statusBarBackground": "#3d3d3d",
|
||||
//"editorIndentGuide.activeBackground": "#333333",
|
||||
//"editorInfo.foreground": "#3794ff",
|
||||
//"editorInlayHint.background": "#88cc4499",
|
||||
//"editorInlayHint.foreground": "#222222cc",
|
||||
//"editorInlayHint.parameterBackground": "#88cc4499",
|
||||
//"editorInlayHint.parameterForeground": "#222222cc",
|
||||
//"editorInlayHint.typeBackground": "#88cc4499",
|
||||
//"editorInlayHint.typeForeground": "#222222cc",
|
||||
//"editorLightBulb.foreground": "#ffcc00",
|
||||
//"editorLightBulbAutoFix.foreground": "#75beff",
|
||||
//"editorLineNumber.activeForeground": "#c6c6c6",
|
||||
//"editorMarkerNavigationError.headerBackground": "#88bbff1a",
|
||||
//"editorMarkerNavigationInfo.background": "#3794ff",
|
||||
//"editorMarkerNavigationInfo.headerBackground": "#3794ff1a",
|
||||
//"editorMarkerNavigationWarning.headerBackground": "#88bbff1a",
|
||||
//"editorOverviewRuler.bracketMatchForeground": "#a0a0a0",
|
||||
//"editorOverviewRuler.commonContentForeground": "#60606066",
|
||||
//"editorOverviewRuler.unicodeForeground": "#d186167e",
|
||||
//"editorPane.background": "#222222",
|
||||
//"editorSuggestWidget.focusHighlightForeground": "#ffffff",
|
||||
//"editorSuggestWidget.selectedForeground": "#ffffff",
|
||||
//"editorSuggestWidgetStatus.foreground": "#88888880",
|
||||
//"editorUnicodeHighlight.border": "#bd9b03",
|
||||
//"editorUnnecessaryCode.opacity": "#000000aa",
|
||||
//"editorWidget.foreground": "#ffffff",
|
||||
//"extensionBadge.remoteBackground": "#88cc44",
|
||||
//"extensionBadge.remoteForeground": "#222222",
|
||||
//"extensionIcon.preReleaseForeground": "#1d9271",
|
||||
//"extensionIcon.starForeground": "#ff8e00",
|
||||
//"extensionIcon.verifiedForeground": "#3794ff",
|
||||
//"gitDecoration.addedResourceForeground": "#81b88b",
|
||||
//"gitDecoration.conflictingResourceForeground": "#e4676b",
|
||||
//"gitDecoration.deletedResourceForeground": "#c74e39",
|
||||
//"gitDecoration.ignoredResourceForeground": "#8c8c8c",
|
||||
//"gitDecoration.renamedResourceForeground": "#73c991",
|
||||
//"gitDecoration.stageDeletedResourceForeground": "#c74e39",
|
||||
//"gitDecoration.stageModifiedResourceForeground": "#e2c08d",
|
||||
//"gitDecoration.submoduleResourceForeground": "#8db9e2",
|
||||
//"gitlens.closedAutolinkedIssueIconColor": "#a371f7",
|
||||
//"gitlens.closedPullRequestIconColor": "#f85149",
|
||||
//"gitlens.decorations.addedForegroundColor": "#81b88b",
|
||||
//"gitlens.decorations.branchAheadForegroundColor": "#35b15e",
|
||||
//"gitlens.decorations.branchBehindForegroundColor": "#b15e35",
|
||||
//"gitlens.decorations.branchDivergedForegroundColor": "#d8af1b",
|
||||
//"gitlens.decorations.branchMissingUpstreamForegroundColor": "#c74e39",
|
||||
//"gitlens.decorations.branchUnpublishedForegroundColor": "#35b15e",
|
||||
//"gitlens.decorations.branchUpToDateForegroundColor": "#888888",
|
||||
//"gitlens.decorations.copiedForegroundColor": "#73c991",
|
||||
//"gitlens.decorations.deletedForegroundColor": "#c74e39",
|
||||
//"gitlens.decorations.ignoredForegroundColor": "#8c8c8c",
|
||||
//"gitlens.decorations.modifiedForegroundColor": "#cccccc",
|
||||
//"gitlens.decorations.renamedForegroundColor": "#73c991",
|
||||
//"gitlens.decorations.untrackedForegroundColor": "#cccccc",
|
||||
//"gitlens.gutterBackgroundColor": "#ffffff13",
|
||||
//"gitlens.gutterForegroundColor": "#bebebe",
|
||||
//"gitlens.gutterUncommittedForegroundColor": "#00bcf299",
|
||||
//"gitlens.lineHighlightBackgroundColor": "#00bcf233",
|
||||
//"gitlens.lineHighlightOverviewRulerColor": "#00bcf299",
|
||||
//"gitlens.mergedPullRequestIconColor": "#a371f7",
|
||||
//"gitlens.openAutolinkedIssueIconColor": "#3fb950",
|
||||
//"gitlens.openPullRequestIconColor": "#3fb950",
|
||||
//"gitlens.trailingLineBackgroundColor": "#00000000",
|
||||
//"gitlens.trailingLineForegroundColor": "#99999959",
|
||||
//"gitlens.unpublishedCommitIconColor": "#35b15e",
|
||||
//"gitlens.unpulledChangesIconColor": "#b15e35",
|
||||
//"gitlens.unpushlishedChangesIconColor": "#35b15e",
|
||||
//"icon.foreground": "#c5c5c5",
|
||||
//"inputOption.activeBackground": "#66666666",
|
||||
//"inputOption.activeForeground": "#ffffff",
|
||||
//"inputOption.hoverBackground": "#5a5d5e80",
|
||||
//"interactive.activeCodeBorder": "#222222",
|
||||
//"interactive.inactiveCodeBorder": "#000000",
|
||||
//"keybindingLabel.background": "#8080802b",
|
||||
//"keybindingLabel.border": "#33333399",
|
||||
//"keybindingLabel.bottomBorder": "#44444499",
|
||||
//"keybindingLabel.foreground": "#cccccc",
|
||||
//"keybindingTable.headerBackground": "#ffffff0a",
|
||||
//"keybindingTable.rowsBackground": "#ffffff0a",
|
||||
//"list.deemphasizedForeground": "#8c8c8c",
|
||||
//"list.errorForeground": "#f88070",
|
||||
//"list.filterMatchBackground": "#444466aa",
|
||||
//"list.focusHighlightForeground": "#ffffff",
|
||||
//"list.focusOutline": "#666666",
|
||||
//"list.invalidItemForeground": "#b89500",
|
||||
//"list.warningForeground": "#cca700",
|
||||
//"listFilterWidget.background": "#653723",
|
||||
//"listFilterWidget.noMatchesOutline": "#be1100",
|
||||
//"listFilterWidget.outline": "#00000000",
|
||||
//"menu.selectionBackground": "#111111",
|
||||
//"menu.selectionForeground": "#ffffff",
|
||||
//"menu.separatorBackground": "#bbbbbb",
|
||||
//"menubar.selectionBackground": "#ffffff1a",
|
||||
//"menubar.selectionForeground": "#888888",
|
||||
//"merge.commonContentBackground": "#60606029",
|
||||
//"merge.commonHeaderBackground": "#60606066",
|
||||
//"minimap.errorHighlight": "#ff1212b3",
|
||||
//"minimap.findMatchHighlight": "#d18616",
|
||||
//"minimap.foregroundOpacity": "#000000",
|
||||
//"minimap.selectionHighlight": "#264f78",
|
||||
//"minimap.selectionOccurrenceHighlight": "#676767",
|
||||
//"minimap.unicodeHighlight": "#d18616",
|
||||
//"minimap.warningHighlight": "#88bbff",
|
||||
//"minimapGutter.addedBackground": "#587c0c",
|
||||
//"minimapGutter.deletedBackground": "#94151b",
|
||||
//"minimapGutter.modifiedBackground": "#0c7d9d",
|
||||
//"minimapSlider.activeBackground": "#ffffff09",
|
||||
//"minimapSlider.background": "#ffffff09",
|
||||
//"minimapSlider.hoverBackground": "#ffffff09",
|
||||
//"notebook.cellBorderColor": "#000000",
|
||||
//"notebook.cellEditorBackground": "#111111",
|
||||
//"notebook.cellInsertionIndicator": "#666666",
|
||||
//"notebook.cellStatusBarItemHoverBackground": "#ffffff26",
|
||||
//"notebook.cellToolbarSeparator": "#80808059",
|
||||
//"notebook.focusedCellBorder": "#666666",
|
||||
//"notebook.focusedEditorBorder": "#666666",
|
||||
//"notebook.inactiveFocusedCellBorder": "#000000",
|
||||
//"notebook.selectedCellBackground": "#000000",
|
||||
//"notebook.selectedCellBorder": "#000000",
|
||||
//"notebook.symbolHighlightBackground": "#ffffff0b",
|
||||
//"notebookScrollbarSlider.activeBackground": "#ffffff12",
|
||||
//"notebookScrollbarSlider.background": "#ffffff12",
|
||||
//"notebookScrollbarSlider.hoverBackground": "#ffffff12",
|
||||
//"notebookStatusErrorIcon.foreground": "#88bbff",
|
||||
//"notebookStatusRunningIcon.foreground": "#ffffff",
|
||||
//"notebookStatusSuccessIcon.foreground": "#89d185",
|
||||
//"notificationCenterHeader.background": "#424242",
|
||||
//"notificationLink.foreground": "#3794ff",
|
||||
//"notifications.background": "#333333",
|
||||
//"notifications.border": "#424242",
|
||||
//"notifications.foreground": "#ffffff",
|
||||
//"notificationsErrorIcon.foreground": "#ee5500",
|
||||
//"notificationsInfoIcon.foreground": "#3794ff",
|
||||
//"notificationsWarningIcon.foreground": "#88bbff",
|
||||
//"panel.dropBorder": "#ffffff",
|
||||
//"panelSection.border": "#111111",
|
||||
//"panelSection.dropBackground": "#111111bb",
|
||||
//"panelSectionHeader.background": "#80808033",
|
||||
//"ports.iconRunningProcessForeground": "#88cc44",
|
||||
//"problemsErrorIcon.foreground": "#ee5500",
|
||||
//"problemsInfoIcon.foreground": "#3794ff",
|
||||
//"problemsWarningIcon.foreground": "#88bbff",
|
||||
//"quickInput.background": "#333333",
|
||||
//"quickInput.foreground": "#ffffff",
|
||||
//"quickInputList.focusBackground": "#111111",
|
||||
//"quickInputList.focusForeground": "#ffffff",
|
||||
//"quickInputTitle.background": "#ffffff1b",
|
||||
//"sash.hoverBorder": "#666666",
|
||||
//"scm.providerBorder": "#454545",
|
||||
//"searchEditor.findMatchBackground": "#44446670",
|
||||
//"searchEditor.textInputBorder": "#333333",
|
||||
//"settings.checkboxBackground": "#222222",
|
||||
//"settings.checkboxBorder": "#222222",
|
||||
//"settings.checkboxForeground": "#888888",
|
||||
//"settings.dropdownBackground": "#222222",
|
||||
//"settings.dropdownBorder": "#222222",
|
||||
//"settings.dropdownForeground": "#888888",
|
||||
//"settings.dropdownListBorder": "#333333",
|
||||
//"settings.focusedRowBackground": "#80808024",
|
||||
//"settings.focusedRowBorder": "#ffffff1f",
|
||||
//"settings.headerForeground": "#e7e7e7",
|
||||
//"settings.modifiedItemIndicator": "#0c7d9d",
|
||||
//"settings.numberInputBackground": "#333333",
|
||||
//"settings.numberInputBorder": "#333333",
|
||||
//"settings.numberInputForeground": "#ffffff",
|
||||
//"settings.rowHoverBackground": "#80808012",
|
||||
//"settings.textInputBackground": "#333333",
|
||||
//"settings.textInputBorder": "#333333",
|
||||
//"settings.textInputForeground": "#ffffff",
|
||||
//"sideBar.dropBackground": "#111111bb",
|
||||
//"sideBySideEditor.horizontalBorder": "#111111",
|
||||
//"sideBySideEditor.verticalBorder": "#111111",
|
||||
//"statusBar.debuggingBorder": "#111111",
|
||||
//"statusBar.noFolderBorder": "#111111",
|
||||
//"statusBarItem.compactHoverBackground": "#ffffff33",
|
||||
//"statusBarItem.errorBackground": "#0066eb",
|
||||
//"statusBarItem.errorForeground": "#ffffff",
|
||||
//"statusBarItem.prominentForeground": "#666666",
|
||||
//"statusBarItem.remoteBackground": "#88cc44",
|
||||
//"statusBarItem.remoteForeground": "#222222",
|
||||
//"statusBarItem.warningBackground": "#0066eb",
|
||||
//"statusBarItem.warningForeground": "#ffffff",
|
||||
//"symbolIcon.arrayForeground": "#ffffff",
|
||||
//"symbolIcon.booleanForeground": "#ffffff",
|
||||
//"symbolIcon.classForeground": "#ee9d28",
|
||||
//"symbolIcon.colorForeground": "#ffffff",
|
||||
//"symbolIcon.constantForeground": "#ffffff",
|
||||
//"symbolIcon.constructorForeground": "#b180d7",
|
||||
//"symbolIcon.enumeratorForeground": "#ee9d28",
|
||||
//"symbolIcon.enumeratorMemberForeground": "#75beff",
|
||||
//"symbolIcon.eventForeground": "#ee9d28",
|
||||
//"symbolIcon.fieldForeground": "#75beff",
|
||||
//"symbolIcon.fileForeground": "#ffffff",
|
||||
//"symbolIcon.folderForeground": "#ffffff",
|
||||
//"symbolIcon.functionForeground": "#b180d7",
|
||||
//"symbolIcon.interfaceForeground": "#75beff",
|
||||
//"symbolIcon.keyForeground": "#ffffff",
|
||||
//"symbolIcon.keywordForeground": "#ffffff",
|
||||
//"symbolIcon.methodForeground": "#b180d7",
|
||||
//"symbolIcon.moduleForeground": "#ffffff",
|
||||
//"symbolIcon.namespaceForeground": "#ffffff",
|
||||
//"symbolIcon.nullForeground": "#ffffff",
|
||||
//"symbolIcon.numberForeground": "#ffffff",
|
||||
//"symbolIcon.objectForeground": "#ffffff",
|
||||
//"symbolIcon.operatorForeground": "#ffffff",
|
||||
//"symbolIcon.packageForeground": "#ffffff",
|
||||
//"symbolIcon.propertyForeground": "#ffffff",
|
||||
//"symbolIcon.referenceForeground": "#ffffff",
|
||||
//"symbolIcon.snippetForeground": "#ffffff",
|
||||
//"symbolIcon.stringForeground": "#ffffff",
|
||||
//"symbolIcon.structForeground": "#ffffff",
|
||||
//"symbolIcon.textForeground": "#ffffff",
|
||||
//"symbolIcon.typeParameterForeground": "#ffffff",
|
||||
//"symbolIcon.unitForeground": "#ffffff",
|
||||
//"symbolIcon.variableForeground": "#75beff",
|
||||
//"tab.activeForeground": "#ffffff",
|
||||
//"tab.activeModifiedBorder": "#3399cc",
|
||||
//"tab.inactiveModifiedBorder": "#3399cc80",
|
||||
//"tab.lastPinnedBorder": "#585858",
|
||||
//"tab.unfocusedActiveBackground": "#222222",
|
||||
//"tab.unfocusedActiveModifiedBorder": "#3399cc80",
|
||||
//"tab.unfocusedInactiveBackground": "#111111",
|
||||
//"tab.unfocusedInactiveModifiedBorder": "#3399cc40",
|
||||
//"terminal.border": "#111111",
|
||||
//"terminal.dropBackground": "#111111bb",
|
||||
//"terminal.selectionBackground": "#ffffff40",
|
||||
//"testing.iconErrored": "#f14c4c",
|
||||
//"testing.iconFailed": "#f14c4c",
|
||||
//"testing.iconPassed": "#73c991",
|
||||
//"testing.iconQueued": "#cca700",
|
||||
//"testing.iconSkipped": "#848484",
|
||||
//"testing.iconUnset": "#848484",
|
||||
//"testing.message.error.decorationForeground": "#ee5500",
|
||||
//"testing.message.error.lineBackground": "#ff000033",
|
||||
//"testing.message.info.decorationForeground": "#ffffff80",
|
||||
//"testing.peekBorder": "#ee5500",
|
||||
//"testing.peekHeaderBackground": "#ee55001a",
|
||||
//"testing.runAction": "#73c991",
|
||||
//"textBlockQuote.background": "#7f7f7f1a",
|
||||
//"textBlockQuote.border": "#007acc80",
|
||||
//"textCodeBlock.background": "#0a0a0a66",
|
||||
//"textLink.activeForeground": "#3794ff",
|
||||
//"textLink.foreground": "#3794ff",
|
||||
//"textPreformat.foreground": "#d7ba7d",
|
||||
//"textSeparator.foreground": "#ffffff2e",
|
||||
//"toolbar.activeBackground": "#63666750",
|
||||
//"toolbar.hoverBackground": "#5a5d5e50",
|
||||
//"tree.indentGuidesStroke": "#585858",
|
||||
//"tree.tableColumnsBorder": "#cccccc20",
|
||||
//"tree.tableOddRowsBackground": "#ffffff0a",
|
||||
//"welcomePage.progress.background": "#333333",
|
||||
//"welcomePage.progress.foreground": "#3794ff",
|
||||
//"welcomePage.tileBackground": "#333333",
|
||||
//"welcomePage.tileHoverBackground": "#3d3d3d",
|
||||
//"welcomePage.tileShadow": "#111111",
|
||||
//"activityBar.activeBackground": null,
|
||||
//"activityBar.activeFocusBorder": null,
|
||||
//"button.border": null,
|
||||
//"contrastActiveBorder": null,
|
||||
//"contrastBorder": null,
|
||||
//"debugToolBar.border": null,
|
||||
//"diffEditor.border": null,
|
||||
//"dropdown.listBackground": null,
|
||||
//"editor.findMatchBorder": null,
|
||||
//"editor.findMatchHighlightBorder": null,
|
||||
//"editor.findRangeHighlightBorder": null,
|
||||
//"editor.rangeHighlightBorder": null,
|
||||
//"editor.selectionForeground": null,
|
||||
//"editor.selectionHighlightBorder": null,
|
||||
//"editor.snippetFinalTabstopHighlightBackground": null,
|
||||
//"editor.snippetTabstopHighlightBorder": null,
|
||||
//"editor.symbolHighlightBorder": null,
|
||||
//"editor.wordHighlightBorder": null,
|
||||
//"editor.wordHighlightStrongBorder": null,
|
||||
//"editorCursor.background": null,
|
||||
//"editorError.background": null,
|
||||
//"editorError.border": null,
|
||||
//"editorGhostText.background": null,
|
||||
//"editorGhostText.border": null,
|
||||
//"editorGroup.emptyBackground": null,
|
||||
//"editorGroup.focusedEmptyBorder": null,
|
||||
//"editorGroupHeader.border": null,
|
||||
//"editorHint.border": null,
|
||||
//"editorInfo.background": null,
|
||||
//"editorInfo.border": null,
|
||||
//"editorOverviewRuler.background": null,
|
||||
//"editorSuggestWidget.selectedIconForeground": null,
|
||||
//"editorUnnecessaryCode.border": null,
|
||||
//"editorWarning.background": null,
|
||||
//"editorWidget.resizeBorder": null,
|
||||
//"inputValidation.errorForeground": null,
|
||||
//"inputValidation.infoForeground": null,
|
||||
//"inputValidation.warningForeground": null,
|
||||
//"list.activeSelectionIconForeground": null,
|
||||
//"list.filterMatchBorder": null,
|
||||
//"list.inactiveFocusBackground": null,
|
||||
//"list.inactiveFocusOutline": null,
|
||||
//"list.inactiveSelectionIconForeground": null,
|
||||
//"menu.border": null,
|
||||
//"menu.selectionBorder": null,
|
||||
//"menubar.selectionBorder": null,
|
||||
//"minimap.background": null,
|
||||
//"notebook.cellHoverBackground": null,
|
||||
//"notebook.focusedCellBackground": null,
|
||||
//"notebook.inactiveSelectedCellBorder": null,
|
||||
//"notebook.outputContainerBackgroundColor": null,
|
||||
//"notebook.outputContainerBorderColor": null,
|
||||
//"notificationCenter.border": null,
|
||||
//"notificationCenterHeader.foreground": null,
|
||||
//"notificationToast.border": null,
|
||||
//"panelInput.border": null,
|
||||
//"panelSectionHeader.border": null,
|
||||
//"panelSectionHeader.foreground": null,
|
||||
//"peekViewEditor.matchHighlightBorder": null,
|
||||
//"quickInput.list.focusBackground": null,
|
||||
//"quickInputList.focusIconForeground": null,
|
||||
//"searchEditor.findMatchBorder": null,
|
||||
//"sideBarSectionHeader.border": null,
|
||||
//"tab.activeBorder": null,
|
||||
//"tab.activeBorderTop": null,
|
||||
//"tab.hoverBackground": null,
|
||||
//"tab.hoverBorder": null,
|
||||
//"tab.hoverForeground": null,
|
||||
//"tab.unfocusedActiveBorderTop": null,
|
||||
//"tab.unfocusedHoverBackground": null,
|
||||
//"tab.unfocusedHoverBorder": null,
|
||||
//"tab.unfocusedHoverForeground": null,
|
||||
//"terminal.tab.activeBorder": null,
|
||||
//"terminalCursor.background": null,
|
||||
//"terminalCursor.foreground": null,
|
||||
//"testing.message.info.lineBackground": null,
|
||||
//"titleBar.border": null,
|
||||
//"toolbar.hoverOutline": null,
|
||||
//"welcomePage.background": null,
|
||||
//"window.activeBorder": null,
|
||||
//"window.inactiveBorder": null
|
||||
},
|
||||
"tokenColors": [
|
||||
{
|
||||
"scope": [
|
||||
"variable.language.this"
|
||||
],
|
||||
"settings": {
|
||||
"fontStyle": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.bold",
|
||||
"settings": {
|
||||
"fontStyle": "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"invalid",
|
||||
"markup.italic",
|
||||
"markup.quote"
|
||||
],
|
||||
"settings": {
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.underline",
|
||||
"settings": {
|
||||
"fontStyle": "underline"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"source.js.jsx punctuation.section.embedded",
|
||||
"entity.name.tag",
|
||||
"string.other.link.description.title",
|
||||
"support.constant",
|
||||
"support.variable",
|
||||
"support.type",
|
||||
"variable.other.class",
|
||||
"support.class.builtin.js",
|
||||
"support.class.console.js",
|
||||
"meta.property-value",
|
||||
"variable.parameter.keyframe-list",
|
||||
"meta.at-rule.keyframes entity.name.function",
|
||||
"expression.embedded.vue punctuation.definition"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#BBA0FF",
|
||||
"fontStyle": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"entity.other.attribute-name.pseudo-class.css",
|
||||
"entity.name.type.object.console",
|
||||
"variable.language",
|
||||
"variable.parameter.function.language.special.self.python",
|
||||
"storage.type",
|
||||
"entity.other.attribute-name.pseudo-element.css"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#BBA0FF",
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"punctuation.definition.string.template"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#BBA0FF",
|
||||
"fontStyle": "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"entity.other.attribute-name.class.css",
|
||||
"entity.other.attribute-name.parent-selector-suffix.css",
|
||||
"entity.other.attribute-name.parent-selector-suffix.css punctuation.definition.entity.css",
|
||||
"meta.definition.function",
|
||||
"markup.underline.link",
|
||||
"markup.inserted",
|
||||
"meta.function-call",
|
||||
"meta.instance.constructor",
|
||||
"meta.attribute-with-value.class string",
|
||||
"string.other.ref",
|
||||
"string.other.restructuredtext.ref",
|
||||
"variable.function",
|
||||
"entity.name.type",
|
||||
"entity.name.function",
|
||||
"variable.other.object.property",
|
||||
"punctuation.separator.key-value",
|
||||
"meta.definition.function.js",
|
||||
"support.class.component.js",
|
||||
"entity.other.inherited-class",
|
||||
"support.class",
|
||||
"support.function.misc",
|
||||
"support.function",
|
||||
"entity.other.attribute-name.id.css",
|
||||
"entity.other.attribute-name.id.css punctuation",
|
||||
"entity.other.attribute-name.class.css punctuation",
|
||||
"meta.function-call support.function.construct",
|
||||
"entity.other.attribute-name"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#FFDD44"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.type.function.arrow",
|
||||
"settings": {
|
||||
"foreground": "#FFDD44",
|
||||
"fontStyle": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"entity.name.section",
|
||||
"constant.numeric.line-number.match.find-in-files",
|
||||
"markup.heading",
|
||||
"markup.changed",
|
||||
"meta.attribute-with-value.id string",
|
||||
"string",
|
||||
"punctuation.definition.string.begin",
|
||||
"punctuation.definition.string.end",
|
||||
"meta.attribute-selector"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#88CC44"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"keyword",
|
||||
"keyword.control",
|
||||
"constant",
|
||||
"constant.numeric",
|
||||
"constant.language",
|
||||
"constant.other.property",
|
||||
"constant.character.escape",
|
||||
"comment keyword.codetag.notation",
|
||||
"keyword.other.template.begin",
|
||||
"keyword.other.template.end",
|
||||
"markup.raw",
|
||||
"markup.deleted",
|
||||
"meta.property-list.css variable.other",
|
||||
"entity.name.constant.preprocessor",
|
||||
"meta.preprocessor",
|
||||
"punctuation.section.embedded",
|
||||
"punctuation.template-string.element.begin",
|
||||
"punctuation.template-string.element.end",
|
||||
"string.quoted.template punctuation.definition.string.begin",
|
||||
"string.quoted.template punctuation.definition.string.end",
|
||||
"meta.paragraph.markdown meta.dummy.line-break",
|
||||
"meta.paragraph.markdown meta.hard-line-break.markdown",
|
||||
"storage",
|
||||
"storage.class.restructuredtext.ref",
|
||||
"string.other.link.title",
|
||||
"string.other.link.description",
|
||||
"string.regexp punctuation.definition.string.begin",
|
||||
"string.regexp punctuation.definition.string.end",
|
||||
"variable.language.arguments",
|
||||
"storage.modifier",
|
||||
"keyword.operator.new",
|
||||
"keyword.operator",
|
||||
"keyword.operator.spread",
|
||||
"punctuation.section.embedded.end.php source.php",
|
||||
"support.function.construct",
|
||||
"meta.at-rule.keyframes",
|
||||
"meta.at-rule.keyframes entity.other.attribute-name"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#88BBFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"variable.other.substitution"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#88BBFF",
|
||||
"fontStyle": "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.function-call meta.function-call.arguments",
|
||||
"meta.function-call meta.arguments",
|
||||
"meta.function-call meta.group",
|
||||
"source.json meta.structure.dictionary",
|
||||
"meta.object.member",
|
||||
"punctuation.definition.group",
|
||||
"string.unquoted.label",
|
||||
"variable.other.member",
|
||||
"entity.name.class",
|
||||
"entity.name.namespace",
|
||||
"constant.other",
|
||||
"variable",
|
||||
"variable.other",
|
||||
"variable.other.constant.js",
|
||||
"text.find-in-files",
|
||||
"source",
|
||||
"entity.name.type.module",
|
||||
"source.sql support.function",
|
||||
"support.other.namespace.php"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#FFFFFF",
|
||||
"fontStyle": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"variable.parameter",
|
||||
"parameters variable.function"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#FFFFFF",
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"variable.other.property",
|
||||
"support.variable.property",
|
||||
"punctuation.separator.key-value",
|
||||
"meta.object-literal.key",
|
||||
"variable.other.object.property",
|
||||
"variable.object.property",
|
||||
"text.html.basic",
|
||||
"text.html.vue-html",
|
||||
"text.html.php",
|
||||
"text.html.derivative",
|
||||
"punctuation.definition.variable",
|
||||
"punctuation.definition.keyword.scss",
|
||||
"meta.jsx.children.js",
|
||||
"meta.tag.attributes keyword.operator.assignment",
|
||||
"support.type.vendored.property-name.css",
|
||||
"support.type.property-name.json",
|
||||
"variable.other.constant.property"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#BBBBCC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.property-name support.type",
|
||||
"settings": {
|
||||
"foreground": "#BBBBCC",
|
||||
"fontStyle": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"keyword.other.substitution",
|
||||
"keyword.operator.heading.restructuredtext",
|
||||
"keyword.operator.table.row.restructuredtext",
|
||||
"keyword.operator.table.data.restructuredtext",
|
||||
"punctuation",
|
||||
"markup.ignored",
|
||||
"markup.untracked",
|
||||
"meta.brace.round",
|
||||
"meta.brace.square",
|
||||
"meta.brace.curly",
|
||||
"meta.delimiter.comma.js",
|
||||
"meta.function-call.without-arguments.js",
|
||||
"meta.function-call.method.without-arguments.js",
|
||||
"source.scss",
|
||||
"string source punctuation.section.embedded",
|
||||
"punctuation.definition.tag.begin source",
|
||||
"source.php"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#9999AA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"comment",
|
||||
"comment punctuation"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#888899",
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "token.info-token",
|
||||
"settings": {
|
||||
"foreground": "#6796E6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "token.warn-token",
|
||||
"settings": {
|
||||
"foreground": "#CD9731"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "token.error-token",
|
||||
"settings": {
|
||||
"foreground": "#F44747"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "token.debug-token",
|
||||
"settings": {
|
||||
"foreground": "#B267E6"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,41 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Spark Client",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/Debug/bin/net6.0-windows/Spark.exe",
|
||||
"cwd": "${workspaceFolder}/build/Debug/bin/net6.0-windows",
|
||||
"enableStepFiltering": false,
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Spark Test",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/Debug/bin/net6.0/sparkcmd.exe",
|
||||
"args": ["test", "${input:test}"],
|
||||
"cwd": "${workspaceFolder}/build/Debug/bin/net6.0-windows",
|
||||
"enableStepFiltering": false,
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Attach (.NET Core)",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "test",
|
||||
"description": "The test(s) to run",
|
||||
"type": "promptString"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"cmake.configureOnOpen": false,
|
||||
"workbench.colorTheme": "Default Dark+",
|
||||
"C_Cpp.intelliSenseEngine": "Disabled",
|
||||
"files.associations": {
|
||||
"*.js": "javascript",
|
||||
"*.conf": "jsonc",
|
||||
"array": "cpp",
|
||||
"xtree": "cpp",
|
||||
"chrono": "cpp",
|
||||
"*.ipp": "cpp",
|
||||
"optional": "cpp",
|
||||
"regex": "cpp",
|
||||
"system_error": "cpp",
|
||||
"format": "cpp",
|
||||
"future": "cpp",
|
||||
"istream": "cpp",
|
||||
"memory": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"variant": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"xutility": "cpp",
|
||||
"xstring": "cpp",
|
||||
"string": "cpp",
|
||||
"cstdint": "cpp"
|
||||
},
|
||||
|
||||
"files.exclude": {
|
||||
"**/.g.cs": true,
|
||||
"**/.g.i.cs": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build Spark Client",
|
||||
"type": "process",
|
||||
"command": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Common7\\Tools\\VsDevCmd.bat",
|
||||
"args": [
|
||||
"-arch=x64",
|
||||
"-host_arch=x64",
|
||||
"&&",
|
||||
"${workspaceRoot}\\tools\\buildspark.cmd"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "new",
|
||||
"showReuseMessage": false,
|
||||
"clear": false,
|
||||
"close": false
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"label": "Run Spark Client",
|
||||
"type": "process",
|
||||
"command": "${workspaceRoot}\\build\\Debug\\bin\\net6.0-windows\\Spark.exe",
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(doc)
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/tools/Protobuf.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/safe_dep_glob.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Protobuf.cmake)
|
||||
|
||||
# DEPENDENCIES
|
||||
set(DOC_GLOBS
|
||||
|
||||
+1
-1
Submodule externals updated: 1dffb78a8e...99c440f568.
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "2.1.508",
|
||||
"version": "6.0.200",
|
||||
"rollForward": "latestMajor"
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,6 @@ 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", "{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40} = {9FA60215-26AF-3EB7-AB89-D4F4B126EF40}
|
||||
@@ -69,14 +67,6 @@ Global
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Release|x64.Build.0 = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.ActiveCfg = Release|x64
|
||||
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64
|
||||
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Debug|x64.Build.0 = Debug|x64
|
||||
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
|
||||
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
|
||||
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Release|x64.ActiveCfg = Release|x64
|
||||
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.Release|x64.Build.0 = Release|x64
|
||||
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
|
||||
{A1B7276B-8F81-3D58-A22E-870AF28DF9B9}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|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}.MinSizeRel|x64.ActiveCfg = Release|x64
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<EnableDefaultContentItems>False</EnableDefaultContentItems>
|
||||
<ShowMissingItemTypes>false</ShowMissingItemTypes>
|
||||
<Configuration Condition="'$(configuration)'==''">Debug</Configuration>
|
||||
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Output">
|
||||
|
||||
@@ -68,6 +68,16 @@
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="RemoveSystemRuntimeFromRefPack"
|
||||
BeforeTargets="_HandlePackageFileConflicts"
|
||||
Condition="'@(Reference -> WithMetadataValue('NugetPackageId', 'System.Runtime.Experimental'))' != ''">
|
||||
<ItemGroup>
|
||||
<Reference Remove="@(Reference)"
|
||||
Condition="$([System.String]::Copy(%(Reference.Identity)).Contains('System.Runtime.dll')) and
|
||||
'%(Reference.NuGetPackageId)' == 'Microsoft.NETCore.App.Ref'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="AfterClean"
|
||||
AfterTargets="Clean">
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts.api.analytics;
|
||||
|
||||
using mbts.analytics;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
@@ -12,9 +15,9 @@ namespace mbts.spark {
|
||||
readonly AnalyticsExpirationParameters _committed_value;
|
||||
readonly Property<bool> _is_selected;
|
||||
|
||||
readonly Property<SecurityExpiration> _driver;
|
||||
readonly Property<SecurityExpiration> _deliverable;
|
||||
readonly Property<SecurityExpiration> _roll_schedule;
|
||||
readonly Property<ExpirationDriverImmutable?> _driver;
|
||||
readonly Property<SecurityExpiration?> _deliverable;
|
||||
readonly Property<SecurityExpiration?> _roll_schedule;
|
||||
readonly Property<double> _roll_target;
|
||||
readonly Property<double> _riskless_rate;
|
||||
readonly Property<double> _settlement_price;
|
||||
@@ -33,18 +36,19 @@ namespace mbts.spark {
|
||||
|
||||
bool _is_last_row_of_month_group;
|
||||
|
||||
public AnalyticsExpirationParametersRowVM(AnalyticsExpirationParametersVM owner, AnalyticsExpirationParameters committed_value) {
|
||||
this.owner = owner ?? throw new ArgumentNullException(nameof(owner));
|
||||
_committed_value = committed_value ?? throw new ArgumentNullException(nameof(committed_value));
|
||||
public AnalyticsExpirationParametersRowVM(
|
||||
AnalyticsExpirationParametersVM owner, AnalyticsExpirationParameters committed_value) {
|
||||
this.owner = owner;
|
||||
_committed_value = committed_value;
|
||||
security = committed_value.security ?? throw new ArgumentException("security must be non-null", nameof(committed_value));
|
||||
|
||||
_is_selected = backing(nameof(is_selected), false);
|
||||
_is_selected.Changed += (oldval, newval) => is_selected_changed?.Invoke(newval);
|
||||
|
||||
var is_dirty_srcs = new List<IObservable<bool>>();
|
||||
(_driver, _is_driver_dirty) = mk_cell_props(nameof(driver), committed_value.driver_security);
|
||||
(_deliverable, _is_deliverable_dirty) = mk_cell_props(nameof(deliverable), committed_value.deliverable_security);
|
||||
(_roll_schedule, _is_roll_schedule_dirty) = mk_cell_props(nameof(roll_schedule), committed_value.roll_schedule_expiration);
|
||||
(_driver, _is_driver_dirty) = mk_cell_props<ExpirationDriverImmutable?>(nameof(driver), committed_value.driver);
|
||||
(_deliverable, _is_deliverable_dirty) = mk_cell_props<SecurityExpiration?>(nameof(deliverable), committed_value.deliverable_security);
|
||||
(_roll_schedule, _is_roll_schedule_dirty) = mk_cell_props<SecurityExpiration?>(nameof(roll_schedule), committed_value.roll_schedule_expiration);
|
||||
(_roll_target, _is_roll_target_dirty) = mk_cell_props(nameof(roll_target), committed_value.roll_target);
|
||||
(_riskless_rate, _is_riskless_rate_dirty) = mk_cell_props(nameof(riskless_rate), committed_value.riskless_rate);
|
||||
(_settlement_price, _is_settlement_price_dirty) = mk_cell_props(nameof(settlement_price), committed_value.settlement_price);
|
||||
@@ -64,13 +68,22 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<bool> is_selected_changed;
|
||||
public event Action<bool>? is_selected_changed;
|
||||
|
||||
public AnalyticsExpirationParametersVM owner { get; }
|
||||
public SecurityExpiration security { get; }
|
||||
public SecurityExpiration driver { get => _driver.Value; set => _driver.set(value); }
|
||||
public SecurityExpiration deliverable { get => _deliverable.Value; set => _deliverable.set(value); }
|
||||
public SecurityExpiration roll_schedule { get => _roll_schedule.Value; set => _roll_schedule.set(value); }
|
||||
public ExpirationDriverImmutable? driver {
|
||||
get => _driver.Value;
|
||||
set => _driver.set(value);
|
||||
}
|
||||
public SecurityExpiration? deliverable {
|
||||
get => _deliverable.Value;
|
||||
set => _deliverable.set(value);
|
||||
}
|
||||
public SecurityExpiration? roll_schedule {
|
||||
get => _roll_schedule.Value;
|
||||
set => _roll_schedule.set(value);
|
||||
}
|
||||
public AnalyticsExpirationParameters original_params => _committed_value;
|
||||
|
||||
public double roll_target { get => _roll_target.Value; set => _roll_target.set(value); }
|
||||
@@ -102,15 +115,15 @@ namespace mbts.spark {
|
||||
public AnalyticsExpirationParameters candidate_value => new(
|
||||
security,
|
||||
driver,
|
||||
deliverable.is_empty() ? null : deliverable,
|
||||
roll_schedule.is_empty() ? null : roll_schedule,
|
||||
deliverable?.is_empty() ?? false ? null : deliverable,
|
||||
roll_schedule?.is_empty() ?? false ? null : roll_schedule,
|
||||
roll_target,
|
||||
riskless_rate,
|
||||
settlement_price,
|
||||
delta_beta,
|
||||
vega_beta);
|
||||
|
||||
public IObservable<SecurityExpiration> observe_roll_schedule() =>
|
||||
public IObservable<SecurityExpiration?> observe_roll_schedule() =>
|
||||
_roll_schedule.observe_current(true);
|
||||
|
||||
public IObservable<bool> observe_roll_schedule_dirty() =>
|
||||
@@ -120,7 +133,7 @@ namespace mbts.spark {
|
||||
_is_dirty.observe_current(true);
|
||||
|
||||
public void revert() {
|
||||
driver = _committed_value.driver_security;
|
||||
driver = _committed_value.driver;
|
||||
deliverable = _committed_value.deliverable_security;
|
||||
roll_schedule = _committed_value.roll_schedule_expiration;
|
||||
roll_target = _committed_value.roll_target;
|
||||
@@ -133,11 +146,11 @@ namespace mbts.spark {
|
||||
public override string ToString() =>
|
||||
$"sec: {security}; drv: {driver}; dlv: {deliverable}; roll: {roll_schedule} @ {roll_target}";
|
||||
|
||||
public bool Equals(AnalyticsExpirationParametersRowVM other) =>
|
||||
public bool Equals(AnalyticsExpirationParametersRowVM? other) =>
|
||||
other is not null &&
|
||||
security.Equals(other.security);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
public override bool Equals(object? obj) =>
|
||||
Equals(obj as AnalyticsExpirationParametersRowVM);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace mbts.spark {
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts.api;
|
||||
using bts.api.analytics;
|
||||
using bts.collections;
|
||||
using bts.reactive;
|
||||
using bts.utils;
|
||||
|
||||
@@ -12,11 +15,15 @@
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Collections;
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
@@ -27,24 +34,32 @@
|
||||
readonly IBus _bus;
|
||||
readonly ObservableCollection<AnalyticsExpirationParameters> _parameters = new();
|
||||
readonly DeferrableActionWrapper _sync_visible_rows;
|
||||
readonly Property<AnalyticsExpirationParametersRowVM> _copy_row_source;
|
||||
readonly Property<AnalyticsExpirationParametersRowVM?> _copy_row_source;
|
||||
readonly Property<bool> _auto_copy_rolls_is_checked;
|
||||
readonly Property<bool> _auto_copy_rates_is_checked;
|
||||
readonly DelegateProperty<bool> _is_dirty;
|
||||
readonly HashSet<AnalyticsExpirationParametersRowVM> _selected_rows = new();
|
||||
readonly ObservableHashSet<AnalyticsExpirationParametersRowVM> _selected_rows = new();
|
||||
readonly DeferrableActionWrapper _raise_selected_rows_changed;
|
||||
readonly IRaiseCanExecuteChangedCommand _set_driver_to_nearest_exp_cmd;
|
||||
|
||||
AnalyticsExpirationParametersMap _parameter_map;
|
||||
bool _auto_copy_rolls_is_checked;
|
||||
bool _auto_copy_rates_is_checked;
|
||||
AnalyticsExpirationParametersMap? _parameter_map;
|
||||
(ICommand cmd, IDisposable can_exe_sub)? _smart_copy_roll_target_to_selection_cmd;
|
||||
(ICommand cmd, IDisposable can_exe_sub)? _smart_copy_riskless_rate_to_selection_cmd;
|
||||
|
||||
public AnalyticsExpirationParametersVM(IBus bus, AnalyticsExpirationParametersSettings startup_data) {
|
||||
_bus = bus ?? throw new ArgumentNullException(nameof(bus));
|
||||
public AnalyticsExpirationParametersVM(
|
||||
IBus bus, AnalyticsExpirationParametersSettings startup_data) {
|
||||
_bus = bus;
|
||||
_auto_copy_rates_is_checked = backing(nameof(auto_copy_rates_is_checked), false);
|
||||
_auto_copy_rolls_is_checked = backing(nameof(auto_copy_rolls_is_checked), false);
|
||||
_raise_selected_rows_changed = new(() => selected_rows_changed?.Invoke(_selected_rows));
|
||||
var selected_symbol_ref_count = new RefCountMap<string>();
|
||||
_sync_visible_rows = new(() =>
|
||||
visible_rows.sync_with(rows.Where(x => selected_symbol_ref_count.ContainsKey(x.security.symbol))));
|
||||
visible_rows.sync_with(
|
||||
rows.Where(x => selected_symbol_ref_count.ContainsKey(x.security.symbol))));
|
||||
non_options_with_empty = new ObservableAggregateCollection<SecurityExpiration>(
|
||||
new() { SecurityExpiration.empty }, non_options);
|
||||
_copy_row_source = backing<AnalyticsExpirationParametersRowVM>(nameof(copy_row_source));
|
||||
|
||||
_copy_row_source = backing<AnalyticsExpirationParametersRowVM?>(nameof(copy_row_source));
|
||||
_is_dirty = backing(nameof(is_dirty), () => rows.Any(r => r.is_dirty));
|
||||
|
||||
if (startup_data is not null) {
|
||||
@@ -55,11 +70,23 @@
|
||||
auto_copy_rates_is_checked = startup_data.auto_copy_rates_is_checked;
|
||||
}
|
||||
|
||||
var explicit_drivers = new ObservableCollection<ExpirationDriverImmutable>();
|
||||
drivers = ObservableAggregateCollection.create(
|
||||
new() { new ExpirationDriverImmutable(new() { TimeWeightedInterpolated = new() }) },
|
||||
explicit_drivers);
|
||||
_set_driver_to_nearest_exp_cmd = new Command(
|
||||
() => _set_driver_to_nearest_exp(_selected_rows), () => _selected_rows.Count > 0);
|
||||
commit_cmd = ObservantCommand.create(_commit, out var can_commit_observer);
|
||||
revert_cmd = ObservantCommand.create(_revert, out var can_revert_observer);
|
||||
selected_rows_changed += _ => _set_driver_to_nearest_exp_cmd.RaiseCanExecuteChanged();
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
_selected_rows.Subscribe((s, e) => _raise_selected_rows_changed.execute()),
|
||||
ObservableAutoWrapper.connect(_parameters, rows, p => new(this, p), r => r.Dispose()),
|
||||
ObservableAutoWrapper.connect(
|
||||
non_options,
|
||||
explicit_drivers,
|
||||
sec => new ExpirationDriverImmutable(new() { Security = sec.to_api_sec_exp() })),
|
||||
rows.Subscribe((s, e) => _sync_visible_rows.execute()),
|
||||
_bus.subscribe(SparkTags.ExpirationParametersInputTag).OnDispatcher().Subscribe(
|
||||
CompositeObserver.create(
|
||||
@@ -75,7 +102,8 @@
|
||||
}
|
||||
});
|
||||
}),
|
||||
ObservableAutoWrapper.connect_item_hooks(rows, r => r.observe_is_dirty().Subscribe(_ => _is_dirty.refresh())),
|
||||
ObservableAutoWrapper.connect_item_hooks(
|
||||
rows, r => r.observe_is_dirty().Subscribe(_ => _is_dirty.refresh())),
|
||||
_is_dirty.observe_current(true).OnDispatcher().Subscribe(can_revert_observer),
|
||||
Disposable.wrap_collection(rows),
|
||||
Disposable.Create(() => {
|
||||
@@ -84,12 +112,15 @@
|
||||
}));
|
||||
}
|
||||
|
||||
public event Action<IReadOnlySet<AnalyticsExpirationParametersRowVM>>? selected_rows_changed;
|
||||
|
||||
public ObservableCollection<SecurityExpiration> non_options { get; } = new();
|
||||
public IReadOnlyObservableCollection<ExpirationDriverImmutable> drivers { get; }
|
||||
public ObservableCollection<SecurityExpiration> non_options_with_empty { get; } = new();
|
||||
|
||||
public ObservableCollection<AnalyticsExpirationParametersRowVM> rows { get; } = new();
|
||||
public ObservableCollection<AnalyticsExpirationParametersRowVM> visible_rows { get; } = new();
|
||||
public AnalyticsExpirationParametersRowVM copy_row_source {
|
||||
public AnalyticsExpirationParametersRowVM? copy_row_source {
|
||||
get => _copy_row_source.Value;
|
||||
set => _copy_row_source.set(value);
|
||||
}
|
||||
@@ -100,18 +131,29 @@
|
||||
public bool is_dirty => _is_dirty.Value;
|
||||
|
||||
public bool auto_copy_rolls_is_checked {
|
||||
get => _auto_copy_rolls_is_checked;
|
||||
set => Set(ref _auto_copy_rolls_is_checked, value);
|
||||
get => _auto_copy_rolls_is_checked.Value;
|
||||
set => _auto_copy_rolls_is_checked.set(value);
|
||||
}
|
||||
public IObservable<bool> observe_auto_copy_rolls() =>
|
||||
_auto_copy_rolls_is_checked.observe_current(true);
|
||||
|
||||
public bool auto_copy_rates_is_checked {
|
||||
get => _auto_copy_rates_is_checked;
|
||||
set => Set(ref _auto_copy_rates_is_checked, value);
|
||||
get => _auto_copy_rates_is_checked.Value;
|
||||
set => _auto_copy_rates_is_checked.set(value);
|
||||
}
|
||||
public IObservable<bool> observe_auto_copy_rates() =>
|
||||
_auto_copy_rates_is_checked.observe_current(true);
|
||||
|
||||
public ICommand toggle_auto_copy_rates_cmd =>
|
||||
new Command(() => auto_copy_rates_is_checked = !auto_copy_rates_is_checked);
|
||||
public ICommand toggle_auto_copy_rolls_cmd =>
|
||||
new Command(() => auto_copy_rolls_is_checked = !auto_copy_rolls_is_checked);
|
||||
|
||||
public ICommand commit_cmd { get; }
|
||||
public ICommand revert_cmd { get; }
|
||||
|
||||
public ICommand set_driver_to_nearest_exp_cmd => _set_driver_to_nearest_exp_cmd;
|
||||
|
||||
public ICommand copy_driver_to_selection_cmd => _copy_to_selection_cmd((f, t) => t.driver = f.driver);
|
||||
public ICommand copy_deliverable_to_selection_cmd => _copy_to_selection_cmd((f, t) => t.deliverable = f.deliverable);
|
||||
public ICommand copy_roll_schedule_to_selection_cmd => _copy_to_selection_cmd((f, t) => t.roll_schedule = f.roll_schedule);
|
||||
@@ -138,7 +180,7 @@
|
||||
|
||||
public IEnumerable<SecurityExpiration> get_smart_copy_roll_exps(
|
||||
SecurityExpiration roll_schedule_expiration) {
|
||||
foreach (var (exp, exp_params) in _parameter_map?.exp_map.empty_if_null()) {
|
||||
foreach (var (exp, exp_params) in (_parameter_map?.exp_map).empty_if_null()) {
|
||||
if (exp_params.roll_schedule_expiration.Equals(roll_schedule_expiration)) {
|
||||
yield return exp;
|
||||
}
|
||||
@@ -152,17 +194,30 @@
|
||||
auto_copy_rolls_is_checked = auto_copy_rolls_is_checked
|
||||
};
|
||||
|
||||
Action<AnalyticsExpirationParametersRowVM> _copy_to_selection(Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
|
||||
void _set_driver_to_nearest_exp(IEnumerable<AnalyticsExpirationParametersRowVM> rows) {
|
||||
foreach (var r in rows) {
|
||||
if (drivers.Where(d => d.HasSecurity).TryGetMinElement(
|
||||
d => (r.security.expiration - d.Security.Expiration.date_time()).Ticks.abs(),
|
||||
out var nearest_exp)) {
|
||||
r.driver = nearest_exp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Action<AnalyticsExpirationParametersRowVM> _copy_to_selection(
|
||||
Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
|
||||
from_row => {
|
||||
foreach (var to_row in _selected_rows) {
|
||||
copy_to_row(from_row, to_row);
|
||||
}
|
||||
};
|
||||
|
||||
ICommand _copy_to_selection_cmd(Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
|
||||
ICommand _copy_to_selection_cmd(
|
||||
Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
|
||||
Command.create(_copy_to_selection(copy_to_row));
|
||||
|
||||
Action<AnalyticsExpirationParametersRowVM> _smart_copy_to_selection(Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
|
||||
Action<AnalyticsExpirationParametersRowVM> _smart_copy_to_selection(
|
||||
Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) =>
|
||||
from_row => {
|
||||
foreach (var to_row in _get_smart_copy_roll_target_rows(from_row)) {
|
||||
copy_to_row(from_row, to_row);
|
||||
@@ -171,21 +226,26 @@
|
||||
|
||||
(ICommand cmd, IDisposable can_exe_sub) _smart_copy_to_selection_cmd(
|
||||
Action<AnalyticsExpirationParametersRowVM, AnalyticsExpirationParametersRowVM> copy_to_row) {
|
||||
var cmd = ObservantCommand.create(
|
||||
_smart_copy_to_selection(copy_to_row), observe_can_execute, out var can_exe_observer);
|
||||
var can_exe_sub = _copy_row_source.observe_current(true).OnDispatcher().Subscribe(can_exe_observer);
|
||||
var copy_fn = _smart_copy_to_selection(copy_to_row);
|
||||
var cmd = ObservantCommand.create<AnalyticsExpirationParametersRowVM?>(
|
||||
r => copy_fn(r ?? throw new InvalidOperationException()),
|
||||
observe_can_execute,
|
||||
out var can_exe_observer);
|
||||
var can_exe_sub = _copy_row_source.observe_current(true).OnDispatcher()
|
||||
.Subscribe(can_exe_observer);
|
||||
return (cmd, can_exe_sub);
|
||||
|
||||
static IObservable<bool> observe_can_execute(AnalyticsExpirationParametersRowVM row) =>
|
||||
static IObservable<bool> observe_can_execute(AnalyticsExpirationParametersRowVM? row) =>
|
||||
row is null ?
|
||||
Observable.Return(false) :
|
||||
row.observe_roll_schedule()
|
||||
.CombineLatest(row.observe_roll_schedule_dirty(), (rs, rs_dirty) => (rs, rs_dirty))
|
||||
.Select(tup => !tup.rs.is_empty() && !tup.rs_dirty)
|
||||
.Select(tup => !tup.rs?.is_empty() ?? false && !tup.rs_dirty)
|
||||
.OnDispatcher();
|
||||
}
|
||||
|
||||
IEnumerable<AnalyticsExpirationParametersRowVM> _get_smart_copy_roll_target_rows(AnalyticsExpirationParametersRowVM row) {
|
||||
IEnumerable<AnalyticsExpirationParametersRowVM> _get_smart_copy_roll_target_rows(
|
||||
AnalyticsExpirationParametersRowVM row) {
|
||||
var target_exps = get_smart_copy_roll_exps(
|
||||
row.original_params.roll_schedule_expiration)
|
||||
.ToHashSet();
|
||||
@@ -207,11 +267,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
[MemberNotNull(nameof(_parameter_map))]
|
||||
void _on_parameter_map(AnalyticsExpirationParametersMap parameter_map) {
|
||||
bool is_first_update = _parameter_map is null;
|
||||
_parameter_map = parameter_map ?? throw new ArgumentNullException(nameof(parameter_map));
|
||||
_parameter_map = parameter_map;
|
||||
_parameters.sync_with(parameter_map.get_sorted_exps().Select(parameter_map.get_parameter));
|
||||
non_options.sync_with(parameter_map.get_sorted_exps().Where(exp => exp.security_type != SecurityType.Option));
|
||||
non_options.sync_with(
|
||||
parameter_map.get_sorted_exps().Where(exp => exp.security_type != SecurityType.Option));
|
||||
|
||||
// Set `is_last_row_of_month_group` for each row
|
||||
foreach (var exp_rows in rows.GroupBy(r => r.security.expiration.Month)) {
|
||||
|
||||
@@ -15,12 +15,33 @@
|
||||
<cv:DoubleTextConverter
|
||||
x:Key="RisklessRateConverter"
|
||||
Multiplier="100" />
|
||||
<shell:ExpTextConverter
|
||||
x:Key="ExpirationTextConverter"
|
||||
ShowSymbol="True"
|
||||
MapExp="True" />
|
||||
|
||||
<CollectionViewSource x:Key="Drivers" Source="{Binding drivers}" />
|
||||
<CollectionViewSource x:Key="NonOptionsWithEmpty" Source="{Binding non_options_with_empty}" />
|
||||
|
||||
<DataTemplate x:Key="DriverTemplate">
|
||||
<TextBlock Text="{Binding Converter={x:Static shell:AnalyticsExpirationParametersView.driver_display_text_converter}}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
<Trigger
|
||||
Property="Text"
|
||||
Value="None">
|
||||
<Setter
|
||||
Property="FontStyle"
|
||||
Value="Italic" />
|
||||
<Setter
|
||||
Property="Foreground"
|
||||
Value="Gray" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ExpirationTemplate">
|
||||
<TextBlock Text="{Binding Converter={StaticResource ExpirationTextConverter}}">
|
||||
<TextBlock Text="{Binding Converter={x:Static shell:AnalyticsExpirationParametersView.expiration_text_converter}}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Style.Triggers>
|
||||
@@ -226,7 +247,7 @@
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Header="Security"
|
||||
Binding="{Binding security, Converter={StaticResource ExpirationTextConverter}}"
|
||||
Binding="{Binding security, Converter={x:Static shell:AnalyticsExpirationParametersView.expiration_text_converter}}"
|
||||
IsReadOnly="True">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style
|
||||
@@ -289,7 +310,7 @@
|
||||
Value="True">
|
||||
<Setter
|
||||
Property="BorderBrush"
|
||||
Value="Red" />
|
||||
Value="{u:BTSColor UI.input_dirty_border}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
@@ -297,8 +318,9 @@
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox
|
||||
ItemsSource="{Binding DataContext.non_options, RelativeSource={RelativeSource AncestorType=c:DataGridEx}}"
|
||||
ItemTemplate="{StaticResource ExpirationTemplate}"
|
||||
IsSynchronizedWithCurrentItem="False"
|
||||
ItemsSource="{Binding Source={StaticResource Drivers}}"
|
||||
ItemTemplate="{StaticResource DriverTemplate}"
|
||||
SelectedItem="{Binding driver, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="2" />
|
||||
</DataTemplate>
|
||||
@@ -328,7 +350,8 @@
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox
|
||||
ItemsSource="{Binding DataContext.non_options_with_empty, RelativeSource={RelativeSource AncestorType=c:DataGridEx}}"
|
||||
IsSynchronizedWithCurrentItem="False"
|
||||
ItemsSource="{Binding Source={StaticResource NonOptionsWithEmpty}}"
|
||||
ItemTemplate="{StaticResource ExpirationTemplate}"
|
||||
SelectedItem="{Binding deliverable, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="2" />
|
||||
@@ -359,7 +382,8 @@
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<ComboBox
|
||||
ItemsSource="{Binding DataContext.non_options_with_empty, RelativeSource={RelativeSource AncestorType=c:DataGridEx}}"
|
||||
IsSynchronizedWithCurrentItem="False"
|
||||
ItemsSource="{Binding Source={StaticResource NonOptionsWithEmpty}}"
|
||||
ItemTemplate="{StaticResource ExpirationTemplate}"
|
||||
SelectedItem="{Binding roll_schedule, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="2" />
|
||||
|
||||
+24
-30
@@ -1,8 +1,10 @@
|
||||
namespace mbts.ui.shell {
|
||||
using bts.api;
|
||||
#nullable enable
|
||||
namespace mbts.ui.shell {
|
||||
using bts.api.analytics;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
|
||||
using System;
|
||||
@@ -16,8 +18,8 @@
|
||||
/// Interaction logic for AnalyticsExpirationParametersView.xaml
|
||||
/// </summary>
|
||||
partial class AnalyticsExpirationParametersView : UserControl {
|
||||
IDisposable _context_menu_op;
|
||||
IDisposable _data_context_hooks;
|
||||
IDisposable? _context_menu_op;
|
||||
IDisposable? _data_context_hooks;
|
||||
|
||||
public AnalyticsExpirationParametersView() {
|
||||
InitializeComponent();
|
||||
@@ -31,6 +33,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
public static ExpirationTextConverter expiration_text_converter =>
|
||||
new() {
|
||||
ShowSymbol = true,
|
||||
ShowType = true,
|
||||
MapExp = true
|
||||
};
|
||||
|
||||
public static IValueConverter driver_display_text_converter =>
|
||||
ValueConverter.create<ExpirationDriverImmutable?, string?>(
|
||||
d => d.HasValue ?
|
||||
d.Value.ModeCase switch {
|
||||
ExpirationDriver.ModeOneofCase.Security =>
|
||||
expiration_text_converter.convert(d.Value.Security.to_sec_exp()),
|
||||
ExpirationDriver.ModeOneofCase.TimeWeightedInterpolated => "Calculate",
|
||||
_ => null
|
||||
} :
|
||||
null);
|
||||
|
||||
void _grid_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) {
|
||||
Disposable.dispose(ref _context_menu_op);
|
||||
if (e.OriginalSource is not FrameworkElement el || DataContext is not AnalyticsExpirationParametersVM vm) {
|
||||
@@ -47,30 +67,4 @@
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
class ExpTextConverter : IValueConverter {
|
||||
public ExpTextConverter() {
|
||||
NullOrEmptyText = "None";
|
||||
}
|
||||
|
||||
public bool ShowType { get; set; }
|
||||
public bool ShowSymbol { get; set; }
|
||||
public bool MapExp { get; set; }
|
||||
public string NullOrEmptyText { get; set; }
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
|
||||
var exp = value as SecurityExpiration;
|
||||
if (exp == null || exp.is_empty()) {
|
||||
return NullOrEmptyText;
|
||||
}
|
||||
return ShowSymbol ?
|
||||
SecurityFormatter.format_future(exp.symbol, exp.expiration) :
|
||||
DateFormatter.Format(exp.expiration, DateFormat.MonthCodeLastDigitYear);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
namespace mbts.spark {
|
||||
namespace mbts.spark {
|
||||
using bts.api;
|
||||
using bts.spark;
|
||||
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
@@ -38,11 +39,30 @@
|
||||
|
||||
public void thread_init() {
|
||||
_vm = new AnalyticsExpirationParametersVM(_bus, _init_settings);
|
||||
_view = new AnalyticsExpirationParametersView { DataContext = _vm };
|
||||
|
||||
var auto_copy_rolls_cmd = new SparkCommandCommand(
|
||||
"Auto Copy Rolls", null, _vm.toggle_auto_copy_rolls_cmd);
|
||||
_vm.SubscribeDisposed(_vm.observe_auto_copy_rolls().Subscribe(v =>
|
||||
auto_copy_rolls_cmd.icon = v ? Icons.RecurringSchedule : Icons.RecurringScheduleGray));
|
||||
|
||||
var auto_copy_rates_cmd = new SparkCommandCommand(
|
||||
"Auto Copy Rates", null, _vm.toggle_auto_copy_rates_cmd);
|
||||
_vm.SubscribeDisposed(_vm.observe_auto_copy_rates().Subscribe(v =>
|
||||
auto_copy_rates_cmd.icon = v ? Icons.FinancialGrowth : Icons.FinancialGrowthGray));
|
||||
|
||||
_commands = new() {
|
||||
new SparkCommandCommand("Commit", Icons.Disk, _vm.commit_cmd, new KeyGesture(Key.S, ModifierKeys.Control)),
|
||||
new SparkCommandCommand("Revert", Icons.UndoCircular, _vm.revert_cmd, new KeyGesture(Key.Z, ModifierKeys.Control)),
|
||||
auto_copy_rates_cmd,
|
||||
auto_copy_rolls_cmd,
|
||||
new SparkCommandCommand(
|
||||
"Set Driver to Nearest Exp", null, _vm.set_driver_to_nearest_exp_cmd) {
|
||||
show_in_toolbar = false,
|
||||
show_in_context_menu = true
|
||||
},
|
||||
};
|
||||
|
||||
_view = new AnalyticsExpirationParametersView { DataContext = _vm };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using bts;
|
||||
using mbts.analytics;
|
||||
using bts.api.analytics;
|
||||
using System.Reactive.Linq;
|
||||
using bts.api;
|
||||
using System.Threading;
|
||||
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
// 1. Reads exp params from the network and publishes
|
||||
// it to the BB
|
||||
// 2. Listens to ExpirationParametersCommitTag and
|
||||
// writes the calls into persistence to save the data
|
||||
public class AnalyticsParametersManager : IDisposable {
|
||||
private readonly ThreadedBlackboard _bb;
|
||||
private AnalyticsService _analytics_service;
|
||||
private List<IDisposable> _disposables = new List<IDisposable>();
|
||||
private IDictionary<mbts.SecurityExpiration, AnalyticsExpirationParameters>
|
||||
_data = new Dictionary<mbts.SecurityExpiration, AnalyticsExpirationParameters>();
|
||||
private readonly ITag<IEnumerable<AnalyticsExpirationParameters>>
|
||||
_in_bb_exp_params_commit_tag = SparkTags.ExpirationParametersCommitTag;
|
||||
private readonly ITag<SystemContractData> _in_contracts_tag = SparkTags.SystemContractsTag;
|
||||
private readonly ITag<AnalyticsExpirationParametersMap> _out_exp_params_tag =
|
||||
SparkTags.ExpirationParametersInputTag;
|
||||
private HighPerformanceTimer _timer = new HighPerformanceTimer();
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.analytics;
|
||||
|
||||
public AnalyticsParametersManager(ThreadedBlackboard bb,
|
||||
AnalyticsService analytics_server) {
|
||||
_bb = bb;
|
||||
_analytics_service = analytics_server;
|
||||
var exp_params_sub = _analytics_service.subscribei_exp_parameters()
|
||||
.ObserveOn(SynchronizationContext.Current)
|
||||
.Subscribe(_on_exp_parameters);
|
||||
_disposables.Add(exp_params_sub);
|
||||
_bb.subscribe(_in_bb_exp_params_commit_tag);
|
||||
_bb.subscribe(_in_contracts_tag);
|
||||
_bb.data_callback += _on_bb_data;
|
||||
}
|
||||
using mbts.analytics;
|
||||
|
||||
private void _on_bb_data(object tag, object data) {
|
||||
if (_in_contracts_tag.Equals(tag)) {
|
||||
_assemble_and_publish();
|
||||
} else if (_in_bb_exp_params_commit_tag == tag) {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// Reads exp params from the network and publishes it to the bus; listens to
|
||||
/// <see cref="SparkTags.ExpirationParametersCommitTag"/> and persists the data
|
||||
/// </summary>
|
||||
public sealed class AnalyticsParametersManager : IDisposable {
|
||||
IDisposable? _dispose;
|
||||
|
||||
public AnalyticsParametersManager(IBus bus, AnalyticsService analytics_service) {
|
||||
var contracts_src = bus.subscribe(SparkTags.SystemContractsTag);
|
||||
|
||||
var exp_params_in_src = analytics_service.subscribei_exp_parameters()
|
||||
.Select(upd => upd.all
|
||||
.SelectMany(ep => ep.ParamList
|
||||
.Select(ep => new AnalyticsExpirationParameters(ep)))
|
||||
.ToDictionary(ep => ep.security));
|
||||
|
||||
var exp_params_out_src = bus.subscribe(SparkTags.ExpirationParametersCommitTag)
|
||||
.Select(eps => eps
|
||||
.Where(ep => ep.security.expiration.Date >= DateTime.Today - TimeSpan.FromDays(1)));
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
exp_params_in_src.CombineLatest(contracts_src)
|
||||
.Select(tup => new AnalyticsExpirationParametersMap(tup.First, tup.Second))
|
||||
.ObserveOn(SynchronizationContext.Current ?? throw new InvalidOperationException())
|
||||
.Subscribe(bus.post_callback(SparkTags.ExpirationParametersInputTag)),
|
||||
exp_params_out_src.Subscribe(save_exp_params));
|
||||
|
||||
void save_exp_params(IEnumerable<AnalyticsExpirationParameters> exp_params) {
|
||||
// listens to the commit tag and saves it
|
||||
IEnumerable<AnalyticsExpirationParameters> exp_params =
|
||||
data as IEnumerable<AnalyticsExpirationParameters>;
|
||||
exp_params = exp_params.Where(e => e.security.expiration.Date >=
|
||||
DateTime.Now.Date.Subtract(TimeSpan.FromDays(1)));
|
||||
bts.Log.Gen.info(
|
||||
String.Format("AnalyticsParametersManager._on_bb_data received commit count:{0}",
|
||||
exp_params.Count()));
|
||||
ExpirationParameterEntryList to_save = new ExpirationParameterEntryList();
|
||||
Log.Gen.info($"AnalyticsParametersManager received commit req. count:{exp_params.Count()}");
|
||||
var to_save = new ExpirationParameterEntryList();
|
||||
to_save.ParamList.AddRange(exp_params.Select(i => i.to_exp_param_entry()));
|
||||
_timer.start();
|
||||
_analytics_service.set_exp_parameters(to_save);
|
||||
_timer.stop();
|
||||
bts.Log.Gen.info(
|
||||
String.Format("AnalyticsParametersManager saved exp_params count:{0} t:{1} msecs",
|
||||
exp_params.Count(), _timer.lastIntervalTime));
|
||||
Log.Gen.record(
|
||||
null,
|
||||
(dt, elapsed, res) => {
|
||||
var msg = "AnalyticsParametersManager saved exp_params. " +
|
||||
$"count:{exp_params.Count()} t:{elapsed.TotalMilliseconds:0.000}ms";
|
||||
return (LogLevel.Info, msg);
|
||||
},
|
||||
() => analytics_service.set_exp_parameters(to_save));
|
||||
}
|
||||
}
|
||||
|
||||
void _on_exp_parameters(bts.api.core.Update<ExpirationParameterEntryListImmutable, int> update) {
|
||||
_data = new Dictionary<mbts.SecurityExpiration, AnalyticsExpirationParameters>();
|
||||
bts.Log.Gen.info(String.Format("AnalyticsParametersManager._on_exp_parameters received count:{0}",
|
||||
update.all.Count()));
|
||||
bts.Log.assert(update.all.Count() == 0 || update.all.Count() == 1);
|
||||
foreach (ExpirationParameterEntryListImmutable l in update.all) {
|
||||
foreach (ExpirationParameterEntryImmutable e in l.ParamList) {
|
||||
AnalyticsExpirationParameters new_entry = new AnalyticsExpirationParameters(e);
|
||||
_data[new_entry.security] = new_entry;
|
||||
}
|
||||
break;
|
||||
}
|
||||
Log.Gen.info($"AnalyticsParametersManager._on_exp_parameters count: {_data.Count}");
|
||||
_assemble_and_publish();
|
||||
// _bb.post(_out_exp_params_tag, new AnalyticsExpirationParametersMap(_data));
|
||||
}
|
||||
|
||||
private void _assemble_and_publish() {
|
||||
if (_data != null) {
|
||||
SystemContractData sc_data = _bb.read_state(_in_contracts_tag);
|
||||
if (sc_data != null) {
|
||||
_bb.post(_out_exp_params_tag, new AnalyticsExpirationParametersMap(_data, sc_data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
public void Dispose() {
|
||||
_bb.data_callback -= _on_bb_data;
|
||||
foreach (IDisposable d in _disposables) {
|
||||
d.Dispose();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
@@ -125,8 +125,8 @@ namespace mbts.spark {
|
||||
if (_is_different(res)) {
|
||||
_last_published = new OptionDriverPriceMap(_sec_tup.security_name, res);
|
||||
var to_fire = new OptionDriverPriceMap(_sec_tup.security_name, res);
|
||||
bts.Log.Gen.info("SingleOptionDriverPricePublisher._compute_and_fire \n" +
|
||||
$"map:{to_fire.log_string()}");
|
||||
// bts.Log.Gen.info("SingleOptionDriverPricePublisher._compute_and_fire \n" +
|
||||
// $"map:{to_fire.log_string()}");
|
||||
_logger.stop_default_timer();
|
||||
_bb.post(SparkTags.OptionDriverBBOTag(_sec_tup.security_name),
|
||||
new OptionDriverPriceMap(_sec_tup.security_name, res));
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using mbts.analytics;
|
||||
using bts;
|
||||
using bts.api.analytics;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using bts.api;
|
||||
using System.Collections;
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.analytics;
|
||||
|
||||
using mbts.analytics;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace mbts.spark {
|
||||
// For a single VolSurface this class
|
||||
// 1. Listents to commmits and calls the service to persist data
|
||||
// 2. Gathers the vol surface input from the nw and publishes it
|
||||
@@ -78,11 +80,8 @@ namespace mbts.spark {
|
||||
_snp_shot_map.Values);
|
||||
}
|
||||
|
||||
static VolSurfaceInput _to_vs_input(VolSurfaceEntryImmutable vs) {
|
||||
byte[] vs_bytes = vs.Volsurface.ToByteArray();
|
||||
return
|
||||
mbts.analytics.VolSurfaceUtilityFunctions.deserialize_proto<VolSurfaceInput>(vs_bytes);
|
||||
}
|
||||
static VolSurfaceInput _to_vs_input(VolSurfaceEntryImmutable vs) =>
|
||||
ProtobufSerializationHelper.deserialize<VolSurfaceInput>(vs.Volsurface.ToByteArray());
|
||||
|
||||
private void _on_bb_data(object tag, object data) {
|
||||
if (_in_bb_committ_tag.Equals(tag)) {
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.Reactive.Linq;
|
||||
using System.Collections;
|
||||
using mbts.spark;
|
||||
using System.DirectoryServices.ActiveDirectory;
|
||||
using Spark.Core.Protobuf;
|
||||
// using mbts.bb;
|
||||
|
||||
namespace mbts.spark {
|
||||
@@ -53,6 +54,7 @@ namespace mbts.spark {
|
||||
// 2. Listens to the activation tag (also published by the UI) and
|
||||
// creates the SingleVolSurfaceManager for the VS
|
||||
public class VolSurfaceBusManager : IDisposable {
|
||||
private List<IDisposable> _disps = new List<IDisposable>();
|
||||
private ThreadedBlackboard _bb;
|
||||
private bts.api.analytics.AnalyticsService _analytics_service;
|
||||
// in tags
|
||||
@@ -77,7 +79,7 @@ namespace mbts.spark {
|
||||
bts.api.analytics.AnalyticsService analytics_service) {
|
||||
bts.Log.Ui.info("VSBusManager cons tid:{VolSurfaceTroubleshootingFunctions.tid()}");
|
||||
_bus = bus;
|
||||
_bb = new ThreadedBlackboard(bus);
|
||||
_bb = new ThreadedBlackboard("AW2", bus);
|
||||
_analytics_service = analytics_service;
|
||||
// subs to bb
|
||||
_bb.subscribe(_in_scdata_tag);
|
||||
@@ -96,7 +98,7 @@ namespace mbts.spark {
|
||||
.Subscribe(_on_vs_usage_update);
|
||||
_subs.Add(vs_sub);
|
||||
_subs.Add(vs_usage_sub);
|
||||
_subs.Add(new AnalyticsParametersManager(_bb, _analytics_service));
|
||||
_subs.Add(new AnalyticsParametersManager(bus, _analytics_service));
|
||||
_subs.Add(new ResourceAllocator(bus, _bb));
|
||||
_subs.Add(new SymbolAggregationPublisher<OptionDriverPriceMap>(
|
||||
_bb, (sym) => SparkTags.OptionDriverBBOTag(sym),
|
||||
@@ -106,38 +108,47 @@ namespace mbts.spark {
|
||||
SparkTags.AllInterpGreeks));
|
||||
_subs.Add(new LiveTheoPublisher(_bb));
|
||||
_subs.Add(new VolSurfaceInterpGreeksMultiplexer(_bb));
|
||||
//_subs.Add(new AnalyticsServiceTester(_bb));
|
||||
// TODO: temporatily publish values
|
||||
VolSurfaceUsageList ulist = new VolSurfaceUsageList();
|
||||
ulist.UsageList.Add(new VolSurfaceUsageEntry() {
|
||||
VsId = 6,
|
||||
SecTup = new bts.api.SecurityTuple() {
|
||||
Type = SecurityType.Option,
|
||||
Venue = VolSurfaceTemp.get_default_venue(SecurityType.Option, "BTC"),
|
||||
Symbol = "BTC"
|
||||
// usage
|
||||
var list_sub = _bus.subscribe(SparkTags.VolSurfaceListTag).OnDispatcher().Subscribe(t => {
|
||||
VolSurfaceUsageList ulist = new VolSurfaceUsageList();
|
||||
IDictionary<string, int> name_to_ids = new Dictionary<string, int>();
|
||||
foreach (var kv in t) {
|
||||
name_to_ids[kv.Value] = kv.Key;
|
||||
}
|
||||
});
|
||||
ulist.UsageList.Add(new VolSurfaceUsageEntry() {
|
||||
VsId = 7,
|
||||
SecTup = new bts.api.SecurityTuple() {
|
||||
Type = SecurityType.Option,
|
||||
Venue = VolSurfaceTemp.get_default_venue(SecurityType.Option, "ETH"),
|
||||
Symbol = "ETH"
|
||||
Log.Ui.info($"VolSurfaceUsageList name_to_ids count: {name_to_ids.Count}");
|
||||
var vs_usage = mbts.config.Configuration.get<bts.config.toggles.VolSurfaceUsage>();
|
||||
Log.Ui.info($"VolSurfaceUsageList vs_usage acount: {vs_usage.vol_surface_usage.Count}");
|
||||
foreach (var u in vs_usage.vol_surface_usage) {
|
||||
string option_name = u.option_name;
|
||||
string vs_name = u.vs_name;
|
||||
int vs_key;
|
||||
if (name_to_ids.TryGetValue(vs_name, out vs_key)) {
|
||||
ulist.UsageList.Add(new VolSurfaceUsageEntry() {
|
||||
VsId = vs_key,
|
||||
SecTup = new bts.api.SecurityTuple {
|
||||
Type = SecurityType.Option,
|
||||
Venue = Venue.Deribit,
|
||||
Symbol = option_name
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Log.Gen.error($"Could not find vol surface with name: {vs_name}");
|
||||
}
|
||||
}
|
||||
var list_imm = new VolSurfaceUsageListImmutable(ulist);
|
||||
Log.Ui.info(list_imm.log_string());
|
||||
_bb.post(SparkTags.VolSurfaceUsageTag, list_imm);
|
||||
});
|
||||
_bb.post(SparkTags.VolSurfaceUsageTag, new VolSurfaceUsageListImmutable(ulist));
|
||||
_disps.Add(list_sub);
|
||||
}
|
||||
|
||||
public ThreadedBlackboard bb() {
|
||||
return _bb;
|
||||
}
|
||||
|
||||
void _on_vs_usage_update(Update<VolSurfaceUsageListImmutable, int> update) {
|
||||
// TODO
|
||||
return;
|
||||
// int vs_count = update.all.Count();
|
||||
// Log.Gen.info($"VSBusManager._on_vs_usage_update " +
|
||||
// $"tid:{Thread.CurrentThread.ManagedThreadId} count:{update.all.Count()}");
|
||||
// Log.assert(vs_count <= 1);
|
||||
// if (update.all.TryGetSingle(out var to_write)) {
|
||||
// _bb.post(SparkTags.VolSurfaceUsageTag, to_write);
|
||||
// }
|
||||
}
|
||||
|
||||
void _on_vss(bts.api.core.Update<VolSurfaceEntryImmutable, UInt64> update) {
|
||||
@@ -172,7 +183,7 @@ namespace mbts.spark {
|
||||
// callback
|
||||
mbts.analytics.VolSurfaceSettings vs_settings = data as mbts.analytics.VolSurfaceSettings;
|
||||
bts.Log.Gen.info(String.Format("VSBusManager._on_bb_update received vs creation tag vs:{0}",
|
||||
VolSurfaceUtilityFunctions.serialize_proto_to_text(vs_settings)));
|
||||
ProtobufSerializationHelper.serialize_to_string(vs_settings)));
|
||||
_waiting_creations.Enqueue(vs_settings);
|
||||
} else if (_in_bb_delete_tag.Equals(tag)) {
|
||||
int vs_id = (int)data;
|
||||
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using mbts.analytics;
|
||||
using bts.api;
|
||||
using mbts.ui;
|
||||
using MathNet.Numerics.Providers.FourierTransform;
|
||||
using BCG;
|
||||
|
||||
namespace mbts.spark {
|
||||
// Publishes theos for all options (on the side, also publishes the theos for TC2)
|
||||
@@ -15,12 +17,15 @@ namespace mbts.spark {
|
||||
private readonly HighPerformanceTimer _timer = new HighPerformanceTimer();
|
||||
private readonly PerfStatsLogger _logger = new PerfStatsLogger(50, 100);
|
||||
private readonly string _log_id = "LiveTheoPublisher";
|
||||
private DateTime _last_future_theos_published = DateTime.MinValue;
|
||||
private TimeSpan _futures_theos_publish_period = TimeSpan.FromMilliseconds(300);
|
||||
|
||||
public LiveTheoPublisher(ThreadedBlackboard bb) {
|
||||
_bb = bb;
|
||||
_bb.subscribe(_greeks_tag);
|
||||
_bb.subscribe(_drivers_tag);
|
||||
_bb.data_callback += _on_data;
|
||||
_bb.timer_callback += _on_timer;
|
||||
_interp_and_publish();
|
||||
}
|
||||
|
||||
@@ -31,6 +36,10 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
private void _on_timer() {
|
||||
_publish_future_theos();
|
||||
}
|
||||
|
||||
private void _log_interpgreeks(
|
||||
IReadOnlyDictionary<string, OptionTheoInterpolationData> interp_greeks) {
|
||||
string log_msg = String.Empty;
|
||||
@@ -127,18 +136,18 @@ namespace mbts.spark {
|
||||
var k_theos = new Dictionary<SecurityStrike, TheoPoint>();
|
||||
foreach (var kv in sc_data.strike_map) {
|
||||
foreach (SecurityStrike k in kv.Value) {
|
||||
var cp_id = k.call_put_id;
|
||||
var (call_id, put_id) = k.call_put_id;
|
||||
var driver = _get_option_driver(driver_map, k);
|
||||
var tp = _interp_theo(interp_greeks, k, driver.get_driver());
|
||||
k_theos[k] = tp;
|
||||
// put it in the output map
|
||||
if (cp_id.Item1.HasValue) {
|
||||
res.Add(cp_id.Item1.Value,
|
||||
if (call_id.HasValue) {
|
||||
res.Add(call_id.Value,
|
||||
new GreeksTuple(tp.vol, tp.driver, tp.call, tp.cdelta, tp.cgamma,
|
||||
tp.cvega, tp.c_theta, Double.NaN));
|
||||
}
|
||||
if (cp_id.Item2.HasValue) {
|
||||
res.Add(cp_id.Item2.Value,
|
||||
if (put_id.HasValue) {
|
||||
res.Add(put_id.Value,
|
||||
new GreeksTuple(tp.vol, tp.driver, tp.put, tp.pdelta, tp.pgamma,
|
||||
tp.pvega, tp.p_theta, Double.NaN));
|
||||
}
|
||||
@@ -153,8 +162,31 @@ namespace mbts.spark {
|
||||
_publish_tc2_theos(k_theos);
|
||||
}
|
||||
|
||||
// recomputes and publishes them periodicially
|
||||
void _publish_future_theos() {
|
||||
if (DateTime.Now - _last_future_theos_published < _futures_theos_publish_period)
|
||||
return;
|
||||
_last_future_theos_published = DateTime.Now;
|
||||
var rv = new Dictionary<SecurityExpiration, FutureTheoStructure>();
|
||||
SystemContractData sc_data = _bb.read_state(SparkTags.SystemContractsTag);
|
||||
if (sc_data == null)
|
||||
return;
|
||||
foreach (var kv in sc_data.exp_map) {
|
||||
var id = kv.Value;
|
||||
SecurityMD sec_md;
|
||||
if (sc_data.sec_md_map.TryGetValue(id, out sec_md)) {
|
||||
if (sec_md.security.HasFuture) {
|
||||
SecurityExpiration exp = kv.Key;
|
||||
double pr = sec_md.md.fixing_price;
|
||||
rv.Add(exp, new FutureTheoStructure(exp.expiration, pr, pr, 1, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
_bb.post(SparkTags.TC2Tags.LiveFutureTheosTag, rv);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
_bb.data_callback -= _on_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ namespace mbts.spark {
|
||||
var exp = kv.Key;
|
||||
if (!temp.ContainsKey(kv.Key)) {
|
||||
temp.Add(kv.Key,
|
||||
new AnalyticsExpirationParameters(exp, empty, empty, empty, 0, 0, 0, 0, 0));
|
||||
new AnalyticsExpirationParameters(exp, null, empty, empty, 0, 0, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
exp_map = temp;
|
||||
|
||||
@@ -1,58 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using mbts.analytics;
|
||||
using bts.api;
|
||||
using bts.api.persistence;
|
||||
using System.Linq;
|
||||
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using mbts.analytics;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
// All driver prices (along with some more info) for an option symbol
|
||||
public class OptionDriverPriceMap {
|
||||
public readonly String option_symbol = null;
|
||||
public readonly IDictionary<SecurityExpiration, DriverMD> exp_price_map = null;
|
||||
public readonly string option_symbol;
|
||||
public readonly IDictionary<SecurityExpiration, DriverMD> exp_price_map;
|
||||
|
||||
Dictionary<SecurityExpiration, DriverMD>? _unexpired_exp_price_map_backing;
|
||||
|
||||
public OptionDriverPriceMap(
|
||||
string option_symbol_a,
|
||||
IDictionary<SecurityExpiration, DriverMD> exp_price_map_a) {
|
||||
option_symbol = option_symbol_a;
|
||||
exp_price_map = exp_price_map_a;
|
||||
}
|
||||
public IDictionary<SecurityExpiration, DriverMD> unexpired_exp_price_map {
|
||||
get {
|
||||
return exp_price_map.Where(
|
||||
kv => kv.Key.expiration >= DateTime.Now.Date).ToDictionary(
|
||||
kv => kv.Key, kv => kv.Value);
|
||||
}
|
||||
string option_symbol, IDictionary<SecurityExpiration, DriverMD> exp_price_map) {
|
||||
this.option_symbol = option_symbol;
|
||||
this.exp_price_map = exp_price_map;
|
||||
}
|
||||
|
||||
public IDictionary<SecurityExpiration, DriverMD> unexpired_exp_price_map =>
|
||||
_unexpired_exp_price_map_backing ??=
|
||||
exp_price_map.Where(kv => kv.Key.expiration >= DateTime.Now.Date).ToDictionary();
|
||||
|
||||
// this is the BS needed because of ignoring exchange
|
||||
public DriverMD get_driver_md(DateTime exp) {
|
||||
foreach (var kv in exp_price_map) {
|
||||
if (kv.Key.expiration == exp) {
|
||||
return kv.Value;
|
||||
}
|
||||
}
|
||||
return DriverMD.empty;
|
||||
}
|
||||
public DriverMD get_driver_md(SecurityExpiration exp) {
|
||||
if (exp_price_map.TryGetValue(exp, out var d))
|
||||
return d;
|
||||
return DriverMD.empty;
|
||||
}
|
||||
public DriverMD get_driver_md(DateTime exp) =>
|
||||
exp_price_map.FirstOrDefault(kv => kv.Key.expiration == exp).Value ?? DriverMD.empty;
|
||||
|
||||
public DriverMD get_driver_md(SecurityExpiration exp) =>
|
||||
exp_price_map.GetValueOrDefault(exp) ?? DriverMD.empty;
|
||||
|
||||
public static IDictionary<VolSurfaceExpiration, DriverMD> get_vs_price_map(
|
||||
IReadOnlyDictionary<string, OptionDriverPriceMap> all_sym_map,
|
||||
Func<string, bool> sym_filter = null) {
|
||||
var rv = new Dictionary<VolSurfaceExpiration, DriverMD>();
|
||||
if (all_sym_map != null)
|
||||
foreach (var kv in all_sym_map) {
|
||||
string sym = kv.Key;
|
||||
OptionDriverPriceMap sym_map = kv.Value;
|
||||
if (sym_filter == null || sym_filter(sym)) {
|
||||
foreach (var kv_int in sym_map.exp_price_map) {
|
||||
rv[new VolSurfaceExpiration(kv_int.Key)] = kv_int.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
Func<string, bool>? sym_filter = null
|
||||
) {
|
||||
sym_filter ??= _ => true;
|
||||
return all_sym_map.SafeWhere(kv => kv.Value is not null && sym_filter(kv.Key))
|
||||
.SelectMany(kv => kv.Value.exp_price_map)
|
||||
.ToDictionaryCrushed(kv => kv.Key.to_vs_exp(), kv => kv.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -173,7 +173,7 @@ namespace mbts.spark {
|
||||
}
|
||||
IDictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>> rv_data =
|
||||
new Dictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>>();
|
||||
return Tuple.Create(exp.to_security_expiration(), rv_data);
|
||||
return Tuple.Create(exp.to_security_expiration(type: SecurityType.Option), rv_data);
|
||||
}
|
||||
|
||||
internal IDictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>> get_option_bbo(
|
||||
@@ -182,12 +182,11 @@ namespace mbts.spark {
|
||||
Dictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>> rv =
|
||||
new Dictionary<double, Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>>();
|
||||
foreach (SecurityStrike k in strikes) {
|
||||
var (call_id, put_id) = k.call_put_id;
|
||||
// call
|
||||
uint? call_id = k.call_put_id.Item1;
|
||||
var call_sec_md = get_sec_md(call_id);
|
||||
VolSurfaceMDUnit call_bbo = call_sec_md == null ? VolSurfaceMDUnit.empty : call_sec_md.Item2;
|
||||
// put
|
||||
uint? put_id = k.call_put_id.Item2;
|
||||
var put_sec_md = get_sec_md(put_id);
|
||||
VolSurfaceMDUnit put_bbo = put_sec_md == null ? VolSurfaceMDUnit.empty : put_sec_md.Item2;
|
||||
rv.Add(k.strike, new Tuple<VolSurfaceMDUnit, VolSurfaceMDUnit>(call_bbo, put_bbo));
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace mbts.spark {
|
||||
/// from the same secondary thread.
|
||||
/// </summary>
|
||||
public class ThreadedBlackboard {
|
||||
private readonly string _bb_name;
|
||||
private IBus _bus;
|
||||
private List<IDisposable> _subs = new List<IDisposable>();
|
||||
private HashSet<object> _tag_map = new HashSet<object>();
|
||||
@@ -31,9 +32,10 @@ namespace mbts.spark {
|
||||
private IDictionary<object, object> _cache = new
|
||||
Dictionary<object, object>();
|
||||
private static int _tid() { return VolSurfaceTroubleshootingFunctions.tid(); }
|
||||
public ThreadedBlackboard(IBus bus) {
|
||||
bts.Log.Ui.info($"ThreadedBlackBoard.cons tid:{ _tid()}");
|
||||
public ThreadedBlackboard(string name, IBus bus) {
|
||||
bts.Log.Ui.info($"ThreadedBlackBoard.cons tid:{_tid()} bb_name: {name}");
|
||||
_bus = bus;
|
||||
_bb_name = name;
|
||||
subscribe(SparkTags.SparkTimer);
|
||||
System.Threading.Thread timer_thread = new System.Threading.Thread(() => {
|
||||
DateTime start_time = DateTime.Now;
|
||||
@@ -61,15 +63,13 @@ namespace mbts.spark {
|
||||
return;
|
||||
}
|
||||
_tag_map.Add(tag);
|
||||
bts.Log.Ui.info($"ThreadedBlackBoard.subscribe tag:{tag} tid:{ _tid()}");
|
||||
bts.Log.Ui.info($"ThreadedBlackBoard.subscribe tag:{tag} tid:{_tid()}");
|
||||
var observable = _bus.subscribe(tag).OnDispatcher().Subscribe(
|
||||
data => _on_bus_data(tag, data));
|
||||
_subs.Add(observable);
|
||||
}
|
||||
|
||||
public void post<T>(ITag<T> tag, T data, bool block_until_written = false) {
|
||||
bts.Log.Ui.diag($"ThreadedBlackBoard.post tag:{tag} data:{_data_string(data)} " +
|
||||
$"tid:{_tid()}");
|
||||
_cache[tag] = data;
|
||||
_bus.post(tag, data);
|
||||
}
|
||||
@@ -92,8 +92,6 @@ namespace mbts.spark {
|
||||
timer_callback();
|
||||
return;
|
||||
}
|
||||
bts.Log.Ui.diag($"ThreadedBlackBoard._on_bus_data tag:{tag} data:{_data_string(data)} " +
|
||||
$"tid:{_tid()}");
|
||||
data_callback(tag, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
using bts.api;
|
||||
using bts.api.analytics;
|
||||
using bts.collections;
|
||||
using bts.utils;
|
||||
|
||||
using IronPython.Compiler.Ast;
|
||||
|
||||
using mbts;
|
||||
using mbts.analytics;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using Spark.Core;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using mbts;
|
||||
using mbts.analytics;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using mbts.spark;
|
||||
using bts.utils;
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace mbts.spark {
|
||||
/// This contains the list of global values for the vol
|
||||
@@ -132,7 +140,7 @@ namespace mbts.spark {
|
||||
|
||||
internal static string log_string(this Theos t) {
|
||||
string rv = $"strike: {t.strike} fwd: {t.fwd} driver: {t.driver} " +
|
||||
$"move: { t.move} vol: {t.vol} c_theo: {t.call} " +
|
||||
$"move: {t.move} vol: {t.vol} c_theo: {t.call} " +
|
||||
$"p_theo: {t.put}";
|
||||
return rv;
|
||||
}
|
||||
@@ -151,8 +159,8 @@ namespace mbts.spark {
|
||||
foreach (var kv in sc_data.strike_map) {
|
||||
var exp = kv.Key;
|
||||
foreach (var k in kv.Value) {
|
||||
rv += $"exp: {exp} k: {k} c_id: {k_id(k.call_put_id.Item1)} " +
|
||||
$"p_id: {k.call_put_id.Item2}" + "\n";
|
||||
rv += $"exp: {exp} k: {k} c_id: {k_id(k.call_put_id.call)} " +
|
||||
$"p_id: {k.call_put_id.put}" + "\n";
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
@@ -178,6 +186,58 @@ namespace mbts.spark {
|
||||
return rv;
|
||||
}
|
||||
|
||||
internal static string log_string(this ComboFullData cf_data) {
|
||||
string rv = String.Empty;
|
||||
if (cf_data.option_expirations != null) {
|
||||
foreach (var kv in cf_data.option_pos_map) {
|
||||
var exp = kv.Key;
|
||||
foreach (var kv_in in kv.Value) {
|
||||
double k = kv_in.Key;
|
||||
ComboStrikeData kp = kv_in.Value;
|
||||
rv += $"exp: {exp.ToString()} k: {k} Pos: {kp.call_pos},{kp.put_pos} ({kp.call_raw_pos},{kp.put_raw_pos}) ";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cf_data.future_pos_map != null) {
|
||||
foreach (var kv in cf_data.future_pos_map) {
|
||||
rv += $"exp: {kv.Key.ToString()} {kv.Value.Item1},{kv.Value.Item2} ";
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
internal static string log_string(this SlideFullData sl_data) {
|
||||
Func<SlideGreekUnitKey, string> sguk_str = (sguk) => sguk.display_name;
|
||||
Func<SlideGreekUnit, string> sgu_str = (sgu) => {
|
||||
if (sgu == null)
|
||||
return "No Handles";
|
||||
string rv = $"Handle: {sgu.driver_price} ";
|
||||
foreach (double d in sgu.position_greeks)
|
||||
rv += $"{d}, ";
|
||||
rv = rv.Trim();
|
||||
if (rv.EndsWith(","))
|
||||
rv = rv.TrimEnd(',');
|
||||
return rv;
|
||||
};
|
||||
string rv = String.Empty;
|
||||
foreach (var kv in sl_data.map) {
|
||||
rv += $"SlideGreekUnitKey: {sguk_str(kv.Key)}\n";
|
||||
foreach (var i_kv in kv.Value) {
|
||||
rv += sgu_str(i_kv.Value) + "\n";
|
||||
}
|
||||
}
|
||||
rv += $"Overriden?: {sl_data.ui_override != null}";
|
||||
return rv;
|
||||
}
|
||||
|
||||
internal static string log_string(this VolSurfaceUsageListImmutable vsu_list) {
|
||||
string rv = "VolSurfaceUsageList:";
|
||||
foreach (var u in vsu_list.UsageList) {
|
||||
rv += $" {u.SecTup.Symbol}:{u.VsId}";
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
internal static int tid() {
|
||||
return System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
||||
@@ -86,9 +86,14 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
private void _save_exp_params() {
|
||||
var exp_params = new AnalyticsExpirationParameters[]{
|
||||
new AnalyticsExpirationParameters(option_exp1, driver_exp, driver_exp, driver_exp, 0, 0, Double.NaN, 1, 1),
|
||||
new AnalyticsExpirationParameters(option_exp2, driver_exp, driver_exp, driver_exp, 0, 0, Double.NaN, 1, 1),
|
||||
var api_driver = new ExpirationDriverImmutable(new() {
|
||||
Security = driver_exp.to_api_sec_exp()
|
||||
});
|
||||
var exp_params = new AnalyticsExpirationParameters[] {
|
||||
new AnalyticsExpirationParameters(
|
||||
option_exp1, api_driver, driver_exp, driver_exp, 0, 0, double.NaN, 1, 1),
|
||||
new AnalyticsExpirationParameters(
|
||||
option_exp2, api_driver, driver_exp, driver_exp, 0, 0, double.NaN, 1, 1),
|
||||
};
|
||||
_bb.post(SparkTags.ExpirationParametersCommitTag, exp_params);
|
||||
}
|
||||
@@ -103,4 +108,4 @@ namespace mbts.spark {
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,11 +31,24 @@ namespace mbts.ui.controls {
|
||||
VolSurfaceExpiration exp = p.Key;
|
||||
VolCurveOutput exp_output = p.Value;
|
||||
IDictionary<VolSurfaceDisplayGridColumn, object> out_exp_map = VolSurfaceUIAdapterUtils.get_exp_data(ref out_grid, exp);
|
||||
out_exp_map[_fwd_col] = exp_output == null ? Double.NaN : exp_output.fwd;
|
||||
out_exp_map[_ref_fwd_col] = exp_output != null && exp_output.adjusted_curve_parameters != null && exp_output.adjusted_curve_parameters.core_parameters != null ?
|
||||
exp_output.adjusted_curve_parameters.core_parameters.ref_forward : Double.NaN;
|
||||
out_exp_map[_live_driver_col] = (exp_output != null && exp_output.aux_data != null) ? exp_output.aux_data.driver_price : Double.NaN;
|
||||
out_exp_map[_driver_override_col] = exp_output.adjusted_curve_parameters.core_parameters.driver_override;
|
||||
out_exp_map[_fwd_col] = exp_output?.fwd ?? double.NaN;
|
||||
|
||||
out_exp_map[_ref_fwd_col] = exp_output
|
||||
?.adjusted_curve_parameters
|
||||
?.core_parameters
|
||||
?.ref_forward
|
||||
?? double.NaN;
|
||||
|
||||
out_exp_map[_live_driver_col] = exp_output
|
||||
?.aux_data
|
||||
?.driver_price
|
||||
?? double.NaN;
|
||||
|
||||
out_exp_map[_driver_override_col] = exp_output
|
||||
?.adjusted_curve_parameters
|
||||
?.core_parameters
|
||||
?.driver_override
|
||||
?? double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,11 @@ namespace mbts.spark {
|
||||
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls.graph;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -110,7 +111,7 @@ namespace mbts.spark {
|
||||
//}
|
||||
|
||||
public object Clone() =>
|
||||
ProtoBufSerializationHelper.clone_protobuf(this);
|
||||
ProtobufSerializationHelper.clone(this);
|
||||
|
||||
public bool Equals(VolSurfaceGraphSettings other) =>
|
||||
other != null &&
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace mbts.spark.VolSurface {
|
||||
id = key ?? throw new InvalidOperationException("Column key cannot be null");
|
||||
}
|
||||
|
||||
public VolSurfaceDisplayGridColumn model { get; set; }
|
||||
public VolSurfaceDisplayGridColumn model { get; }
|
||||
public VolSurfaceColumnType type => model.type;
|
||||
|
||||
public new VolSurfaceColumnSettings create() {
|
||||
@@ -43,7 +43,7 @@ namespace mbts.spark.VolSurface {
|
||||
static Thickness _header_cell_padding => new(6, 0, 4, 0);
|
||||
|
||||
IValueConverter _data_context_converter =>
|
||||
ValueConverter.create<VolSurfaceRowContext, object>(row => row?.get_cell(model));
|
||||
ValueConverter.create<VolSurfaceRowVM, object>(row => row?.get_cell(model));
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
return type switch {
|
||||
@@ -58,7 +58,7 @@ namespace mbts.spark.VolSurface {
|
||||
HorizontalAlignment = HorizontalAlignment.Left,
|
||||
Foreground = BTSColors.header_text.to_brush(),
|
||||
};
|
||||
text.SetBinding(TextBlock.TextProperty, new Binding(nameof(VolSurfaceRowContext.expiration)));
|
||||
text.SetBinding(TextBlock.TextProperty, new Binding(nameof(VolSurfaceRowVM.expiration)));
|
||||
var bd = new Border {
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
Padding = _header_cell_padding,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace mbts.spark.VolSurface {
|
||||
namespace mbts.spark.VolSurface {
|
||||
using bts;
|
||||
using bts.collections;
|
||||
using bts.utils;
|
||||
@@ -63,7 +63,7 @@
|
||||
};
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
ObservableAutoWrapper.connect(_expirations, rows, exp => new VolSurfaceRowContext(this, exp), row => row.Dispose()),
|
||||
ObservableAutoWrapper.connect(_expirations, rows, exp => new VolSurfaceRowVM(this, exp), row => row.Dispose()),
|
||||
sorted_visible_rows.bind(visible_rows),
|
||||
Disposable.Create(() => {
|
||||
this.model.on_candidate_grid_change -= _on_grid_updated;
|
||||
@@ -102,14 +102,14 @@
|
||||
public IReadOnlyObservableCollection<DIYGrid.ColumnBase> columns =>
|
||||
_columns_model.views_with_header_ro;
|
||||
|
||||
public ObservableCollection<VolSurfaceRowContext> rows { get; } = new();
|
||||
public ObservableCollection<VolSurfaceRowContext> visible_rows { get; } = new();
|
||||
public ObservableSortedCollection<VolSurfaceRowContext> sorted_visible_rows { get; } = new(_row_comparer);
|
||||
public ObservableCollection<VolSurfaceRowVM> rows { get; } = new();
|
||||
public ObservableCollection<VolSurfaceRowVM> visible_rows { get; } = new();
|
||||
public ObservableSortedCollection<VolSurfaceRowVM> sorted_visible_rows { get; } = new(_row_comparer);
|
||||
|
||||
public bool any_masters_selected =>
|
||||
selected_rows.select_display_exp().where_master().Any();
|
||||
|
||||
public IEnumerable<VolSurfaceRowContext> selected_rows =>
|
||||
public IEnumerable<VolSurfaceRowVM> selected_rows =>
|
||||
visible_rows.where_selected();
|
||||
|
||||
public IEnumerable<DIYGridSuperHeaderVM> super_headers =>
|
||||
@@ -118,13 +118,13 @@
|
||||
.Select(group => new DIYGridSuperHeaderVM(group.Key, group.Count(), collapsed_content: _short_header(group.Key)))
|
||||
.ToList();
|
||||
|
||||
public ICommand main_settings_command => _parent.main_settings_command;
|
||||
public ICommand advanced_settings_command => _parent.advanced_settings_command;
|
||||
public ICommand graph_settings_command => _parent.vol_curve_vm.settings_command;
|
||||
public ICommand grid_settings_command => _parent.grid_settings_command;
|
||||
public ICommand main_settings_cmd => _parent.main_settings_cmd;
|
||||
public ICommand advanced_settings_cmd => _parent.advanced_settings_cmd;
|
||||
public ICommand graph_settings_cmd => _parent.vol_curve_vm.settings_command;
|
||||
public ICommand grid_settings_cmd => _parent.grid_settings_cmd;
|
||||
|
||||
static IComparer<VolSurfaceRowContext> _row_comparer =>
|
||||
Comparer<VolSurfaceDisplayGridExpiration>.Default.wrap<VolSurfaceDisplayGridExpiration, VolSurfaceRowContext>(r => r.expiration);
|
||||
static IComparer<VolSurfaceRowVM> _row_comparer =>
|
||||
Comparer<VolSurfaceDisplayGridExpiration>.Default.wrap<VolSurfaceDisplayGridExpiration, VolSurfaceRowVM>(r => r.expiration);
|
||||
|
||||
public void load_from(VolSurfaceMacroGridSettings grid_settings) {
|
||||
_hidden_columns = new HashSet<string>(grid_settings.hidden_cols());
|
||||
@@ -193,23 +193,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
internal void copy_to_selection(VolSurfaceDisplayGridExpiration exp, VolSurfaceDisplayGridColumn column, GridColumnSet set) {
|
||||
if (column.type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_replicate(exp, selected_rows.select_display_exp().Except(exp), _get_columns(column, set));
|
||||
}
|
||||
}
|
||||
internal void copy_to_selection(
|
||||
VolSurfaceDisplayGridExpiration exp,
|
||||
VolSurfaceDisplayGridColumn column,
|
||||
GridColumnSet set) =>
|
||||
_replicate(exp, selected_rows.select_display_exp().Except(exp), _get_columns(column, set));
|
||||
|
||||
internal void copy_to_masters(VolSurfaceDisplayGridExpiration exp, VolSurfaceDisplayGridColumn column, GridColumnSet set) {
|
||||
if (column.type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_replicate(exp, _expirations.where_master().Except(exp), _get_columns(column, set));
|
||||
}
|
||||
}
|
||||
internal void copy_to_masters(
|
||||
VolSurfaceDisplayGridExpiration exp,
|
||||
VolSurfaceDisplayGridColumn column,
|
||||
GridColumnSet set) =>
|
||||
_replicate(exp, _expirations.where_master().Except(exp), _get_columns(column, set));
|
||||
|
||||
internal void copy_to_all(VolSurfaceDisplayGridExpiration exp, VolSurfaceDisplayGridColumn column, GridColumnSet set) {
|
||||
if (column.type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_replicate(exp, _expirations.Except(exp), _get_columns(column, set));
|
||||
}
|
||||
}
|
||||
internal void copy_to_all(
|
||||
VolSurfaceDisplayGridExpiration exp,
|
||||
VolSurfaceDisplayGridColumn column,
|
||||
GridColumnSet set) =>
|
||||
_replicate(exp, _expirations.Except(exp), _get_columns(column, set));
|
||||
|
||||
static string _short_header(string header) =>
|
||||
string.Join("", header.Where(Char.IsUpper));
|
||||
@@ -299,7 +299,7 @@
|
||||
VolSurfaceDisplayGridExpiration source,
|
||||
IEnumerable<VolSurfaceDisplayGridExpiration> target_exps,
|
||||
IEnumerable<VolSurfaceDisplayGridColumn> cols) {
|
||||
foreach (var col in cols) {
|
||||
foreach (var col in cols.Where(c => c.type.is_editable())) {
|
||||
foreach (var exp in target_exps) {
|
||||
_copy(source, exp, col);
|
||||
}
|
||||
@@ -307,7 +307,14 @@
|
||||
_commit();
|
||||
}
|
||||
|
||||
void _copy(VolSurfaceDisplayGridExpiration source, VolSurfaceDisplayGridExpiration dest, VolSurfaceDisplayGridColumn col) {
|
||||
void _copy(
|
||||
VolSurfaceDisplayGridExpiration source,
|
||||
VolSurfaceDisplayGridExpiration dest,
|
||||
VolSurfaceDisplayGridColumn col
|
||||
) {
|
||||
if (!col.type.is_editable()) {
|
||||
throw new ArgumentException("Expected col to be editable", nameof(col));
|
||||
}
|
||||
if (current_grid.read(source, col, out var value)) {
|
||||
current_grid.set_grid_data(dest, col.unique_col_name, value);
|
||||
}
|
||||
|
||||
@@ -1,335 +0,0 @@
|
||||
namespace mbts.spark.VolSurface {
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
public struct VolSurfaceCellState {
|
||||
public bool is_dirty => committed_text != null;
|
||||
public FormattedText text;
|
||||
public FormattedText committed_text;
|
||||
}
|
||||
|
||||
class VolSurfaceRowContext : DisposableViewModelBase, ISelectable, IFormatCellsToClipboard, DIYGrid.IRowVM {
|
||||
readonly VolSurfaceGridVM _parent;
|
||||
readonly VolSurfaceDisplayGridExpiration _exp;
|
||||
/// TODO: remove _text_cache when we are sure that the new row view
|
||||
/// (<see cref="VolSurfaceRowView"/>) is solid. It must remain in place
|
||||
/// until then, as it is used by old row view
|
||||
/// (<see cref="VolSurfaceRowControl"/>).
|
||||
readonly Dictionary<string, CellData> _cells = new();
|
||||
|
||||
MenuContext _context_menu;
|
||||
bool _is_selected;
|
||||
|
||||
public VolSurfaceRowContext(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp) {
|
||||
_parent = parent;
|
||||
_exp = exp;
|
||||
_dispose = Disposable.Create(() => {
|
||||
_cells.Values.DisposeAll();
|
||||
_cells.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
public event Action<bool> is_selected_changed;
|
||||
public event Action invalidated;
|
||||
|
||||
public VolSurfaceDisplayGridExpiration expiration => _exp;
|
||||
|
||||
public bool is_selected {
|
||||
get => _is_selected;
|
||||
set {
|
||||
if (_is_selected != value) {
|
||||
_is_selected = value;
|
||||
is_selected_changed?.Invoke(value);
|
||||
_parent.signal_selection_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MenuContext context_menu =>
|
||||
_context_menu ??= new(_parent, this);
|
||||
|
||||
public VolGridCellVM get_cell(VolSurfaceDisplayGridColumn column) {
|
||||
if (!_cells.TryGetValue(column.unique_col_name, out var cell_data)) {
|
||||
_cells.Add(column.unique_col_name, cell_data = _create_cell(column));
|
||||
}
|
||||
return cell_data.vm;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
foreach (var cell in _cells.Values) {
|
||||
_sync_cell(cell.vm);
|
||||
}
|
||||
raise_invalidated();
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
_exp.ToString();
|
||||
|
||||
public string[] get_cell_clipboard_strings() =>
|
||||
new[] { _exp.ToString() }.Concat(
|
||||
_parent.columns.OfType<VolSurfaceColumn>()
|
||||
.Skip(1)
|
||||
.Select(c => {
|
||||
var val = _parent.current_grid.read(_exp, c.model.unique_col_name);
|
||||
return c.model.format(val);
|
||||
}))
|
||||
.ToArray();
|
||||
|
||||
public void raise_invalidated() =>
|
||||
invalidated?.Invoke();
|
||||
|
||||
CellData _create_cell(VolSurfaceDisplayGridColumn column) {
|
||||
var cell = column.type switch {
|
||||
VolSurfaceColumnType.ReadWriteTick => create_tick_cell(),
|
||||
_ => new VolGridCellVM(column.unique_col_name),
|
||||
};
|
||||
_sync_cell(cell);
|
||||
var dispose = cell.observe_candidate().DistinctUntilChanged().OnDispatcher().Subscribe(value => {
|
||||
var curr = _parent.current_grid.read(expiration, column);
|
||||
if (Equals(value, curr)) {
|
||||
return;
|
||||
}
|
||||
var grid = _parent.current_grid;
|
||||
grid.write(expiration, column, value);
|
||||
_parent.model.set_candidate_grid(grid);
|
||||
});
|
||||
return new(cell, dispose);
|
||||
|
||||
VolGridTickCellVM create_tick_cell() {
|
||||
var cell = new VolGridTickCellVM(column.unique_col_name);
|
||||
cell.request_tick += is_up => {
|
||||
var col = _parent.get_column(column.unique_col_name);
|
||||
if (col is null) {
|
||||
throw new InvalidOperationException($"Failed to find column with id {column.unique_col_name}");
|
||||
}
|
||||
if (col.model?.tick_move is var tm) {
|
||||
cell.candidate_value = tm(cell.candidate_value, is_up);
|
||||
return;
|
||||
}
|
||||
throw new InvalidOperationException($"tick_move is not defined");
|
||||
};
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
void _sync_cell(VolGridCellVM cell) {
|
||||
if (cell is null) {
|
||||
return;
|
||||
}
|
||||
cell.committed_value = _parent.committed_grid.read(_exp, cell.column_id);
|
||||
cell.candidate_value = _parent.current_grid.read(_exp, cell.column_id);
|
||||
}
|
||||
|
||||
public class MenuContext : ViewModelBase {
|
||||
readonly VolSurfaceRowContext _row;
|
||||
readonly VolSurfaceGridVM _parent;
|
||||
|
||||
VolSurfaceColumn _col;
|
||||
DIYGridSuperHeaderVM _supercol;
|
||||
|
||||
ICommand _revert_command;
|
||||
ICommand _shift_selection_command;
|
||||
ICommand _shift_masters_command;
|
||||
ICommand _shift_all_command;
|
||||
ICommand _clear_command;
|
||||
ICommand _copy_to_selection_command;
|
||||
ICommand _copy_to_masters_command;
|
||||
ICommand _copy_to_all_command;
|
||||
ICommand _copy_all_to_clipboard_command;
|
||||
ICommand _smart_copy_rolls_rates_command;
|
||||
ICommand _import_csv_command;
|
||||
|
||||
public MenuContext(
|
||||
VolSurfaceGridVM parent,
|
||||
VolSurfaceRowContext row) {
|
||||
_parent = parent;
|
||||
_row = row;
|
||||
}
|
||||
|
||||
public string column_name => _col.model.display_name;
|
||||
public string group_name => _supercol.id;
|
||||
public string expiration_name => _row.ToString();
|
||||
public VolSurfaceColumn column {
|
||||
get => _col;
|
||||
set => Set(ref _col, value);
|
||||
}
|
||||
public DIYGridSuperHeaderVM super_header {
|
||||
get => _supercol;
|
||||
set => Set(ref _supercol, value);
|
||||
}
|
||||
public VolSurfaceDisplayGridExpiration expiration => _row.expiration;
|
||||
|
||||
public class SymbolFilter {
|
||||
readonly MenuContext _context;
|
||||
bool? _visible;
|
||||
|
||||
public SymbolFilter(MenuContext context, string symbol, int count, int visible_count) {
|
||||
_context = context;
|
||||
this.symbol = symbol;
|
||||
if (count == visible_count) {
|
||||
_visible = true;
|
||||
} else if (visible_count == 0) {
|
||||
_visible = false;
|
||||
} else {
|
||||
_visible = null;
|
||||
}
|
||||
}
|
||||
|
||||
public string symbol { get; private set; }
|
||||
public bool? visible {
|
||||
get => _visible;
|
||||
set {
|
||||
_visible = value;
|
||||
if (value == true) {
|
||||
_context._show_symbol(symbol);
|
||||
} else if (value == false) {
|
||||
_context._hide_symbol(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<SymbolFilter> symbols {
|
||||
get {
|
||||
var visible_exps = _parent.visible_rows
|
||||
.Cast<VolSurfaceRowContext>()
|
||||
.GroupBy(r => r._exp.symbol)
|
||||
.ToDictionary(g => g.Key);
|
||||
var result = _parent.rows
|
||||
.GroupBy(r => r._exp.symbol)
|
||||
.OrderBy(g => g.Key)
|
||||
.Select(g => new SymbolFilter(this, g.Key, g.Count(), visible_exps.ContainsKey(g.Key) ? visible_exps[g.Key].Count() : 0));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_input_cell => _col.type == VolSurfaceColumnType.ReadWriteTick;
|
||||
|
||||
public ICommand main_settings_command => _parent.main_settings_command;
|
||||
public ICommand advanced_settings_command => _parent.advanced_settings_command;
|
||||
public ICommand grid_settings_command => _parent.grid_settings_command;
|
||||
public ICommand graph_settings_command => _parent.graph_settings_command;
|
||||
|
||||
public ICommand revert_command =>
|
||||
_revert_command ??= new DelegateCommand(_ => _row.get_cell(_col.model)?.revert());
|
||||
|
||||
public ICommand shift_selection_command =>
|
||||
_shift_selection_command ??= new DelegateCommand(() => {
|
||||
if (_col.model.type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_parent.shift_selection(_col.model);
|
||||
}
|
||||
}, () => _col.model.type == VolSurfaceColumnType.ReadWriteTick);
|
||||
|
||||
public ICommand shift_masters_command =>
|
||||
_shift_masters_command ??= new DelegateCommand(() => {
|
||||
if (_col.model.type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_parent.shift_masters(_col.model);
|
||||
}
|
||||
}, () => _col.model.type == VolSurfaceColumnType.ReadWriteTick);
|
||||
|
||||
public ICommand shift_all_command =>
|
||||
_shift_all_command ??= new DelegateCommand(() => {
|
||||
if (_col.model.type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_parent.shift_all(_col.model);
|
||||
}
|
||||
}, () => _col.model.type == VolSurfaceColumnType.ReadWriteTick);
|
||||
|
||||
public ICommand clear_command =>
|
||||
_clear_command ??= new DelegateCommand<string>(where => {
|
||||
if (Enum.TryParse(where, out GridColumnSet gc)) {
|
||||
_parent.clear(_col.model, gc);
|
||||
}
|
||||
},
|
||||
_has_editable_cells);
|
||||
|
||||
public ICommand copy_to_selection_command =>
|
||||
_copy_to_selection_command ??= new DelegateCommand<string>(where => {
|
||||
if (Enum.TryParse(where, out GridColumnSet gc)) {
|
||||
_parent.copy_to_selection(expiration, _col.model, gc);
|
||||
}
|
||||
},
|
||||
_has_editable_cells);
|
||||
|
||||
public ICommand copy_to_masters_command =>
|
||||
_copy_to_masters_command ??= new DelegateCommand<string>(where => {
|
||||
if (Enum.TryParse(where, out GridColumnSet gc)) {
|
||||
_parent.copy_to_masters(expiration, _col.model, gc);
|
||||
}
|
||||
},
|
||||
_has_editable_cells);
|
||||
|
||||
public ICommand copy_to_all_command =>
|
||||
_copy_to_all_command ??= new DelegateCommand<string>(where => {
|
||||
if (Enum.TryParse(where, out GridColumnSet gc)) {
|
||||
_parent.copy_to_all(expiration, _col.model, gc);
|
||||
}
|
||||
},
|
||||
_has_editable_cells);
|
||||
|
||||
public ICommand copy_all_to_clipboard_command =>
|
||||
_copy_all_to_clipboard_command ??= new DelegateCommand(() => {
|
||||
string clipboard_text = ClipboardHelper.GetExcelFormattedText(
|
||||
_parent.visible_rows
|
||||
.OfType<VolSurfaceRowContext>()
|
||||
.Select(r => r.get_cell_clipboard_strings())
|
||||
.Where(cells => cells != null));
|
||||
if (!clipboard_text.is_null_or_empty()) {
|
||||
ClipboardHelper.CopyTextToClipboard(clipboard_text);
|
||||
}
|
||||
});
|
||||
|
||||
public ICommand smart_copy_rolls_rates_command =>
|
||||
_smart_copy_rolls_rates_command ??= new DelegateCommand(_ => _parent.model.sync_slave_ref_forwards(expiration.ToVSExp()));
|
||||
|
||||
public ICommand import_csv_command =>
|
||||
_import_csv_command ??= FileDialogCommand.open_file(
|
||||
_parent.model.import_csv_file,
|
||||
() => _parent.model.csv_importing_enabled(),
|
||||
null);
|
||||
|
||||
bool _has_editable_cells(string where) =>
|
||||
!Enum.TryParse(where, out GridColumnSet gc) ||
|
||||
gc == GridColumnSet.Column || _col.model.type != VolSurfaceColumnType.ReadOnly;
|
||||
|
||||
void _hide_symbol(string symbol) {
|
||||
_parent.visible_rows.RemoveAll(s => ((VolSurfaceRowContext)s).expiration.symbol == symbol);
|
||||
_parent.signal_selection_changed();
|
||||
}
|
||||
|
||||
void _show_symbol(string symbol) {
|
||||
// Insert items from rows to visible_Rows that have symbol=true in sorted order
|
||||
// do this by making the desired collection and then leveraging sync_with
|
||||
var visible = new HashSet<VolSurfaceDisplayGridExpiration>(_parent.visible_rows.Cast<VolSurfaceRowContext>().Select(row => row.expiration));
|
||||
var desired = _parent.rows.Where(row => row.expiration.symbol == symbol || visible.Contains(row.expiration));
|
||||
_parent.visible_rows.sync_with(desired);
|
||||
}
|
||||
}
|
||||
|
||||
class CellData : IDisposable {
|
||||
IDisposable _dispose;
|
||||
VolGridCellVM _vm;
|
||||
|
||||
public CellData(VolGridCellVM vm, IDisposable dispose) {
|
||||
_vm = vm;
|
||||
_dispose = dispose;
|
||||
}
|
||||
|
||||
public VolGridCellVM vm => _vm;
|
||||
|
||||
public void Dispose() {
|
||||
Disposable.dispose(ref _vm);
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts.collections;
|
||||
|
||||
using mbts.spark.VolSurface;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
class VolSurfaceRowContextMenuVM : DisposableViewModelBase {
|
||||
readonly VolSurfaceRowVM _row;
|
||||
readonly VolSurfaceGridVM _parent;
|
||||
readonly Property<VolSurfaceColumn?> _column;
|
||||
readonly Property<DIYGridSuperHeaderVM?> _super_header;
|
||||
|
||||
ICommand? _revert_cmd;
|
||||
ICommand? _shift_selection_cmd;
|
||||
ICommand? _shift_masters_cmd;
|
||||
ICommand? _shift_all_cmd;
|
||||
ICommand? _clear_cmd;
|
||||
ICommand? _copy_to_selection_cmd;
|
||||
ICommand? _copy_to_masters_cmd;
|
||||
ICommand? _copy_to_all_cmd;
|
||||
ICommand? _copy_all_to_clipboard_cmd;
|
||||
ICommand? _smart_copy_rolls_rates_cmd;
|
||||
ICommand? _import_csv_cmd;
|
||||
|
||||
public VolSurfaceRowContextMenuVM(VolSurfaceGridVM parent, VolSurfaceRowVM row) {
|
||||
_parent = parent;
|
||||
_row = row;
|
||||
_column = backing<VolSurfaceColumn?>(nameof(column));
|
||||
_super_header = backing<DIYGridSuperHeaderVM?>(nameof(super_header));
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
_column.invalidate_others_on_change(nameof(column_name)),
|
||||
_super_header.invalidate_others_on_change(nameof(group_name)),
|
||||
_parent.visible_rows.ObserveSelf()
|
||||
.BufferLatest(TimeSpan.FromSeconds(1))
|
||||
.OnDispatcher()
|
||||
.Subscribe(_ => sync_symbols()));
|
||||
|
||||
void sync_symbols() {
|
||||
var visible_exps = _parent.visible_rows.Map(r => r.expiration.symbol);
|
||||
symbols.sync_with_models(
|
||||
visible_exps,
|
||||
kv => new SymbolItemVM(this, kv.Key),
|
||||
(svm, kv) => {
|
||||
svm.symbol = kv.Key;
|
||||
svm.visible = SelectAllToggle.is_all_selected(
|
||||
visible_exps[svm.symbol].Count(),
|
||||
kv.Value.Count());
|
||||
return svm;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public string column_name => column?.model?.display_name ?? string.Empty;
|
||||
public string group_name => super_header?.id ?? string.Empty;
|
||||
public string expiration_name => _row.ToString();
|
||||
public VolSurfaceColumn? column {
|
||||
get => _column.Value;
|
||||
set => _column.set(value);
|
||||
}
|
||||
public DIYGridSuperHeaderVM? super_header {
|
||||
get => _super_header.Value;
|
||||
set => _super_header.set(value);
|
||||
}
|
||||
public VolSurfaceDisplayGridExpiration expiration => _row.expiration;
|
||||
|
||||
public ObservableCollection<SymbolItemVM> symbols { get; } = new();
|
||||
|
||||
public bool is_input_cell => column?.type == VolSurfaceColumnType.ReadWriteTick;
|
||||
|
||||
public ICommand main_settings_cmd => _parent.main_settings_cmd;
|
||||
public ICommand advanced_settings_cmd => _parent.advanced_settings_cmd;
|
||||
public ICommand grid_settings_cmd => _parent.grid_settings_cmd;
|
||||
public ICommand graph_settings_cmd => _parent.graph_settings_cmd;
|
||||
|
||||
public ICommand revert_cmd =>
|
||||
_revert_cmd ??= new Command(_ => {
|
||||
_row.get_cell(_assert_model())?.revert();
|
||||
});
|
||||
|
||||
public ICommand shift_selection_cmd =>
|
||||
_shift_selection_cmd ??= new Command(() => {
|
||||
if (_assert_model().type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_parent.shift_selection(_assert_model());
|
||||
}
|
||||
}, () => _assert_model().type == VolSurfaceColumnType.ReadWriteTick);
|
||||
|
||||
public ICommand shift_masters_cmd =>
|
||||
_shift_masters_cmd ??= new Command(() => {
|
||||
if (_assert_model().type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_parent.shift_masters(_assert_model());
|
||||
}
|
||||
}, () => _assert_model().type == VolSurfaceColumnType.ReadWriteTick);
|
||||
|
||||
public ICommand shift_all_cmd =>
|
||||
_shift_all_cmd ??= new Command(() => {
|
||||
if (_assert_model().type == VolSurfaceColumnType.ReadWriteTick) {
|
||||
_parent.shift_all(_assert_model());
|
||||
}
|
||||
}, () => _assert_model().type == VolSurfaceColumnType.ReadWriteTick);
|
||||
|
||||
public ICommand clear_cmd =>
|
||||
_clear_cmd ??= new Command<string>(where => {
|
||||
if (Enum.TryParse(where, out GridColumnSet gc)) {
|
||||
_parent.clear(_assert_model(), gc);
|
||||
}
|
||||
},
|
||||
_has_editable_cells);
|
||||
|
||||
public ICommand copy_to_selection_cmd =>
|
||||
_copy_to_selection_cmd ??= new Command<string>(where => {
|
||||
if (Enum.TryParse(where, out GridColumnSet gc)) {
|
||||
_parent.copy_to_selection(expiration, _assert_model(), gc);
|
||||
}
|
||||
});
|
||||
|
||||
public ICommand copy_to_masters_cmd =>
|
||||
_copy_to_masters_cmd ??= new Command<string>(where => {
|
||||
if (Enum.TryParse(where, out GridColumnSet gc)) {
|
||||
_parent.copy_to_masters(expiration, _assert_model(), gc);
|
||||
}
|
||||
});
|
||||
|
||||
public ICommand copy_to_all_cmd =>
|
||||
_copy_to_all_cmd ??= new Command<string>(where => {
|
||||
if (Enum.TryParse(where, out GridColumnSet gc)) {
|
||||
_parent.copy_to_all(expiration, _assert_model(), gc);
|
||||
}
|
||||
});
|
||||
|
||||
public ICommand copy_all_to_clipboard_cmd =>
|
||||
_copy_all_to_clipboard_cmd ??= new Command(() => {
|
||||
string clipboard_text = ClipboardHelper.GetExcelFormattedText(
|
||||
_parent.visible_rows
|
||||
.OfType<VolSurfaceRowVM>()
|
||||
.Select(r => r.get_cell_clipboard_strings())
|
||||
.Where(cells => cells != null));
|
||||
if (!clipboard_text.is_null_or_empty()) {
|
||||
ClipboardHelper.CopyTextToClipboard(clipboard_text);
|
||||
}
|
||||
});
|
||||
|
||||
public ICommand smart_copy_rolls_rates_cmd =>
|
||||
_smart_copy_rolls_rates_cmd ??= new Command(
|
||||
() => _parent.model.sync_slave_ref_forwards(expiration.ToVSExp()));
|
||||
|
||||
public ICommand import_csv_cmd =>
|
||||
_import_csv_cmd ??= FileDialogCommand.open_file(
|
||||
_parent.model.import_csv_file,
|
||||
_parent.model.csv_importing_enabled,
|
||||
null);
|
||||
|
||||
VolSurfaceDisplayGridColumn _assert_model() =>
|
||||
column?.model ?? throw new InvalidOperationException();
|
||||
|
||||
bool _has_editable_cells(string where) =>
|
||||
!Enum.TryParse(where, out GridColumnSet gc) ||
|
||||
gc == GridColumnSet.Column || _assert_model().type != VolSurfaceColumnType.ReadOnly;
|
||||
|
||||
void _hide_symbol(string symbol) {
|
||||
_parent.visible_rows.RemoveAll(s => s.expiration.symbol == symbol);
|
||||
_parent.signal_selection_changed();
|
||||
}
|
||||
|
||||
void _show_symbol(string symbol) {
|
||||
// Insert items from rows to visible_Rows that have symbol=true in sorted order
|
||||
// do this by making the desired collection and then leveraging sync_with
|
||||
var visible = _parent.visible_rows
|
||||
.Select(row => row.expiration)
|
||||
.ToHashSet();
|
||||
var desired = _parent.rows.Where(
|
||||
row => row.expiration.symbol == symbol || visible.Contains(row.expiration));
|
||||
_parent.visible_rows.sync_with(desired);
|
||||
}
|
||||
|
||||
public class SymbolItemVM : ViewModelBase {
|
||||
readonly VolSurfaceRowContextMenuVM _context;
|
||||
string _symbol;
|
||||
bool? _visible;
|
||||
|
||||
public SymbolItemVM(
|
||||
VolSurfaceRowContextMenuVM context, string symbol) {
|
||||
_context = context;
|
||||
_symbol = symbol;
|
||||
}
|
||||
|
||||
public string symbol {
|
||||
get => _symbol;
|
||||
set => Set(ref _symbol, value);
|
||||
}
|
||||
|
||||
public bool? visible {
|
||||
get => _visible;
|
||||
set {
|
||||
if (Set(ref _visible, value)) {
|
||||
if (value is true) {
|
||||
_context._show_symbol(symbol);
|
||||
} else if (value is false) {
|
||||
_context._hide_symbol(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,372 +0,0 @@
|
||||
// using mbts;
|
||||
// using mbts.ui;
|
||||
// using mbts.ui.controls;
|
||||
// using mbts.ui.dpi;
|
||||
// using mbts.ui.utility.text;
|
||||
|
||||
// using System;
|
||||
// using System.Collections.Generic;
|
||||
// using System.Diagnostics;
|
||||
// using System.Globalization;
|
||||
// using System.Linq;
|
||||
// using System.Text;
|
||||
// using System.Threading.Tasks;
|
||||
// using System.Windows;
|
||||
// using System.Windows.Controls;
|
||||
// using System.Windows.Input;
|
||||
// using System.Windows.Media;
|
||||
|
||||
// namespace mbts.spark.VolSurface {
|
||||
// class VolSurfaceRowControl : DIYGrid.HighPerformanceRow<VolSurfaceRowContext> {
|
||||
// FormattedText _exp_text;
|
||||
// readonly double _padding = 5.0;
|
||||
// readonly Pen _dirty_pen = new Pen(Brushes.Red, 1.0);
|
||||
// readonly Pen _selection_pen = new Pen(Brushes.Black, 1.0) { DashStyle = DashStyles.Dot };
|
||||
|
||||
// TickUpDown _cell_editor_backing;
|
||||
// TickUpDown _cell_editor => _cell_editor_backing ??= new ();
|
||||
|
||||
// int? _selected_cell;
|
||||
|
||||
// public VolSurfaceRowControl() {
|
||||
// Loaded += (s, e) => ContextMenu = TryFindResource("context_menu") as ContextMenu;
|
||||
// GotKeyboardFocus += (s, e) => {
|
||||
// if (e.OriginalSource is not FrameworkElement el) {
|
||||
// return;
|
||||
// }
|
||||
// var cell_tl = el.TranslatePoint(new Point(0, 0), this);
|
||||
// _on_focus_move(DIYGrid.RowUtils.column_at(_column_layout, cell_tl));
|
||||
// };
|
||||
// }
|
||||
|
||||
// int? _column_index(DIYGrid.ColumnGeometry col) =>
|
||||
// _column_layout.TryGetFirst(c => c.Equals(col), out var _, out var idx) ? idx : null;
|
||||
|
||||
// //private static int _render_count = 0;
|
||||
// protected override void OnRender(DrawingContext dc) {
|
||||
// // Fill with transparent for hit testing
|
||||
// dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, new Rect(0, 0, _column_layout.Sum(c => c.width), RenderSize.Height));
|
||||
|
||||
// string last_group = "";
|
||||
// foreach (var cell in _column_layout) {
|
||||
// // Render collapsed columns
|
||||
// if (cell.column.is_collapsed) {
|
||||
// dc.DrawRectangle(BTSColors.AW2.collapsed, null, new Rect(cell.left, 0, cell.width, Height));
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// var column = (VolSurfaceSimplifiedColumn)cell.column;
|
||||
// VolSurfaceCellState celldata;
|
||||
|
||||
// // Get the cell text
|
||||
// if (column.type == VolSurfaceSimplifiedColumnType.RowHeader) {
|
||||
// // Fill in this one
|
||||
// dc.DrawRectangle(BTSColors.header, null, new Rect(cell.left, 0, cell.width, Height));
|
||||
// var exp = _row.expiration;
|
||||
// if (_exp_text == null) {
|
||||
// _exp_text = format_text(exp.ToString(), exp.is_slave ? Brushes.LightGray : Brushes.White);
|
||||
// }
|
||||
// celldata = new VolSurfaceCellState() {
|
||||
// text = _exp_text
|
||||
// };
|
||||
// } else if (column.type == VolSurfaceSimplifiedColumnType.ReadOnly) {
|
||||
// celldata = _row.get_value(column.volsurface_column, format(Brushes.Blue), _ => null);
|
||||
// } else {
|
||||
// celldata = _row.get_value(column.volsurface_column, format(Brushes.Black), format(Brushes.DarkGray));
|
||||
// }
|
||||
|
||||
// // If the cell is partly hidden clip the draw
|
||||
// if (cell.is_partially_obscured) {
|
||||
// dc.PushClip(new RectangleGeometry(
|
||||
// new Rect(cell.left, 0, cell.width, Height)));
|
||||
// }
|
||||
|
||||
// // draw text centered vertially
|
||||
// double total_width = _padding + celldata.text.Width;
|
||||
// double pad_top = (Height - celldata.text.Height) / 2.0;
|
||||
// dc.DrawText(celldata.text, new Point(cell.unclipped_left + _padding, pad_top));
|
||||
// if (celldata.committed_text != null) {
|
||||
// pad_top = (Height - celldata.committed_text.Height) / 2.0;
|
||||
// dc.DrawText(celldata.committed_text, new Point(cell.unclipped_left + total_width, pad_top));
|
||||
// total_width += celldata.committed_text.Width;
|
||||
// }
|
||||
|
||||
// // draw grid lines
|
||||
// double line_width; Brush fill;
|
||||
// if (last_group == cell.group) { line_width = 1; fill = _grid.VerticalGridLineBrush; } else { line_width = 2; fill = _grid.VerticalGroupSeparatorBrush; }
|
||||
// dc.DrawRectangle(fill, null, new Rect(cell.left - line_width, 0, line_width, Height));
|
||||
// Rect cell_border_rect = new Rect(cell.unclipped_left + 1, 0, Math.Max(0, cell.width - (line_width * 2)), Height - 1);
|
||||
// // draw dirty rect
|
||||
// if (celldata.is_dirty) {
|
||||
// dc.DrawRectangle(null, _dirty_pen, cell_border_rect);
|
||||
// }
|
||||
|
||||
// // Draw selection
|
||||
// if (_selected_cell == _column_index(cell) && column.type != VolSurfaceSimplifiedColumnType.ReadWriteTick) {
|
||||
// dc.DrawRectangle(null, _selection_pen, cell_border_rect);
|
||||
// }
|
||||
|
||||
// if (cell.is_partially_obscured) { dc.Pop(); }
|
||||
|
||||
// total_width += _padding;//right padding
|
||||
// if (!celldata.is_dirty && column.type == VolSurfaceSimplifiedColumnType.ReadWriteTick) total_width += 22; // size of buttons on a tickupdown
|
||||
// if (total_width > cell.width) {
|
||||
// Dispatcher.BeginInvoke(((Action)(() => cell.column.Width = total_width)));
|
||||
// }
|
||||
// last_group = cell.group;
|
||||
// }
|
||||
|
||||
// if (_selected_cell != NO_CELL) {
|
||||
// if (_cell_editor.Visibility == Visibility.Visible) {
|
||||
// var position = _column_geometry[_selected_cell];
|
||||
// PositionControlOver(tud, position);
|
||||
// }
|
||||
// }
|
||||
|
||||
// //++_render_count;
|
||||
// //dc.DrawText(
|
||||
// // format_text(_render_count.ToString(), Brushes.Black),
|
||||
// // new Point(0, 0));
|
||||
// }
|
||||
|
||||
// Func<string, FormattedText> format(Brush brush) =>
|
||||
// str => format_text(str, brush);
|
||||
|
||||
// void _on_focus_move(DIYGrid.ColumnGeometry? new_column) {
|
||||
// if (new_column != null) {
|
||||
// enable_edit(new_column.Value);
|
||||
// }
|
||||
// InvalidateVisual();
|
||||
// }
|
||||
|
||||
// private async void enable_edit(DIYGrid.ColumnGeometry column) {
|
||||
// await Task.Yield();
|
||||
// var col = (VolSurfaceSimplifiedColumn)column.column;
|
||||
// var tud = _grid.get_edit_control<TickUpDown>();
|
||||
// if (col.type == VolSurfaceSimplifiedColumnType.ReadWriteTick) {
|
||||
// tud.DataContext = _row.get_edit_context(col.volsurface_column);
|
||||
// PositionControlOver(tud, column);
|
||||
// var fe = tud.Content as TextBox;
|
||||
// if (fe != null) {
|
||||
// await Task.Yield();
|
||||
// fe.Focus();
|
||||
// fe.SelectAll();
|
||||
// }
|
||||
// } else {
|
||||
// tud.DataContext = null;
|
||||
// tud.Visibility = Visibility.Collapsed;
|
||||
// var item = this.find_ancestor_of_type<ListBoxItem>();
|
||||
// if (item != null) {
|
||||
// item.Focus();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// protected override void OnMouseRightButtonUp(MouseButtonEventArgs e) {
|
||||
// // _grid.column
|
||||
// var cell = DIYGrid.RowUtils.column_at(_column_layout, e.GetPosition(this));
|
||||
// if (ContextMenu != null) {
|
||||
// if (cell.HasValue && DIYGrid.RowUtils.column_index(_column_layout, cell.Value) > (_grid.FrozenColumnCount - 1)) {
|
||||
// ContextMenu.DataContext = _row.get_context_menu_context((VolSurfaceSimplifiedColumn)cell.Value.column, cell.Value.super_column);
|
||||
// } else {
|
||||
// e.Handled = true;
|
||||
// ContextMenu.DataContext = null;
|
||||
// }
|
||||
// }
|
||||
// base.OnMouseRightButtonDown(e);
|
||||
// }
|
||||
|
||||
// FormattedText format_text(string content, Brush fg) {
|
||||
// var ft = new FormattedText(content, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
|
||||
// TextUtils.StandardTypeface, FontSizes.Regular, Brushes.Black, HighDpiExtensions.dpi_ratio);
|
||||
// return ft;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// using mbts;
|
||||
// using mbts.ui;
|
||||
// using mbts.ui.controls;
|
||||
// using mbts.ui.dpi;
|
||||
// using mbts.ui.utility.text;
|
||||
|
||||
// using System;
|
||||
// using System.Collections.Generic;
|
||||
// using System.Diagnostics;
|
||||
// using System.Globalization;
|
||||
// using System.Linq;
|
||||
// using System.Text;
|
||||
// using System.Threading.Tasks;
|
||||
// using System.Windows;
|
||||
// using System.Windows.Controls;
|
||||
// using System.Windows.Input;
|
||||
// using System.Windows.Media;
|
||||
|
||||
// namespace mbts.spark.VolSurface {
|
||||
// class VolSurfaceRowControl : DIYGrid.RowBase {
|
||||
// VolSurfaceRowContext _row;
|
||||
// FormattedText _exp_text;
|
||||
// readonly double _padding = 5.0;
|
||||
// readonly Pen _dirty_pen = new Pen(Brushes.Red, 1.0);
|
||||
// readonly Pen _selection_pen = new Pen(Brushes.Black, 1.0) { DashStyle = DashStyles.Dot };
|
||||
|
||||
// public VolSurfaceRowControl() {
|
||||
// DataContextChanged += OnDataContextChanged;
|
||||
// Loaded += OnLoaded;
|
||||
// }
|
||||
|
||||
// #region SelectedCell
|
||||
// public static readonly DependencyProperty SelectedCellProperty =
|
||||
// WPFHelper.create_dp<object, VolSurfaceRowControl>(nameof(SelectedCell));
|
||||
|
||||
// public object SelectedCell {
|
||||
// get => GetValue(SelectedCellProperty);
|
||||
// set => SetValue(SelectedCellProperty, value);
|
||||
// }
|
||||
// #endregion
|
||||
|
||||
// void OnLoaded(object sender, RoutedEventArgs e) {
|
||||
// ContextMenu = TryFindResource("context_menu") as ContextMenu;
|
||||
// }
|
||||
|
||||
// void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) {
|
||||
// var oldrow = e.OldValue as VolSurfaceRowContext;
|
||||
// if (oldrow != null) {
|
||||
// oldrow.on_invalidate -= InvalidateVisual;
|
||||
// }
|
||||
// _row = DataContext as VolSurfaceRowContext;
|
||||
// _row.on_invalidate += InvalidateVisual;
|
||||
// }
|
||||
|
||||
// //private static int _render_count = 0;
|
||||
// protected override void OnRender(DrawingContext dc) {
|
||||
// if (_grid == null) return;
|
||||
// // Fill with transparent for hit testing
|
||||
// dc.DrawRectangle(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)), null, new Rect(0, 0, _column_geometry.Sum(g => g.width), RenderSize.Height));
|
||||
|
||||
// string last_group = "";
|
||||
// foreach (var cell in _column_geometry) {
|
||||
// // Render collapsed columns
|
||||
// if (cell.column.is_collapsed) {
|
||||
// dc.DrawRectangle(BTSColors.AW2.collapsed, null, new Rect(cell.left, 0, cell.width, Height));
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// var column = (VolSurfaceSimplifiedColumn)cell.column;
|
||||
// VolSurfaceCellState celldata;
|
||||
|
||||
// // Get the cell text
|
||||
// if (column.type == VolSurfaceSimplifiedColumnType.RowHeader) {
|
||||
// // Fill in this one
|
||||
// dc.DrawRectangle(BTSColors.header, null, new Rect(cell.left, 0, cell.width, Height));
|
||||
// var exp = _row.expiration;
|
||||
// if (_exp_text == null) {
|
||||
// _exp_text = format_text(exp.ToString(), exp.is_slave ? Brushes.LightGray : Brushes.White);
|
||||
// }
|
||||
// celldata = new VolSurfaceCellState() { text = _exp_text };
|
||||
// } else if (column.type == VolSurfaceSimplifiedColumnType.ReadOnly) {
|
||||
// celldata = _row.get_value(column.volsurface_column, format(Brushes.Blue), _ => null);
|
||||
// } else {
|
||||
// celldata = _row.get_value(column.volsurface_column, format(Brushes.Black), format(Brushes.DarkGray));
|
||||
// }
|
||||
|
||||
// // If the cell is partly hidden clip the draw
|
||||
// if (cell.is_partially_obscured) {
|
||||
// dc.PushClip(new RectangleGeometry(
|
||||
// new Rect(cell.left, 0, cell.width, Height)));
|
||||
// }
|
||||
|
||||
// // draw text centered vertially
|
||||
// double total_width = _padding + celldata.text.Width;
|
||||
// double pad_top = (Height - celldata.text.Height) / 2.0;
|
||||
// dc.DrawText(celldata.text, new Point(cell.unclipped_left + _padding, pad_top));
|
||||
// if (celldata.committed_text != null) {
|
||||
// pad_top = (Height - celldata.committed_text.Height) / 2.0;
|
||||
// dc.DrawText(celldata.committed_text, new Point(cell.unclipped_left + total_width, pad_top));
|
||||
// total_width += celldata.committed_text.Width;
|
||||
// }
|
||||
|
||||
// // draw grid lines
|
||||
// double line_width; Brush fill;
|
||||
// if (last_group == cell.group) { line_width = 1; fill = _grid.VerticalGridLineBrush; } else { line_width = 2; fill = _grid.VerticalGroupSeparatorBrush; }
|
||||
// dc.DrawRectangle(fill, null, new Rect(cell.left - line_width, 0, line_width, Height));
|
||||
// Rect cell_border_rect = new Rect(cell.unclipped_left + 1, 0, Math.Max(0, cell.width - (line_width * 2)), Height - 1);
|
||||
// // draw dirty rect
|
||||
// if (celldata.is_dirty) {
|
||||
// dc.DrawRectangle(null, _dirty_pen, cell_border_rect);
|
||||
// }
|
||||
|
||||
// // Draw selection
|
||||
// if (_selected_cell == column_index(cell) && column.type != VolSurfaceSimplifiedColumnType.ReadWriteTick) {
|
||||
// dc.DrawRectangle(null, _selection_pen, cell_border_rect);
|
||||
// }
|
||||
|
||||
// if (cell.is_partially_obscured) { dc.Pop(); }
|
||||
|
||||
// total_width += _padding;//right padding
|
||||
// if (!celldata.is_dirty && column.type == VolSurfaceSimplifiedColumnType.ReadWriteTick) total_width += 22; // size of buttons on a tickupdown
|
||||
// if (total_width > cell.width) {
|
||||
// Dispatcher.BeginInvoke(((Action)(() => cell.column.Width = total_width)));
|
||||
// }
|
||||
// last_group = cell.group;
|
||||
// }
|
||||
|
||||
// //++_render_count;
|
||||
// //dc.DrawText(
|
||||
// // format_text(_render_count.ToString(), Brushes.Black),
|
||||
// // new Point(0, 0));
|
||||
// }
|
||||
// private Func<string, FormattedText> format(Brush brush) {
|
||||
// return str => format_text(str, brush);
|
||||
// }
|
||||
// protected override void OnClick(DIYGrid.ColumnGeometry column) {
|
||||
// }
|
||||
|
||||
// protected override void OnFocusMove(DIYGrid.ColumnGeometry? new_column) {
|
||||
// if (new_column != null) enable_edit(new_column.Value);
|
||||
// InvalidateVisual();
|
||||
// }
|
||||
|
||||
// private async void enable_edit(DIYGrid.ColumnGeometry column) {
|
||||
// await Task.Yield();
|
||||
// var col = (VolSurfaceSimplifiedColumn)column.column;
|
||||
// var tud = _grid.get_edit_control<TickUpDown>();
|
||||
// if (col.type == VolSurfaceSimplifiedColumnType.ReadWriteTick) {
|
||||
// tud.DataContext = _row.get_edit_context(col.volsurface_column);
|
||||
// PositionControlOver(tud, column);
|
||||
// var fe = tud.Content as TextBox;
|
||||
// if (fe != null) {
|
||||
// await Task.Yield();
|
||||
// fe.Focus();
|
||||
// fe.SelectAll();
|
||||
// }
|
||||
// } else {
|
||||
// tud.DataContext = null;
|
||||
// tud.Visibility = Visibility.Collapsed;
|
||||
// var item = this.find_ancestor_of_type<ListBoxItem>();
|
||||
// if (item != null) {
|
||||
// item.Focus();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// protected override void OnMouseRightButtonUp(MouseButtonEventArgs e) {
|
||||
// var cell = column_at(e.GetPosition(this));
|
||||
// if (ContextMenu != null) {
|
||||
// if (cell.HasValue && column_index(cell.Value) > (_grid.FrozenColumnCount - 1)) {
|
||||
// ContextMenu.DataContext = _row.get_context_menu_context((VolSurfaceSimplifiedColumn)cell.Value.column, cell.Value.super_column);
|
||||
// } else {
|
||||
// e.Handled = true;
|
||||
// ContextMenu.DataContext = null;
|
||||
// }
|
||||
// }
|
||||
// base.OnMouseRightButtonDown(e);
|
||||
// }
|
||||
|
||||
// FormattedText format_text(string content, Brush fg) {
|
||||
// var ft = new FormattedText(content, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
|
||||
// TextUtils.StandardTypeface, FontSizes.Regular, Brushes.Black, HighDpiExtensions.dpi_ratio);
|
||||
// return ft;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,137 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark.VolSurface {
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
public struct VolSurfaceCellState {
|
||||
public bool is_dirty => committed_text is not null;
|
||||
public FormattedText? text;
|
||||
public FormattedText? committed_text;
|
||||
}
|
||||
|
||||
class VolSurfaceRowVM : DisposableViewModelBase, ISelectable, IFormatCellsToClipboard, DIYGrid.IRowVM {
|
||||
readonly VolSurfaceGridVM _parent;
|
||||
readonly VolSurfaceDisplayGridExpiration _exp;
|
||||
readonly Dictionary<string, CellData> _cells = new();
|
||||
|
||||
VolSurfaceRowContextMenuVM? _context_menu;
|
||||
bool _is_selected;
|
||||
|
||||
public VolSurfaceRowVM(VolSurfaceGridVM parent, VolSurfaceDisplayGridExpiration exp) {
|
||||
_parent = parent;
|
||||
_exp = exp;
|
||||
_dispose = Disposable.Create(() => {
|
||||
_cells.Values.DisposeAll();
|
||||
_cells.Clear();
|
||||
_context_menu?.Dispose();
|
||||
});
|
||||
}
|
||||
|
||||
public event Action<bool>? is_selected_changed;
|
||||
public event Action? invalidated;
|
||||
|
||||
public VolSurfaceDisplayGridExpiration expiration => _exp;
|
||||
|
||||
public bool is_selected {
|
||||
get => _is_selected;
|
||||
set {
|
||||
if (Set(ref _is_selected, value)) {
|
||||
is_selected_changed?.Invoke(value);
|
||||
_parent.signal_selection_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public VolSurfaceRowContextMenuVM context_menu =>
|
||||
_context_menu ??= new(_parent, this);
|
||||
|
||||
public VolGridCellVM get_cell(VolSurfaceDisplayGridColumn column) =>
|
||||
_cells.GetOrAdd(column.unique_col_name, _ => _create_cell(column)).vm;
|
||||
|
||||
public void invalidate() {
|
||||
foreach (var cell in _cells.Values) {
|
||||
_sync_cell(cell.vm);
|
||||
}
|
||||
invalidated?.Invoke();
|
||||
}
|
||||
|
||||
public string[] get_cell_clipboard_strings() =>
|
||||
new[] { _exp.ToString() }.Concat(
|
||||
_parent.columns.OfType<VolSurfaceColumn>().Skip(1).Select(c => {
|
||||
var val = _parent.current_grid.read(_exp, c.model.unique_col_name);
|
||||
return c.model.format(val);
|
||||
}))
|
||||
.ToArray();
|
||||
|
||||
public override string ToString() =>
|
||||
_exp.ToString();
|
||||
|
||||
void DIYGrid.IRowVM.raise_invalidated() =>
|
||||
invalidated?.Invoke();
|
||||
|
||||
CellData _create_cell(VolSurfaceDisplayGridColumn column) {
|
||||
var cell = column.type switch {
|
||||
VolSurfaceColumnType.ReadWriteTick => create_tick_cell(),
|
||||
_ => new VolGridCellVM(column.unique_col_name),
|
||||
};
|
||||
_sync_cell(cell);
|
||||
var dispose = cell.observe_candidate().DistinctUntilChanged().OnDispatcher()
|
||||
.Subscribe(value => {
|
||||
var curr = _parent.current_grid.read(expiration, column);
|
||||
if (Equals(value, curr)) {
|
||||
return;
|
||||
}
|
||||
var grid = _parent.current_grid;
|
||||
grid.write(expiration, column, value);
|
||||
_parent.model.set_candidate_grid(grid);
|
||||
});
|
||||
return new(cell, dispose);
|
||||
|
||||
VolGridTickCellVM create_tick_cell() {
|
||||
var cell = new VolGridTickCellVM(column.unique_col_name);
|
||||
cell.request_tick += is_up => {
|
||||
var col = _parent.get_column(column.unique_col_name);
|
||||
if (col is null) {
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to find column with id {column.unique_col_name}");
|
||||
}
|
||||
if (col.model?.tick_move is var tm) {
|
||||
cell.candidate_value = tm?.Invoke(cell.candidate_value, is_up);
|
||||
return;
|
||||
}
|
||||
throw new InvalidOperationException($"tick_move is not defined");
|
||||
};
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
void _sync_cell(VolGridCellVM cell) {
|
||||
if (cell is null) {
|
||||
return;
|
||||
}
|
||||
cell.committed_value = _parent.committed_grid.read(_exp, cell.column_id);
|
||||
cell.candidate_value = _parent.current_grid.read(_exp, cell.column_id);
|
||||
}
|
||||
|
||||
class CellData : IDisposable {
|
||||
IDisposable _dispose;
|
||||
|
||||
public CellData(VolGridCellVM vm, IDisposable dispose) {
|
||||
this.vm = vm;
|
||||
_dispose = Disposable.Create(vm, dispose);
|
||||
}
|
||||
|
||||
public VolGridCellVM vm { get; }
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -3,6 +3,8 @@ using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -21,7 +23,7 @@ namespace mbts.spark {
|
||||
if (_settings.precomputed_theo_handles == null)
|
||||
_settings.precomputed_theo_handles = new List<double>();
|
||||
vol_surface_name = vs_name;
|
||||
this.commit = new DelegateCommand(() => request_commit?.Invoke(ProtoBufSerializationHelper.clone_protobuf(_settings)));
|
||||
this.commit = new DelegateCommand(() => request_commit?.Invoke(ProtobufSerializationHelper.clone(_settings)));
|
||||
}
|
||||
|
||||
public event Action<VolSurfaceAdvancedSettings> request_commit;
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace mbts.spark {
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -23,6 +25,9 @@ namespace mbts.spark {
|
||||
readonly VolSurfaceSettings _settings;
|
||||
readonly VolSurfaceCoreSettings _core_settings;
|
||||
|
||||
string _selected_section;
|
||||
bool _can_edit_name = true;
|
||||
|
||||
public VolSurfaceSettingsEditorVM(IBus bus, VolSurfaceSettings settings, Action<VolSurfaceSettings> commit) {
|
||||
if (settings is null) {
|
||||
throw new ArgumentNullException(nameof(settings));
|
||||
@@ -113,7 +118,23 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public string vol_surface_name { get { return _core_settings.vol_surface_name; } }
|
||||
public string vol_surface_name {
|
||||
get => _core_settings.vol_surface_name;
|
||||
set {
|
||||
using var _ = detect_property_change();
|
||||
_core_settings.vol_surface_name = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool can_edit_name {
|
||||
get => _can_edit_name;
|
||||
set => Set(ref _can_edit_name, value);
|
||||
}
|
||||
|
||||
public string selected_section {
|
||||
get => _selected_section;
|
||||
set => Set(ref _selected_section, value);
|
||||
}
|
||||
|
||||
public VSSymbolStructureVM symbol_structure_vm { get; set; }
|
||||
public VolModelPolynomialVM vol_model_polynomial_vm { get; } = new();
|
||||
@@ -353,7 +374,7 @@ namespace mbts.spark {
|
||||
public ICommand commit { get; }
|
||||
|
||||
public VolSurfaceSettings get_settings() {
|
||||
var rv = ProtoBufSerializationHelper.clone_protobuf(_settings);
|
||||
var rv = ProtobufSerializationHelper.clone(_settings);
|
||||
var symbols = symbol_structure_vm.driver_grids
|
||||
.SelectMany(dg => dg.symbols.Where(sym => sym.is_selected))
|
||||
.ToList();
|
||||
@@ -522,11 +543,16 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public class VSSymbolStructureVM : DisposableViewModelBase {
|
||||
readonly Property<VSSymbolGridVM> _selected_driver_grid;
|
||||
|
||||
public VSSymbolStructureVM(IBus bus, VolSurfaceSettings settings) {
|
||||
driver_grids = new ObservableCollection<VSSymbolGridVM>();
|
||||
_selected_driver_grid = backing<VSSymbolGridVM>(nameof(selected_driver_grid));
|
||||
settings.core_settings.master_slave_relationships ??= new List<VolSurfaceSymbolRelationship>();
|
||||
settings.core_settings.implied_vol_marketdata_relationships ??= new List<VolSurfaceSymbolRelationship>();
|
||||
_dispose = new DisposableCombo(
|
||||
driver_grids.Subscribe((s, e) =>
|
||||
selected_driver_grid ??= driver_grids.FirstOrDefault()),
|
||||
bus.subscribe(SparkTags.ExpirationParametersInputTag).OnDispatcher().Subscribe(on_exp_params_input)) +
|
||||
new Action(() => Disposable.dispose(driver_grids));
|
||||
|
||||
@@ -586,6 +612,10 @@ namespace mbts.spark {
|
||||
|
||||
public IReadOnlyList<VolSurfaceEnums.VolSurfacePricingModel> pricing_models => EnumHelper.get_values<VolSurfaceEnums.VolSurfacePricingModel>();
|
||||
public ObservableCollection<VSSymbolGridVM> driver_grids { get; private set; }
|
||||
public VSSymbolGridVM selected_driver_grid {
|
||||
get => _selected_driver_grid.Value;
|
||||
set => _selected_driver_grid.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
public class RotationVM : ViewModelBase, IEquatable<RotationVM> {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using Spark.Core.Reflection;
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
class VolSurfaceMacroActionsVM : ViewModelBase {
|
||||
readonly VolSurfaceDisplayModel _model;
|
||||
@@ -76,7 +77,7 @@
|
||||
public IRaiseCanExecuteChangedCommand vol_shift { get; }
|
||||
|
||||
IEnumerable<IRaiseCanExecuteChangedCommand> _commands =>
|
||||
ObjectSnapshot.property_delegates_of_type<VolSurfaceMacroActionsVM, IRaiseCanExecuteChangedCommand>()
|
||||
ReflectionHelper.property_delegates_of_type<VolSurfaceMacroActionsVM, IRaiseCanExecuteChangedCommand>()
|
||||
.Select(g => g(this));
|
||||
|
||||
public void selection_changed(IEnumerable<VolSurfaceDisplayGridExpiration> selected) {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
|
||||
@@ -8,48 +8,105 @@ namespace mbts.spark {
|
||||
using mbts.spark.VolSurface;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
class VolSurfaceVM : ViewModelBase, IDisposable {
|
||||
class VolSurfaceVM : DisposableViewModelBase {
|
||||
readonly int _vs_id;
|
||||
readonly IBus _bus;
|
||||
readonly string _request_id = Guid.NewGuid().ToString();
|
||||
VolSurfaceDisplayModel _model;
|
||||
VolSurfaceMacroGridSettings _grid_settings;
|
||||
readonly VolSurfaceDisplayModel _model;
|
||||
readonly IRaiseCanExecuteChangedCommand _revert_cmd;
|
||||
readonly IRaiseCanExecuteChangedCommand _commit_cmd;
|
||||
readonly IDialogCommand _main_settings_cmd;
|
||||
readonly IDialogCommand _advanced_settings_cmd;
|
||||
readonly IDialogCommand _grid_settings_cmd;
|
||||
|
||||
public VolSurfaceVM(VolSurfaceDisplayModel model, int id, IBus bus, VolSurfaceWindowSettings startup_data) {
|
||||
Log.notnull(startup_data);
|
||||
string? _selected_state = string.Empty;
|
||||
VolSurfaceMacroGridSettings _grid_settings;
|
||||
bool _ignore_implied_vol_settings;
|
||||
|
||||
public VolSurfaceVM(
|
||||
VolSurfaceDisplayModel model, int id, IBus bus, VolSurfaceWindowSettings startup_data) {
|
||||
_model = model;
|
||||
var demux = new VolSurfaceEventDemux(model);
|
||||
_vs_id = id;
|
||||
_bus = bus;
|
||||
macro_actions = new VolSurfaceMacroActionsVM(_model);
|
||||
globals = new VolSurfaceGlobalParameterVM(_model);
|
||||
|
||||
// grid
|
||||
_grid_settings = startup_data.macro_grid_settings ?? new VolSurfaceMacroGridSettings();
|
||||
var hidden_exps_set = startup_data.hidden_exps.empty_if_null().ToHashSet();
|
||||
grid = new VolSurfaceGridVM(
|
||||
this, _model, _grid_settings.hidden_cols(), hidden_exps_set);
|
||||
grid.on_selection_changed += _on_grid_selection_changed;
|
||||
_model.publish_grid_settings(_grid_settings);
|
||||
|
||||
// graphs
|
||||
var demux = new VolSurfaceEventDemux(model);
|
||||
vol_curve_vm = new VolSurfaceGraphVM(
|
||||
_model, demux, startup_data, (show_ivol, exp) => _model.publish_new_grid_selection(_request_id, exp, show_ivol));
|
||||
_model, demux, startup_data, (show_ivol, exp) =>
|
||||
_model.publish_new_grid_selection(_request_id, exp, show_ivol));
|
||||
vol_terms_vm = new VolTermsGraphVM(_model, demux);
|
||||
revert_command = new DelegateCommand(o => _model.revert(), o => _model.is_dirty);
|
||||
commit_command = new DelegateCommand(o => _model.commit(), o => _model.is_dirty);
|
||||
load_previous_command = new DelegateCommand(_ => { var cur = selected_previous_state; selected_previous_state = null; _model.load_previous_state(cur); }, _ => !string.IsNullOrEmpty(selected_previous_state));
|
||||
main_settings_command = new DialogCommand(() => {
|
||||
|
||||
// command
|
||||
_revert_cmd = new Command(o => _model.revert(), o => _model.is_dirty);
|
||||
_commit_cmd = new Command(o => _model.commit(), o => _model.is_dirty);
|
||||
load_previous_cmd = new Command(
|
||||
load_previous, () => !selected_previous_state.is_null_or_empty());
|
||||
_main_settings_cmd = new DialogCommand(mk_main_settings_win);
|
||||
_advanced_settings_cmd = new DialogCommand(mk_advanced_settings_win);
|
||||
_grid_settings_cmd = new DialogCommand(mk_grid_settings_win);
|
||||
|
||||
_model.on_dirty_change += () => {
|
||||
RaisePropertyChanged(nameof(is_dirty));
|
||||
_revert_cmd.RaiseCanExecuteChanged();
|
||||
_commit_cmd.RaiseCanExecuteChanged();
|
||||
};
|
||||
demux.on_candidate_grid_change += () =>
|
||||
previous_states.sync_with(_model.get_previous_states());
|
||||
demux.on_schema_change += () => {
|
||||
var existing_cols = _grid_settings.column_settings.ToDictionary(c => c.id);
|
||||
_grid_settings.set_columns(
|
||||
model.get_active_grid()
|
||||
.get_ordered_columns()
|
||||
.Select(col => existing_cols.GetValueOrDefault(col.unique_col_name) ??
|
||||
VolSurfaceColumnSettings.from_column_model(col)));
|
||||
};
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
vol_curve_vm,
|
||||
vol_terms_vm,
|
||||
globals,
|
||||
Disposable.Create(() => grid?.Dispose()),
|
||||
_main_settings_cmd,
|
||||
_advanced_settings_cmd,
|
||||
_grid_settings_cmd,
|
||||
_model);
|
||||
Window? mk_main_settings_win() {
|
||||
var settings = _model.get_active_settings();
|
||||
if (settings == null) {
|
||||
if (settings is null) {
|
||||
return null;
|
||||
}
|
||||
return new VolSurfaceSettingsEditor { DataContext = new VolSurfaceSettingsEditorVM(_bus, settings, _model.set_candidate_settings) };
|
||||
});
|
||||
advanced_settings_command = new DialogCommand(() => {
|
||||
return new VolSurfaceSettingsEditor {
|
||||
DataContext = new VolSurfaceSettingsEditorVM(
|
||||
_bus, settings, _model.set_candidate_settings) {
|
||||
can_edit_name = false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Window? mk_advanced_settings_win() {
|
||||
var settings = _model.get_active_settings();
|
||||
if (settings == null) {
|
||||
return null;
|
||||
@@ -69,76 +126,71 @@ namespace mbts.spark {
|
||||
// )
|
||||
// };
|
||||
return null;
|
||||
});
|
||||
grid_settings_command = new DialogCommand(() => {
|
||||
}
|
||||
|
||||
Window? mk_grid_settings_win() {
|
||||
var settings = _model.get_active_settings();
|
||||
if (settings == null) {
|
||||
if (settings is null) {
|
||||
return null;
|
||||
}
|
||||
var vm = new VolSurfaceGridSettingsEditorVM(settings.core_settings.vol_surface_name, _grid_settings.Clone());
|
||||
var vm = new VolSurfaceGridSettingsEditorVM(
|
||||
settings.core_settings.vol_surface_name, _grid_settings.Clone());
|
||||
vm.request_commit += on_grid_settings;
|
||||
return new VolSurfaceGridSettingsEditor {
|
||||
DataContext = vm
|
||||
};
|
||||
});
|
||||
_model.on_dirty_change += raise_dirty_changed;
|
||||
macro_actions = new VolSurfaceMacroActionsVM(_model);
|
||||
globals = new VolSurfaceGlobalParameterVM(_model);
|
||||
demux.on_candidate_grid_change += () => previous_states.sync_with(_model.get_previous_states());
|
||||
demux.on_schema_change += () => {
|
||||
// _grid_settings.clear_columns();
|
||||
var existing_cols = _grid_settings.column_settings.ToDictionary(c => c.id);
|
||||
_grid_settings.set_columns(
|
||||
model.get_active_grid()
|
||||
.get_ordered_columns()
|
||||
.Select(col => {
|
||||
if (!existing_cols.TryGetValue(col.unique_col_name, out var rv)) {
|
||||
rv = VolSurfaceColumnSettings.from_column_model(col);
|
||||
}
|
||||
return rv;
|
||||
}));
|
||||
};
|
||||
grid = new VolSurfaceGridVM(this, _model, _grid_settings.hidden_cols(), new HashSet<VolSurfaceExpiration>(startup_data.hidden_exps ?? Enumerable.Empty<VolSurfaceExpiration>()));
|
||||
grid.on_selection_changed += _on_grid_selection_changed;
|
||||
|
||||
void on_grid_settings(VolSurfaceMacroGridSettings new_settings) {
|
||||
using var _ = grid.defer_invalidate_rows();
|
||||
_grid_settings = new_settings;
|
||||
grid.load_from(_grid_settings);
|
||||
grid.invalidate_rows();
|
||||
_model.publish_grid_settings(_grid_settings);
|
||||
void on_grid_settings(VolSurfaceMacroGridSettings new_settings) {
|
||||
using var _ = grid.defer_invalidate_rows();
|
||||
_grid_settings = new_settings;
|
||||
grid.load_from(_grid_settings);
|
||||
grid.invalidate_rows();
|
||||
_model.publish_grid_settings(_grid_settings);
|
||||
}
|
||||
}
|
||||
|
||||
void load_previous() {
|
||||
var cur = selected_previous_state;
|
||||
selected_previous_state = null;
|
||||
_model.load_previous_state(cur);
|
||||
}
|
||||
}
|
||||
|
||||
public VolSurfaceGridVM grid { get; private set; }
|
||||
public VolSurfaceGraphVM vol_curve_vm { get; private set; }
|
||||
public VolTermsGraphVM vol_terms_vm { get; private set; }
|
||||
public VolSurfaceMacroActionsVM macro_actions { get; private set; }
|
||||
public VolSurfaceGlobalParameterVM globals { get; private set; }
|
||||
public VolSurfaceGridVM grid { get; }
|
||||
public VolSurfaceGraphVM vol_curve_vm { get; }
|
||||
public VolTermsGraphVM vol_terms_vm { get; }
|
||||
public VolSurfaceMacroActionsVM macro_actions { get; }
|
||||
public VolSurfaceGlobalParameterVM globals { get; }
|
||||
|
||||
private bool _ignore_implied_vol_settings;
|
||||
public bool ignore_implied_vol_settings { get { return _ignore_implied_vol_settings; } set { _model.publish_ignore_implied_vol_settings(_request_id, _ignore_implied_vol_settings = value); } }
|
||||
|
||||
public bool is_dirty { get { return _model.is_dirty; } }
|
||||
public IRaiseCanExecuteChangedCommand revert_command { get; private set; }
|
||||
public IRaiseCanExecuteChangedCommand commit_command { get; private set; }
|
||||
public IDialogCommand main_settings_command { get; private set; }
|
||||
public IDialogCommand advanced_settings_command { get; private set; }
|
||||
public IDialogCommand grid_settings_command { get; private set; }
|
||||
public ICommand details_command => new DelegateCommand(_ => _show_details());
|
||||
|
||||
public ICommand load_previous_command { get; private set; }
|
||||
public ObservableCollection<string> previous_states { get; } = new();
|
||||
private string _selected_state = string.Empty;
|
||||
public string selected_previous_state {
|
||||
get { return _selected_state; }
|
||||
public bool ignore_implied_vol_settings {
|
||||
get => _ignore_implied_vol_settings;
|
||||
set {
|
||||
_selected_state = value;
|
||||
load_previous_command.TryRaiseCanExecuteChanged();
|
||||
RaisePropertyChanged();
|
||||
if (Set(ref _ignore_implied_vol_settings, value)) {
|
||||
_model.publish_ignore_implied_vol_settings(_request_id, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<string> previous_states { get; } = new();
|
||||
public string? selected_previous_state {
|
||||
get => _selected_state;
|
||||
set {
|
||||
if (Set(ref _selected_state, value)) {
|
||||
load_previous_cmd.TryRaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_dirty => _model.is_dirty;
|
||||
|
||||
public ICommand revert_cmd => _revert_cmd;
|
||||
public ICommand commit_cmd => _commit_cmd;
|
||||
public ICommand main_settings_cmd => _main_settings_cmd;
|
||||
public ICommand advanced_settings_cmd => _advanced_settings_cmd;
|
||||
public ICommand grid_settings_cmd => _grid_settings_cmd;
|
||||
public ICommand details_command => new Command(_show_details);
|
||||
public ICommand load_previous_cmd { get; }
|
||||
|
||||
public VolSurfaceWindowSettings get_startup_data() {
|
||||
var rv = new VolSurfaceWindowSettings {
|
||||
id = _vs_id,
|
||||
@@ -152,39 +204,20 @@ namespace mbts.spark {
|
||||
return rv;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
vol_curve_vm.Dispose();
|
||||
vol_terms_vm.Dispose();
|
||||
globals.Dispose();
|
||||
if (grid != null) grid.Dispose();
|
||||
main_settings_command.Dispose();
|
||||
advanced_settings_command.Dispose();
|
||||
grid_settings_command.Dispose();
|
||||
Disposable.dispose(ref _model);
|
||||
}
|
||||
|
||||
void raise_dirty_changed() {
|
||||
RaisePropertyChanged("is_dirty");
|
||||
revert_command.RaiseCanExecuteChanged();
|
||||
commit_command.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
void _on_grid_selection_changed(IEnumerable<VolSurfaceDisplayGridExpiration> selection) {
|
||||
var selected = selection.FirstOrDefault();
|
||||
if (selected != null) {
|
||||
if (selection.TryGetFirst(out var selected)) {
|
||||
vol_curve_vm.set_visible_expiration(selected);
|
||||
_model.publish_new_grid_selection(_request_id, selected, vol_curve_vm.show_implied_vol);
|
||||
}
|
||||
macro_actions.selection_changed(selection);
|
||||
}
|
||||
|
||||
async Task<VolSurfaceSettings> _get_volsurface_settings() =>
|
||||
(await _bus.subscribe(SparkTags.VolSurfaceCommittedOutputTag(_vs_id)).FirstOrDefaultAsync())
|
||||
?.volsurface_input
|
||||
?.surface_settings;
|
||||
|
||||
void _show_details() =>
|
||||
_model.show_details();
|
||||
|
||||
async Task<VolSurfaceSettings?> _get_volsurface_settings() =>
|
||||
(await _bus.subscribe(SparkTags.VolSurfaceCommittedOutputTag(_vs_id)).FirstOrDefaultAsync())
|
||||
?.volsurface_input
|
||||
?.surface_settings;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
using bts.api;
|
||||
using bts.utils;
|
||||
namespace mbts.spark {
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
|
||||
using mbts.analytics;
|
||||
using mbts.collections;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using mbts.analytics;
|
||||
using mbts.collections;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using Spark.Core;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
class VolTermsGraphVM : DisposableViewModelBase {
|
||||
readonly VolSurfaceDisplayModel _model;
|
||||
readonly DateTimeAxisVM _exp_terms_x_axis;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
using mbts.analytics;
|
||||
using mbts.ui.controls;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -296,7 +298,7 @@
|
||||
bool succ = false;
|
||||
VolSurfaceInputSnapshot vs_sn = _input_snapshot_list.ToList().Find(v => v.save_marker == state_name);
|
||||
if (vs_sn != null) {
|
||||
VolSurfaceInput vs_inp = VolSurfaceUtilityFunctions.deserialize_proto<VolSurfaceInput>(vs_sn.data);
|
||||
var vs_inp = ProtobufSerializationHelper.deserialize<VolSurfaceInput>(vs_sn.data);
|
||||
if (vs_inp != null) {
|
||||
_change_state(DisplayModelState.WaitingForCalcResponse);
|
||||
_bus.post(SparkTags.VolSurfaceCalculationRequestTag(_vs_id),
|
||||
@@ -319,7 +321,7 @@
|
||||
public byte[] get_current_state() {
|
||||
DisplayModelData curr_data = _get_current_data();
|
||||
if (curr_data != null && curr_data.vs_output != null && curr_data.vs_output.volsurface_input != null)
|
||||
return VolSurfaceUtilityFunctions.serialize_proto(curr_data.vs_output.volsurface_input);
|
||||
return ProtobufSerializationHelper.serialize(curr_data.vs_output.volsurface_input);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
@@ -327,7 +329,7 @@
|
||||
/// Sets the state that is passed in bytes form. Returns
|
||||
/// true is state is set successfully
|
||||
public bool set_state(byte[] file_data) {
|
||||
VolSurfaceInput vs_input = VolSurfaceUtilityFunctions.deserialize_proto<VolSurfaceInput>(file_data);
|
||||
var vs_input = ProtobufSerializationHelper.deserialize<VolSurfaceInput>(file_data);
|
||||
if (vs_input != null) {
|
||||
if (_state == DisplayModelState.Idle || _state == DisplayModelState.ResponseReady) {
|
||||
_change_state(DisplayModelState.WaitingForCalcResponse);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace mbts.spark {
|
||||
namespace mbts.spark {
|
||||
using bts.api;
|
||||
using bts.api.analytics;
|
||||
using bts.utils;
|
||||
@@ -34,6 +34,9 @@
|
||||
public static HashSet<string> hidden_cols(this VolSurfaceMacroGridSettings gs) =>
|
||||
new(gs.column_settings.Where(cc => !cc.is_visible).Select(cc => cc.id));
|
||||
|
||||
public static bool is_editable(this VolSurfaceColumnType vs_col_type) =>
|
||||
vs_col_type is VolSurfaceColumnType.ReadWriteTick;
|
||||
|
||||
public static analytics.VolSurfaceExpiration ToVSExp(this VolSurfaceDisplayGridExpiration vsdge) =>
|
||||
new() { expiration = vsdge.expiration, symbol = vsdge.symbol };
|
||||
|
||||
@@ -80,10 +83,10 @@
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public static IEnumerable<VolSurfaceExpiration> select_exp(this IEnumerable<VolSurfaceRowContext> rows) =>
|
||||
public static IEnumerable<VolSurfaceExpiration> select_exp(this IEnumerable<VolSurfaceRowVM> rows) =>
|
||||
rows.Select(r => r.expiration.ToVSExp());
|
||||
|
||||
public static IEnumerable<VolSurfaceDisplayGridExpiration> select_display_exp(this IEnumerable<VolSurfaceRowContext> rows) =>
|
||||
public static IEnumerable<VolSurfaceDisplayGridExpiration> select_display_exp(this IEnumerable<VolSurfaceRowVM> rows) =>
|
||||
rows.Select(r => r.expiration);
|
||||
|
||||
public static IEnumerable<VolSurfaceDisplayGridExpiration> where_master(this IEnumerable<VolSurfaceDisplayGridExpiration> rows) =>
|
||||
@@ -113,6 +116,94 @@
|
||||
public static IEnumerable<(double x, double y)> real_all_points(this VolCurveDisplayGraph graph) =>
|
||||
_select_real_points(graph.all_points);
|
||||
|
||||
public static bool is_delta_greek(this VolSurfaceGreeks g) =>
|
||||
g switch {
|
||||
VolSurfaceGreeks.Delta or
|
||||
VolSurfaceGreeks.Bsmdelta or
|
||||
VolSurfaceGreeks.FlatVolDelta or
|
||||
VolSurfaceGreeks.ExpirationDelta or
|
||||
VolSurfaceGreeks.Charm or
|
||||
VolSurfaceGreeks.Vanna => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public static bool is_gamma_greek(this VolSurfaceGreeks g) =>
|
||||
g switch {
|
||||
VolSurfaceGreeks.Gamma or
|
||||
VolSurfaceGreeks.Bsmgamma => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
public static bool is_oev_col(this VolSurfaceGreeks g) =>
|
||||
g switch {
|
||||
VolSurfaceGreeks.Oev or
|
||||
VolSurfaceGreeks.Bsmoev => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
public static VolSurfaceExpiration to_vs_exp(this SecurityExpiration sec_exp) =>
|
||||
new() { security_type = sec_exp.security_type, symbol = sec_exp.symbol, expiration = sec_exp.expiration };
|
||||
|
||||
public static DriverCalculationType to_driver_calc_type(
|
||||
this ExpirationDriver.ModeOneofCase driver_mode) =>
|
||||
driver_mode switch {
|
||||
ExpirationDriver.ModeOneofCase.Security => DriverCalculationType.Direct,
|
||||
ExpirationDriver.ModeOneofCase.TimeWeightedInterpolated => DriverCalculationType.TimeWeightedInterp,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
public static SecurityExpiration to_sec_exp(
|
||||
this bts.api.domain.SecurityExpiration api_sec_exp) =>
|
||||
to_sec_exp(new bts.api.domain.SecurityExpirationImmutable(api_sec_exp));
|
||||
|
||||
public static SecurityExpiration to_sec_exp(
|
||||
this bts.api.domain.SecurityExpirationImmutable api_sec_exp) =>
|
||||
new(
|
||||
api_sec_exp.Exchange.to_venue(),
|
||||
api_sec_exp.SecurityType.contract_case(),
|
||||
api_sec_exp.Symbol,
|
||||
api_sec_exp.Expiration.date_time());
|
||||
|
||||
public static SecurityExpiration to_sec_exp(this Security sec) =>
|
||||
to_sec_exp(new SecurityImmutable(sec));
|
||||
|
||||
public static SecurityExpiration to_sec_exp(this SecurityImmutable sec) {
|
||||
var exp_date = sec.security_expiration() ?? throw new InvalidOperationException();
|
||||
return new(sec.Exchange, sec.ContractCase, sec.Symbol, exp_date, sec.Id);
|
||||
}
|
||||
|
||||
public static ExpirationDriver to_exp_driver(this SecurityExpiration sec_exp) =>
|
||||
new() {
|
||||
Security = new() {
|
||||
Symbol = sec_exp.symbol,
|
||||
Expiration = new() {
|
||||
ExpirationDay = (uint)sec_exp.expiration.Day,
|
||||
ExpirationMonth = (uint)sec_exp.expiration.Month,
|
||||
ExpirationYear = (uint)sec_exp.expiration.Year,
|
||||
},
|
||||
Exchange = sec_exp.exchange.to_api_venue(),
|
||||
}
|
||||
};
|
||||
|
||||
public static ExpirationDriverImmutable to_exp_driveri(this SecurityExpiration sec_exp) =>
|
||||
new(to_exp_driver(sec_exp));
|
||||
|
||||
public static bts.api.domain.SecurityExpiration to_api_sec_exp(this SecurityExpiration sec_exp) =>
|
||||
new() {
|
||||
Exchange = sec_exp.exchange.to_api_venue(),
|
||||
SecurityType = sec_exp.security_type,
|
||||
Symbol = sec_exp.symbol,
|
||||
Expiration = new() {
|
||||
ExpirationDay = (uint)sec_exp.expiration.Day,
|
||||
ExpirationMonth = (uint)sec_exp.expiration.Month,
|
||||
ExpirationYear = (uint)sec_exp.expiration.Year
|
||||
}
|
||||
};
|
||||
|
||||
public static bts.api.domain.SecurityExpirationImmutable to_api_sec_expi(
|
||||
this SecurityExpiration sec_exp) =>
|
||||
new(to_api_sec_exp(sec_exp));
|
||||
|
||||
static IEnumerable<(double x, double y)> _select_real_points(IEnumerable<Tuple<double, double>> points) =>
|
||||
points.Where(pt => pt.Item1.is_real() && pt.Item2.is_real()).Select(t => (t.Item1, t.Item2));
|
||||
}
|
||||
|
||||
@@ -24,13 +24,14 @@ Value: OptionDriverPriceMap which is a wrapper around
|
||||
IDictionary<SecurityExpiration, Tuple<Security, VolSurfaceBBOUnit>>
|
||||
|
||||
----------------------------------------------------------------
|
||||
SingleVolSurfaceInterpGreeksPublisher listens to all inputs and publishes
|
||||
exp vs InterpGreeks
|
||||
SingleVolSurfaceInterpGreeksPublisher (for one vs) listens to all inputs and publishes
|
||||
IDictionary<VolSurfaceExpiration, IEnumerable<Theos>>
|
||||
|
||||
VolSurfaceInterpGreeksMultiplexer listens to all vol surfaces, resolves symbols and
|
||||
publishes symbol vs InterpGreeks
|
||||
VolSurfaceInterpGreeksMultiplexer (for multiple option symbols) listens to all vol
|
||||
surfaces, resolves symbols and for each option_symbol publishes
|
||||
OptionTheoInterpolationData*
|
||||
|
||||
SymbolAggregationPublisher is a thin class that combines individual symbol data and
|
||||
SymbolAggregationPublisher is a thin generic class that combines individual symbol data and
|
||||
publishes symbol vs data map
|
||||
|
||||
LiveTheoPublisher:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.admin;
|
||||
using bts.api.core;
|
||||
using bts.collections;
|
||||
using bts.utils;
|
||||
|
||||
using System;
|
||||
@@ -18,10 +20,10 @@ namespace mbts.spark {
|
||||
IReadOnlyDictionary<uint, string> _account_map = DictionaryHelper.empty<uint, string>();
|
||||
|
||||
[ImportingConstructor]
|
||||
public AdminModel(AdminService service, Lazy<UserTuple> user)
|
||||
: this(service, user.Value) { }
|
||||
public AdminModel(IBus bus, AdminService service, Lazy<UserTuple> user)
|
||||
: this(bus, service, user.Value) { }
|
||||
|
||||
AdminModel(AdminService service, UserTuple user) {
|
||||
AdminModel(IBus bus, AdminService service, UserTuple user) {
|
||||
this.user = user;
|
||||
// accounts
|
||||
on_accounts = service.subscribei_admin_data()
|
||||
@@ -48,7 +50,11 @@ namespace mbts.spark {
|
||||
.Table(UserTuple.key_fn)
|
||||
.Cache() :
|
||||
Observable.Return(TableUpdate<UserTuple, uint>.mk_empty(UserTuple.key_fn).with_added(new[] { user }));
|
||||
_dispose = on_accounts.Subscribe(map => _account_map = map);
|
||||
_dispose = on_accounts.Subscribe(map => {
|
||||
_account_map = map;
|
||||
var account_tuple_map = MappedDictionary.create(map, (id, name) => new AccountTuple(id, name));
|
||||
bus.post(SparkTags.AccountsTag, account_tuple_map);
|
||||
});
|
||||
}
|
||||
|
||||
public IObservable<IReadOnlyDictionary<uint, string>> on_users { get; }
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace mbts.spark {
|
||||
this AlgoTypeSetImmutable algo_set,
|
||||
DynamicOrderHelper dynamic_helper,
|
||||
Func<CustomOrderDefinitionImmutable, CustomOrderDefinitionImmutable> fit_to_type = null,
|
||||
uint display_qty_divisor = 1) =>
|
||||
double display_qty_divisor = 1) =>
|
||||
//
|
||||
algo_set.factory_types
|
||||
.Select(algo_type => {
|
||||
@@ -109,7 +109,7 @@ namespace mbts.spark {
|
||||
SecurityCriteriaClause clause,
|
||||
DynamicOrderHelper dynamic_helper,
|
||||
Func<CustomOrderDefinitionImmutable, CustomOrderDefinitionImmutable> fit_to_type = null,
|
||||
uint display_qty_divisor = 1) {
|
||||
double display_qty_divisor = 1) {
|
||||
//
|
||||
ImmutableDictionary<AlgoKey, AlgoData> rv = null;
|
||||
foreach (var set in security_algo_sets) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.api;
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
@@ -50,9 +51,7 @@ namespace mbts.spark {
|
||||
/// <see cref="subscribe_algos"/>,
|
||||
/// <see cref="subscribe_algos(SecurityCriteriaClause)"/>,
|
||||
/// <see cref="subscribe_algos(SecurityImmutable)"/>
|
||||
_studio_algos_source = studio_model.definitions
|
||||
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
|
||||
.Cache();
|
||||
_studio_algos_source = studio_model.definitions.Cache();
|
||||
}
|
||||
|
||||
public AlgoTypeSetImmutable get_permitted_algos(SecurityImmutable security) =>
|
||||
@@ -67,10 +66,11 @@ namespace mbts.spark {
|
||||
|
||||
public IObservable<TableUpdate<AlgoData, AlgoKey>> subscribe_algos() =>
|
||||
_studio_algos_source
|
||||
.AppendTable(_security_algo_types
|
||||
.Select(s => s.algo_type_set)
|
||||
.Aggregate(AlgoTypeSetImmutable.empty, (a, b) => a.union_with(b))
|
||||
.define_algos(_dynamic_helper));
|
||||
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
|
||||
.AppendTable(_security_algo_types
|
||||
.Select(s => s.algo_type_set)
|
||||
.Aggregate(AlgoTypeSetImmutable.empty, (a, b) => a.union_with(b))
|
||||
.define_algos(_dynamic_helper));
|
||||
|
||||
public IObservable<TableUpdate<AlgoData, AlgoKey>> subscribe_algos(SecurityCriteriaClause security_clause) {
|
||||
var system_algos = _security_algo_types.match_algos(
|
||||
@@ -78,11 +78,13 @@ namespace mbts.spark {
|
||||
_dynamic_helper,
|
||||
def => _fit_to_type(def, security_clause, _security_algo_types));
|
||||
|
||||
return _studio_algos_source.Select(
|
||||
a => (algo: a, fitted_def: _fit_to_type(a.value, security_clause, _security_algo_types)),
|
||||
tup => tup.algo.key)
|
||||
.Select(tup => AlgoData.create(tup.fitted_def, tup.algo.name, tup.algo.key), a => a.key)
|
||||
.AppendTable(system_algos.Values);
|
||||
return _studio_algos_source
|
||||
.Select(
|
||||
a => (algo: a, fitted_def: _fit_to_type(a.value, security_clause, _security_algo_types)),
|
||||
tup => tup.algo.key)
|
||||
.Select(tup => AlgoData.create(tup.fitted_def, tup.algo.name, tup.algo.key), a => a.key)
|
||||
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
|
||||
.AppendTable(system_algos.Values);
|
||||
}
|
||||
|
||||
public IObservable<TableUpdate<AlgoData, AlgoKey>> subscribe_algos(SecurityImmutable security) {
|
||||
@@ -96,14 +98,18 @@ namespace mbts.spark {
|
||||
def => _fit_to_type(def, security, _security_algo_types),
|
||||
_security_auxiliary_model.get_display_quantity_divisor(security));
|
||||
|
||||
return _studio_algos_source.Select(
|
||||
a => (algo: a, fitted_def: _fit_to_type(a.value, security, _security_algo_types)),
|
||||
tup => tup.algo.key)
|
||||
// filter out algos that use an unsupported root, because this would have the effect of duplicating the default algo
|
||||
// (e.g., GlbxIceberg orders would be converted to Limit/Spreader orders for non-Glbx securities).
|
||||
.Filter(tup => tup.fitted_def.actual().CustomCase == tup.algo.value.actual().CustomCase, tup => tup.algo.key)
|
||||
.Select(tup => AlgoData.create(tup.fitted_def, tup.algo.name, tup.algo.key), AlgoData.key_fn)
|
||||
.AppendTable(system_algos.Values);
|
||||
var filtered_studio_algos = _studio_algos_source
|
||||
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
|
||||
.Select(
|
||||
a => (algo: a, fitted_def: _fit_to_type(a.value, security, _security_algo_types)),
|
||||
tup => tup.algo.key)
|
||||
// filter out algos that use an unsupported root, because this would have the effect of duplicating the default algo
|
||||
// (e.g., GlbxIceberg orders would be converted to Limit/Spreader orders for non-Glbx securities).
|
||||
.Filter(tup => tup.fitted_def.actual().CustomCase == tup.algo.value.actual().CustomCase, tup => tup.algo.key)
|
||||
.Select(tup => AlgoData.create(tup.fitted_def, tup.algo.name, tup.algo.key), AlgoData.key_fn);
|
||||
return filtered_studio_algos
|
||||
.StartWith(TableUpdate<AlgoData, AlgoKey>.mk_empty(AlgoData.key_fn))
|
||||
.AppendTable(system_algos.Values);
|
||||
}
|
||||
|
||||
protected virtual void assign_fallback_algo(CustomOrderDefinition mdef, SecurityCriteriaClause security_clause) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace mbts.analytics {
|
||||
using bts;
|
||||
|
||||
using mbts.ui.shell;
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -40,7 +40,7 @@
|
||||
new Dictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData>());
|
||||
|
||||
public VolSurfaceOutput Clone() => new(
|
||||
ProtoBufSerializationHelper.clone_protobuf(volsurface_input),
|
||||
ProtobufSerializationHelper.clone(volsurface_input),
|
||||
new Dictionary<VolSurfaceExpiration, VolCurveOutput>(exp_output_map),
|
||||
new Dictionary<VolSurfaceExpiration, VolSurfaceExpirationAuxiliaryData>(aux_data_map));
|
||||
|
||||
|
||||
@@ -1,112 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using bts.api;
|
||||
using bts.settings;
|
||||
using bts.spark;
|
||||
|
||||
using mbts.spark;
|
||||
|
||||
#nullable enable
|
||||
namespace mbts {
|
||||
[ProtoBuf.ProtoContract()]
|
||||
using bts.api;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Core.Collections;
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
[ProtoContract]
|
||||
[Serializable]
|
||||
public class SecurityExpiration : IEquatable<SecurityExpiration> {
|
||||
[ProtoBuf.ProtoMember(1)]
|
||||
private readonly SecurityTuple _sec_tup;
|
||||
[ProtoBuf.ProtoMember(2)]
|
||||
public readonly DateTime expiration;
|
||||
[ProtoBuf.ProtoMember(2)]
|
||||
private readonly Nullable<uint> _id;
|
||||
public readonly static SecurityExpiration empty = new SecurityExpiration(SecurityTuple.empty.security_exchange, SecurityTuple.empty.security_type, SecurityTuple.empty.security_name, DateTime.MinValue);
|
||||
public class SecurityExpiration : IProtobuf<SecurityExpiration> {
|
||||
public readonly static SecurityExpiration empty = new(
|
||||
SecurityTuple.empty.security_exchange,
|
||||
SecurityTuple.empty.security_type,
|
||||
SecurityTuple.empty.security_name,
|
||||
DateTime.MinValue);
|
||||
|
||||
// for ProtoBuf deserialization
|
||||
private SecurityExpiration() { }
|
||||
// for deserialization
|
||||
SecurityExpiration() =>
|
||||
security_tuple = SecurityTuple.empty;
|
||||
|
||||
public SecurityExpiration(Venue exchange, Security.ContractOneofCase type, string symbol, DateTime exp, Nullable<uint> id = null)
|
||||
public SecurityExpiration(
|
||||
Venue exchange,
|
||||
Security.ContractOneofCase type,
|
||||
string symbol,
|
||||
DateTime exp,
|
||||
uint? id = null)
|
||||
: this(exchange, type.security_type(), symbol, exp, id) { }
|
||||
|
||||
public SecurityExpiration(Venue exchange, SecurityType type, string symbol, DateTime exp, Nullable<uint> id = null)
|
||||
: this(new SecurityTuple(type, symbol, exchange), exp, id) { }
|
||||
public SecurityExpiration(
|
||||
Venue exchange, SecurityType type, string symbol, DateTime expiration, uint? id = null)
|
||||
: this(new SecurityTuple(type, symbol, exchange), expiration, id) { }
|
||||
|
||||
public SecurityExpiration(SecurityTuple sec_tup, DateTime exp, Nullable<uint> id = null) {
|
||||
_sec_tup = sec_tup;
|
||||
expiration = exp;
|
||||
_id = id;
|
||||
public SecurityExpiration(
|
||||
SecurityTuple security_tuple, DateTime expiration, uint? id = null) {
|
||||
this.security_tuple = security_tuple;
|
||||
this.expiration = expiration;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public SecurityTuple security_tuple { get { return _sec_tup; } }
|
||||
[ProtoMember(1)]
|
||||
public SecurityTuple security_tuple { get; }
|
||||
|
||||
public SecurityType security_type { get { return _sec_tup.security_type; } }
|
||||
[ProtoMember(2)]
|
||||
public DateTime expiration { get; }
|
||||
|
||||
public string symbol { get { return _sec_tup.security_name; } }
|
||||
[ProtoMember(3)]
|
||||
public uint? id { get; }
|
||||
|
||||
public Venue exchange { get { return _sec_tup.security_exchange; } }
|
||||
public SecurityType security_type => security_tuple?.security_type ?? SecurityType.Unset;
|
||||
public string symbol => security_tuple?.security_name ?? string.Empty;
|
||||
public Venue exchange => security_tuple?.security_exchange ?? Venue.Unset;
|
||||
|
||||
public bool is_empty() { return Equals(empty); }
|
||||
|
||||
public Nullable<uint> id() { return _id; }
|
||||
|
||||
public SecurityExpiration unset_exchange() {
|
||||
return new SecurityExpiration(Venue.Unset, security_type, symbol, expiration);
|
||||
[Flags]
|
||||
public enum Properties {
|
||||
Type = 1,
|
||||
Symbol = 1 << 1,
|
||||
Expiration = 1 << 2,
|
||||
Venue = 1 << 3,
|
||||
Id = 1 << 4
|
||||
}
|
||||
|
||||
public bool Equals(SecurityExpiration other) {
|
||||
if (other != null)
|
||||
return _sec_tup.Equals(other._sec_tup) && expiration.Equals(other.expiration);
|
||||
else
|
||||
return false;
|
||||
public static IEqualityComparer<SecurityExpiration> get_eq_comparer() =>
|
||||
get_eq_comparer(
|
||||
Properties.Type, Properties.Symbol, Properties.Expiration, Properties.Venue);
|
||||
|
||||
public static IEqualityComparer<SecurityExpiration> get_eq_comparer(Properties props) {
|
||||
return EqualityComparerBuilder.create_comparer(
|
||||
EnumHelper.split_flags(props).Select(get_prop_comparer));
|
||||
|
||||
static IEqualityComparer<SecurityExpiration> get_prop_comparer(Properties single_prop) =>
|
||||
single_prop switch {
|
||||
Properties.Type => wrap(s => s.security_type),
|
||||
Properties.Symbol => wrap(s => s.symbol).to_safe(),
|
||||
Properties.Expiration => wrap(s => s.expiration),
|
||||
Properties.Venue => wrap(s => s.exchange),
|
||||
Properties.Id => wrap(s => s.id).to_safe(),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
static IEqualityComparer<SecurityExpiration> wrap<T>(
|
||||
Func<SecurityExpiration, T> selector, IEqualityComparer<T>? comparer = null) =>
|
||||
EqualityComparer.Create(selector, comparer ?? EqualityComparer<T>.Default);
|
||||
}
|
||||
|
||||
#region Object overrides
|
||||
public static IEqualityComparer<SecurityExpiration> get_eq_comparer(params Properties[] hierarchy) =>
|
||||
EqualityComparerBuilder.create(hierarchy.Select(get_eq_comparer)).to_comparer();
|
||||
|
||||
public override int GetHashCode() { return _sec_tup.GetHashCode() ^ expiration.GetHashCode(); }
|
||||
public static IComparer<SecurityExpiration> get_comparer() =>
|
||||
get_comparer(Properties.Venue, Properties.Type, Properties.Symbol, Properties.Expiration);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return Equals(obj as SecurityExpiration);
|
||||
public static IComparer<SecurityExpiration> get_comparer(Properties prop) {
|
||||
return prop switch {
|
||||
Properties.Type => wrap(s => s.security_type),
|
||||
Properties.Symbol => wrap(s => s.symbol),
|
||||
Properties.Expiration => wrap(s => s.expiration),
|
||||
Properties.Venue => wrap(s => s.exchange),
|
||||
Properties.Id => wrap(s => s.id).to_safe(),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
static IComparer<SecurityExpiration> wrap<T>(
|
||||
Func<SecurityExpiration, T> selector, IComparer<T>? comparer = null) =>
|
||||
(comparer ?? Comparer<T>.Default).wrap(selector);
|
||||
}
|
||||
|
||||
public static bool operator ==(SecurityExpiration x, SecurityExpiration y) {
|
||||
if (Object.ReferenceEquals(x, null)) return Object.ReferenceEquals(y, null);
|
||||
if (Object.ReferenceEquals(y, null)) return false;
|
||||
return x.Equals(y);
|
||||
}
|
||||
public static bool operator !=(SecurityExpiration x, SecurityExpiration y) { return !(x == y); }
|
||||
public static IComparer<SecurityExpiration> get_comparer(params Properties[] hierarchy) =>
|
||||
ComparerBuilder.create(hierarchy.Select(get_comparer)).to_comparer();
|
||||
|
||||
public override string ToString() {
|
||||
return String.Format("{0} {1}", _sec_tup.ToString(), expiration.ToShortDateString());
|
||||
}
|
||||
#endregion
|
||||
public static IComparer<SecurityExpiration> get_exp_based_comparer() =>
|
||||
get_comparer(Properties.Expiration, Properties.Venue, Properties.Type, Properties.Symbol);
|
||||
|
||||
public static int ExpBasedCompare(SecurityExpiration x, SecurityExpiration y) {
|
||||
if (y == null) return 1;
|
||||
if (x == null) return -1;
|
||||
int rv = x.expiration.CompareTo(y.expiration);
|
||||
if (rv == 0)
|
||||
rv = x._sec_tup.CompareTo(y._sec_tup);
|
||||
return rv;
|
||||
}
|
||||
public bool is_empty() => Equals(empty);
|
||||
|
||||
public static int SecBasedCompare(SecurityExpiration x, SecurityExpiration y) {
|
||||
if (y == null) return 1;
|
||||
if (x == null) return -1;
|
||||
int rv = x._sec_tup.CompareTo(y._sec_tup);
|
||||
if (rv == 0)
|
||||
rv = x.expiration.CompareTo(y.expiration);
|
||||
return rv;
|
||||
public SecurityExpiration unset_exchange() =>
|
||||
new(Venue.Unset, security_type, symbol, expiration);
|
||||
|
||||
public SecurityExpiration Clone() =>
|
||||
new(security_tuple.Clone(), expiration, id);
|
||||
|
||||
public bool Equals(SecurityExpiration? other) =>
|
||||
other is not null &&
|
||||
security_tuple.safe_equals(other.security_tuple) &&
|
||||
expiration == other.expiration;
|
||||
|
||||
public override bool Equals(object? obj) =>
|
||||
Equals(obj as SecurityExpiration);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
HashCode.Combine(security_tuple, expiration);
|
||||
|
||||
object ICloneable.Clone() =>
|
||||
Clone();
|
||||
|
||||
public static bool operator ==(SecurityExpiration? x, SecurityExpiration? y) =>
|
||||
x is null ? y is null : x.Equals(y);
|
||||
|
||||
public static bool operator !=(SecurityExpiration x, SecurityExpiration y) =>
|
||||
!(x == y);
|
||||
|
||||
public override string ToString() =>
|
||||
$"{security_tuple} {expiration.ToShortDateString()}";
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class SecurityExpirationProto : IProtobuf<SecurityExpirationProto> {
|
||||
[ProtoMember(1)]
|
||||
public SecurityTuple? security { get; set; }
|
||||
|
||||
[ProtoMember(2)]
|
||||
public double expiration_oa_date { get; set; }
|
||||
|
||||
public SecurityExpirationProto Clone() =>
|
||||
new() { security = security, expiration_oa_date = expiration_oa_date};
|
||||
|
||||
public bool Equals(SecurityExpirationProto? other) =>
|
||||
other is not null &&
|
||||
security.safe_equals(other.security) &&
|
||||
expiration_oa_date.Equals(other.expiration_oa_date);
|
||||
|
||||
public override bool Equals(object? obj) =>
|
||||
Equals(obj as SecurityExpirationProto);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
HashCode.Combine(security, expiration_oa_date);
|
||||
|
||||
object ICloneable.Clone() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoBuf.ProtoContract]
|
||||
public class SecurityExpirationProto {
|
||||
[ProtoBuf.ProtoMember(1)]
|
||||
public SecurityTuple security;
|
||||
|
||||
[ProtoBuf.ProtoMember(2)]
|
||||
public double expiration_oa_date;
|
||||
public static class SecurityExpirationExtensions {
|
||||
public static SecurityExpiration with(
|
||||
this SecurityExpiration sec_exp,
|
||||
Venue? exchange = null,
|
||||
SecurityType? security_type = null,
|
||||
string? symbol = null,
|
||||
DateTime? expiration = null,
|
||||
Optional<uint> id = default) => new(
|
||||
exchange ?? sec_exp.exchange, security_type ?? sec_exp.security_type,
|
||||
symbol ?? sec_exp.symbol,
|
||||
expiration ?? sec_exp.expiration,
|
||||
id.has_value ? id.value : sec_exp.id);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using bts.api;
|
||||
using bts.settings;
|
||||
using bts.spark;
|
||||
|
||||
using mbts.spark;
|
||||
|
||||
#nullable enable
|
||||
namespace mbts {
|
||||
using bts.api;
|
||||
|
||||
using Spark.Core.Data;
|
||||
|
||||
using System;
|
||||
|
||||
public class SecurityStrike : IEquatable<SecurityStrike> {
|
||||
public readonly SecurityExpiration expiration;
|
||||
public readonly double strike;
|
||||
public readonly Tuple<UInt32?, UInt32?> call_put_id;
|
||||
public SecurityStrike(SecurityExpiration expiration, double strike,
|
||||
Tuple<UInt32?, UInt32?> call_put_id = null) {
|
||||
public SecurityStrike(
|
||||
SecurityExpiration expiration, double strike, CallPutTuple<uint?> call_put_id = default) {
|
||||
this.expiration = expiration;
|
||||
this.strike = strike;
|
||||
this.call_put_id = call_put_id;
|
||||
}
|
||||
|
||||
public SecurityStrike(string symbol, DateTime exp, double strike,
|
||||
Tuple<UInt32?, UInt32?> call_put_id = null)
|
||||
: this(new SecurityExpiration(
|
||||
Venue.Unset, SecurityType.Option, symbol, exp), strike, call_put_id) { }
|
||||
public SecurityStrike(
|
||||
string symbol, DateTime exp, double strike, CallPutTuple<uint?> call_put_id = default)
|
||||
: this(new(Venue.Unset, SecurityType.Option, symbol, exp), strike, call_put_id) { }
|
||||
|
||||
public bool Equals(SecurityStrike other) {
|
||||
if (other != null)
|
||||
return expiration.Equals(other.expiration) && strike.Equals(other.strike);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
public SecurityExpiration expiration { get; }
|
||||
public double strike { get; }
|
||||
public CallPutTuple<uint?> call_put_id { get; }
|
||||
|
||||
public override int GetHashCode() { return expiration.GetHashCode() ^ strike.GetHashCode(); }
|
||||
/// TODO: figure out why <see cref="call_put_id"/> is excluded from equality logic
|
||||
/// (was it an accident, or intentional? if the latter, why?)
|
||||
public bool Equals(SecurityStrike? other) =>
|
||||
other is not null &&
|
||||
expiration.Equals(other.expiration) &&
|
||||
strike.Equals(other.strike);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return Equals(obj as SecurityStrike);
|
||||
}
|
||||
public override int GetHashCode() =>
|
||||
HashCode.Combine(expiration, strike);
|
||||
|
||||
public static bool operator ==(SecurityStrike x, SecurityStrike y) {
|
||||
if ((object)x == null) return (object)y == null;
|
||||
return x.Equals(y);
|
||||
}
|
||||
public override bool Equals(object? obj) =>
|
||||
Equals(obj as SecurityStrike);
|
||||
|
||||
public static bool operator !=(SecurityStrike x, SecurityStrike y) { return !(x == y); }
|
||||
public static bool operator ==(SecurityStrike x, SecurityStrike y) =>
|
||||
x is null ? y is null : x.Equals(y);
|
||||
|
||||
public override string ToString() {
|
||||
return String.Format("{0} {1}", expiration.ToString(), strike.ToString());
|
||||
}
|
||||
public static bool operator !=(SecurityStrike x, SecurityStrike y) =>
|
||||
!(x == y);
|
||||
|
||||
public SecurityStrike unset_exchange() { return new SecurityStrike(expiration.unset_exchange(), strike); }
|
||||
public override string ToString() =>
|
||||
$"{expiration} {strike}";
|
||||
|
||||
public SecurityStrike unset_exchange() =>
|
||||
new(expiration.with(exchange: Venue.Unset), strike);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,29 @@ namespace mbts.analytics {
|
||||
/// they all in a way "derive" from core
|
||||
[ProtoContract]
|
||||
public class VolSurfaceSettings {
|
||||
public static VolSurfaceSettings Default(string name) => new() {
|
||||
core_settings = new VolSurfaceCoreSettings {
|
||||
vol_surface_name = name,
|
||||
vol_surface_model = VolSurfaceEnums.VolSurfaceModel.Polynomial,
|
||||
call_rotation = new() { strike_vol_movement_pairs = new() },
|
||||
put_rotation = new() { strike_vol_movement_pairs = new() },
|
||||
strangle = new() { strike_vol_movement_pairs = new() },
|
||||
full_rotation = new() { strike_vol_movement_pairs = new() },
|
||||
atm_shift = new() { strike_vol_movement_pairs = new() },
|
||||
symbols = new(),
|
||||
symbol_structure_list = new(),
|
||||
wing_settings = new() {
|
||||
explicit_slope_wing_settings = new(),
|
||||
implicit_slope_wing_settings = new(),
|
||||
onepoint_slope_wing_settings = new()
|
||||
},
|
||||
unset_expiration_calculation_settings = new(),
|
||||
volpath_settings = new(),
|
||||
safety_limits = new(),
|
||||
},
|
||||
polynomial_model_settings = new()
|
||||
};
|
||||
|
||||
[ProtoMember(10)]
|
||||
public VolSurfaceCoreSettings core_settings;
|
||||
[ProtoMember(15)]
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using bts.api;
|
||||
using bts.settings;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.ui.converters;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using bts.api;
|
||||
using bts.settings;
|
||||
using bts.spark;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.ui.converters;
|
||||
using bts.utils;
|
||||
|
||||
namespace mbts.analytics {
|
||||
/// <summary>
|
||||
/// An expiration in the Vol Surface is expressed
|
||||
@@ -26,7 +26,13 @@ namespace mbts.analytics {
|
||||
public class VolSurfaceExpiration : IEquatable<VolSurfaceExpiration>, IComparable<VolSurfaceExpiration> {
|
||||
public VolSurfaceExpiration() : this(string.Empty, default(DateTime)) { }
|
||||
|
||||
public VolSurfaceExpiration(string symbol, DateTime expiration) {
|
||||
public VolSurfaceExpiration(string symbol, DateTime expiration)
|
||||
:
|
||||
this(bts.api.SecurityType.Option, symbol, expiration) {
|
||||
}
|
||||
|
||||
public VolSurfaceExpiration(bts.api.SecurityType security_type, string symbol, DateTime expiration) {
|
||||
this.security_type = security_type;
|
||||
this.symbol = symbol;
|
||||
this.expiration = expiration;
|
||||
}
|
||||
@@ -36,28 +42,31 @@ namespace mbts.analytics {
|
||||
expiration = exp.expiration;
|
||||
}
|
||||
|
||||
public SecurityExpiration to_security_expiration() {
|
||||
// TODO
|
||||
return new SecurityExpiration(Venue.Unset, SecurityType.Option, symbol, expiration);
|
||||
}
|
||||
public SecurityExpiration to_security_expiration(
|
||||
Venue exchange = Venue.Unset, SecurityType type = SecurityType.Unset) =>
|
||||
new(exchange, security_type, symbol, expiration);
|
||||
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public string symbol;
|
||||
[ProtoBuf.ProtoMember(20)]
|
||||
public DateTime expiration;
|
||||
[ProtoBuf.ProtoMember(30)]
|
||||
public bts.api.SecurityType security_type = bts.api.SecurityType.Option;
|
||||
|
||||
|
||||
public bool Equals(VolSurfaceExpiration other) {
|
||||
return other != null && symbol.Equals(other.symbol) && expiration.Equals(other.expiration);
|
||||
}
|
||||
|
||||
public override int GetHashCode() { return symbol.GetHashCode() ^ expiration.GetHashCode(); }
|
||||
public override int GetHashCode() { return security_type.GetHashCode() ^ symbol.GetHashCode() ^ expiration.GetHashCode(); }
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return Equals(obj as VolSurfaceExpiration);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return String.Format("{0} {1}", symbol, expiration.ToShortDateString());
|
||||
return String.Format("{0} {1} {2}",
|
||||
security_type.ToString().ToUpper().First(), symbol, expiration.ToShortDateString());
|
||||
}
|
||||
|
||||
public int CompareTo(VolSurfaceExpiration other) {
|
||||
@@ -68,15 +77,11 @@ namespace mbts.analytics {
|
||||
}
|
||||
|
||||
public static class VolSurfaceExpirationExtensions {
|
||||
public static SecurityExpiration ToSecurityExp(this VolSurfaceExpiration vs_exp) {
|
||||
var type = SecurityType.Unset;
|
||||
if (!string.IsNullOrWhiteSpace(vs_exp.symbol)) {
|
||||
if (Char.IsLower(vs_exp.symbol.First())) type = SecurityType.Option;
|
||||
else if (Char.IsUpper(vs_exp.symbol.First())) type = SecurityType.Future;
|
||||
}
|
||||
return new SecurityExpiration(Venue.Unset, type, vs_exp.symbol, vs_exp.expiration);
|
||||
}
|
||||
public static SecurityExpiration ToSecurityExp(
|
||||
this VolSurfaceExpiration vs_exp, SecurityType type) =>
|
||||
new(Venue.Unset, type, vs_exp.symbol, vs_exp.expiration);
|
||||
}
|
||||
|
||||
public class VolSurfaceStrikeMap {
|
||||
public readonly IDictionary<VolSurfaceExpiration, IEnumerable<double>> strike_map = null;
|
||||
public VolSurfaceStrikeMap(IEnumerable<string> option_names, SystemContractData sc_data) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using Spark.Core.Collections;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -17,11 +19,14 @@ namespace mbts.analytics
|
||||
/// with default inputs
|
||||
public static VolSurfaceInput sync_input_expirations_to_master_list(VolSurfaceInput vs_input, IEnumerable<VolSurfaceExpiration> system_exp_list)
|
||||
{
|
||||
HashSet<VolSurfaceExpiration> vs_exp_map = new HashSet<VolSurfaceExpiration>(vs_input.surface_parameters.curve_parameters_map.Select(t => t.Item1)),
|
||||
sys_exp_map = new HashSet<VolSurfaceExpiration>(system_exp_list);
|
||||
HashSet<VolSurfaceExpiration> deleted_exps, added_exps;
|
||||
if (VolSurfaceUtilityFunctions.compare_hashsets(sys_exp_map, vs_exp_map, out deleted_exps, out added_exps))
|
||||
{
|
||||
var vs_exp_map = vs_input.surface_parameters.curve_parameters_map
|
||||
.empty_if_null().Select(t => t.Item1).ToHashSet();
|
||||
var sys_exp_map = system_exp_list.ToHashSet();
|
||||
sys_exp_map.difference(
|
||||
vs_exp_map,
|
||||
out HashSet<VolSurfaceExpiration> deleted_exps,
|
||||
out HashSet<VolSurfaceExpiration> added_exps);
|
||||
if (deleted_exps.Any() || added_exps.Any()) {
|
||||
VolSurfaceInput rv = VolSurfaceUtilityFunctions.clone_proto(vs_input);
|
||||
rv.surface_parameters.curve_parameters_map = rv.surface_parameters.curve_parameters_map ?? new List<Tuple<VolSurfaceExpiration, VolCurveParameters>>();
|
||||
rv.surface_parameters.curve_parameters_map.RemoveAll(t => deleted_exps.Contains(t.Item1));
|
||||
@@ -100,7 +105,11 @@ namespace mbts.analytics
|
||||
else if (vs_settings.core_settings.vol_surface_model == VolSurfaceEnums.VolSurfaceModel.Polynomial)
|
||||
{
|
||||
rv.spline_model_parameters = null;
|
||||
rv.polynomial_model_parameters = rv.polynomial_model_parameters ?? new PolynomialModelVolCurveParameters() { atm_vol = Double.NaN, put_points = new List<Tuple<double, double>>(), call_points = new List<Tuple<double, double>>() };
|
||||
rv.polynomial_model_parameters ??= new PolynomialModelVolCurveParameters() {
|
||||
atm_vol = double.NaN
|
||||
};
|
||||
rv.polynomial_model_parameters.put_points ??= new();
|
||||
rv.polynomial_model_parameters.call_points ??= new();
|
||||
if (rv.polynomial_model_parameters.put_points.Count != vs_settings.polynomial_model_settings.n_put_points)
|
||||
rv.polynomial_model_parameters.put_points = _sync_volpoints_to_settings(rv.polynomial_model_parameters.put_points, false, vs_settings.polynomial_model_settings.strike_axis, (int)vs_settings.polynomial_model_settings.n_put_points).ToList();
|
||||
if (rv.polynomial_model_parameters.call_points.Count != vs_settings.polynomial_model_settings.n_call_points)
|
||||
@@ -228,10 +237,8 @@ namespace mbts.analytics
|
||||
return vs_input != null ? get_symbols(vs_input.surface_settings) : new string[] { };
|
||||
}
|
||||
|
||||
public static IEnumerable<string> get_symbols(VolSurfaceSettings vs_settings)
|
||||
{
|
||||
return vs_settings.core_settings.symbol_pricing_model_map.Select(i => i.Item1);
|
||||
}
|
||||
public static IEnumerable<string> get_symbols(VolSurfaceSettings vs_settings) =>
|
||||
vs_settings.core_settings.symbol_pricing_model_map.empty_if_null().Select(i => i.Item1);
|
||||
|
||||
public static VolCurveParameters get_curve_parameters(VolSurfaceInput vs_input, VolSurfaceExpiration exp)
|
||||
{
|
||||
@@ -675,4 +682,4 @@ namespace mbts.analytics
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
namespace mbts.analytics {
|
||||
using bts.utils;
|
||||
|
||||
using mbts.spark.filters;
|
||||
|
||||
using System;
|
||||
|
||||
public class VolSurfaceDriverTheo {
|
||||
public static readonly VolSurfaceDriverTheo empty = new(
|
||||
SecurityExpiration.empty, SecurityExpiration.empty);
|
||||
|
||||
public readonly SecurityExpiration exp;
|
||||
public readonly SecurityExpiration driver_exp;
|
||||
public readonly double market_price;
|
||||
public readonly double driver_market_price;
|
||||
public readonly double roll;
|
||||
public readonly double theo;
|
||||
public readonly double delta;
|
||||
public readonly double settlement_price;
|
||||
public readonly double settlement_theo;
|
||||
public readonly double settlement_delta;
|
||||
|
||||
public VolSurfaceDriverTheo(
|
||||
SecurityExpiration exp,
|
||||
SecurityExpiration driver_exp,
|
||||
double market_price = double.NaN,
|
||||
double driver_market_price = double.NaN,
|
||||
double roll = double.NaN,
|
||||
double theo = double.NaN,
|
||||
double delta = double.NaN,
|
||||
double settlement_price = double.NaN,
|
||||
double settlement_theo = double.NaN,
|
||||
double settlement_delta = double.NaN
|
||||
) {
|
||||
this.exp = exp;
|
||||
this.driver_exp = driver_exp;
|
||||
this.market_price = market_price;
|
||||
this.driver_market_price = driver_market_price;
|
||||
this.roll = roll;
|
||||
this.theo = theo;
|
||||
this.delta = delta;
|
||||
this.settlement_price = settlement_price;
|
||||
this.settlement_theo = settlement_theo;
|
||||
this.settlement_delta = settlement_delta;
|
||||
}
|
||||
|
||||
public bool is_settlement_populated {
|
||||
get {
|
||||
bool non_populated = double.IsNaN(settlement_price) ||
|
||||
Math.Abs(settlement_price) < 0.0000001;
|
||||
return !non_populated;
|
||||
}
|
||||
}
|
||||
|
||||
public double effective_theo => is_settlement_populated ? settlement_price : theo;
|
||||
public double effective_delta => is_settlement_populated ? 0 : delta;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -5,6 +5,9 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using bts;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.utils;
|
||||
|
||||
namespace mbts.analytics
|
||||
{
|
||||
@@ -152,12 +155,8 @@ namespace mbts.analytics
|
||||
out double adjusted_ref_fwd,
|
||||
out double adjusted_ref_vol)
|
||||
{
|
||||
IComparer<ControlPoint> comp = VolSurfaceUtilityFunctions.get_comparer<ControlPoint>(
|
||||
(cp1, cp2) =>
|
||||
{
|
||||
double d1 = Math.Abs(cp1.standard_strike), d2 = Math.Abs(cp2.standard_strike);
|
||||
return (Double.IsNaN(d1) ? Double.PositiveInfinity : d1).CompareTo((Double.IsNaN(d2) ? Double.PositiveInfinity : d2));
|
||||
});
|
||||
var comp = new ComparerBuilder<ControlPoint>()
|
||||
.to_comparer(cp => cp.standard_strike.abs().if_nan(double.PositiveInfinity));
|
||||
adjusted_atm_vol = input_atm_vol + parallel_curve_move + (is_rebuild ? volpath_move : 0.0);
|
||||
adjusted_put_points = calculated_controlpoints.Where(cp => cp.point_type == CoordinatesBasedModelHelper.ControlPoint.PointType.Put).OrderBy(cp => cp, comp).Select(cp => Tuple.Create(cp.input_strike, cp.input_abstract_vol));
|
||||
adjusted_call_points = calculated_controlpoints.Where(cp => cp.point_type == CoordinatesBasedModelHelper.ControlPoint.PointType.Call).OrderBy(cp => cp, comp).Select(cp => Tuple.Create(cp.input_strike, cp.input_abstract_vol));
|
||||
@@ -306,12 +305,8 @@ namespace mbts.analytics
|
||||
IEnumerable<ControlPoint> calculated_controlpoints)
|
||||
{
|
||||
curve_parameters = VolSurfaceUtilityFunctions.clone_proto(curve_parameters);
|
||||
IComparer<ControlPoint> comp = VolSurfaceUtilityFunctions.get_comparer<ControlPoint>(
|
||||
(cp1, cp2) =>
|
||||
{
|
||||
double d1 = Math.Abs(cp1.standard_strike), d2 = Math.Abs(cp2.standard_strike);
|
||||
return (Double.IsNaN(d1) ? Double.PositiveInfinity : d1).CompareTo((Double.IsNaN(d2) ? Double.PositiveInfinity : d2));
|
||||
});
|
||||
var comp = new ComparerBuilder<ControlPoint>()
|
||||
.to_comparer(cp => cp.standard_strike.abs().if_nan(double.PositiveInfinity));
|
||||
var adjusted_put_points = calculated_controlpoints.Where(cp => cp.point_type == CoordinatesBasedModelHelper.ControlPoint.PointType.Put).OrderBy(cp => cp, comp).Select(cp => Tuple.Create(cp.input_strike, cp.input_abstract_vol));
|
||||
var adjusted_call_points = calculated_controlpoints.Where(cp => cp.point_type == CoordinatesBasedModelHelper.ControlPoint.PointType.Call).OrderBy(cp => cp, comp).Select(cp => Tuple.Create(cp.input_strike, cp.input_abstract_vol));
|
||||
if (surface_settings.core_settings.vol_surface_model == VolSurfaceEnums.VolSurfaceModel.CubicSpline)
|
||||
@@ -467,4 +462,4 @@ namespace mbts.analytics
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
namespace mbts.analytics {
|
||||
using bts.api.analytics;
|
||||
|
||||
namespace mbts.analytics
|
||||
{
|
||||
/// move this out later
|
||||
public enum VolSurfaceGreeks
|
||||
{
|
||||
/* *********************Primary Greeks*********************/
|
||||
Vol,
|
||||
Moneyness,
|
||||
Theo,
|
||||
Delta,
|
||||
[Description("BSM Delta")]
|
||||
BSMDelta,
|
||||
FlatVolDelta,
|
||||
Vega,
|
||||
[Description("BSM Vega")]
|
||||
BSMVega,
|
||||
AdjustedVega, /// adjusted_vega = vega * (atm_vega/n_days_atm_vega)
|
||||
Theta,
|
||||
AdjustedTheta, /// what is that ? (its in slide)
|
||||
RollRho,
|
||||
DiscountRho,
|
||||
TimeValue,
|
||||
/* *********************Second Order*********************/
|
||||
Gamma,
|
||||
[Description("BSM Gamma")]
|
||||
BSMGamma,
|
||||
OEV,
|
||||
[Description("BSM OEV")]
|
||||
BSMOEV,
|
||||
Vanna,
|
||||
Volga,
|
||||
Charm,
|
||||
[Description("Vol/Und")]
|
||||
DVolDUnd,
|
||||
[Description("Vol²/DUnd²")]
|
||||
D2VolDUnd2,
|
||||
/* *********************Curve Operations******************/
|
||||
[Description("Price/DRR")]
|
||||
DPriceDRiskReversal,
|
||||
[Description("Price/Strangle")]
|
||||
DPriceDStrangle,
|
||||
[Description("Price/Put Tilt")]
|
||||
DPriceDPutTilt,
|
||||
[Description("Price/Call Tilt")]
|
||||
DPriceDCallTilt,
|
||||
/* *********************Risk*******************************/
|
||||
[Description("PnL")]
|
||||
PNL,
|
||||
ExpirationDelta
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[ProtoBuf.ProtoContract(Name = "VolSurfaceStrikeTheo")]
|
||||
public class VolSurfaceStrikeTheo
|
||||
{
|
||||
public class VolSurfaceStrikeTheo {
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public DateTime exp;
|
||||
[ProtoBuf.ProtoMember(20)]
|
||||
@@ -73,9 +19,8 @@ namespace mbts.analytics
|
||||
}
|
||||
|
||||
[ProtoBuf.ProtoContract(Name = "VolSurfaceSymbolTheos")]
|
||||
public class VolSurfaceSymbolTheos
|
||||
{
|
||||
public class VolSurfaceSymbolTheos {
|
||||
[ProtoBuf.ProtoMember(10)]
|
||||
public List<VolSurfaceStrikeTheo> all_theos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
namespace mbts.analytics {
|
||||
using bts.api.analytics;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.analytics
|
||||
{
|
||||
/// Computes greeks from VolMatrix
|
||||
public class VolSurfaceTheoCalculator
|
||||
{
|
||||
@@ -26,27 +25,27 @@ namespace mbts.analytics
|
||||
{VolSurfaceGreeks.Moneyness, (volmat, is_call) => volmat.standard_strike},
|
||||
{VolSurfaceGreeks.Theo, (volmat, is_call) => compute_theo (volmat, is_call)},
|
||||
{VolSurfaceGreeks.Delta, (volmat, is_call) => compute_delta (volmat, is_call)},
|
||||
{VolSurfaceGreeks.BSMDelta, (volmat, is_call) => compute_bsm_delta (volmat, is_call)},
|
||||
{VolSurfaceGreeks.Bsmdelta, (volmat, is_call) => compute_bsm_delta (volmat, is_call)},
|
||||
{VolSurfaceGreeks.FlatVolDelta, (volmat, is_call) => compute_flatvol_delta (volmat, is_call)},
|
||||
{VolSurfaceGreeks.Vega, (volmat, is_call) => compute_vega (volmat, is_call)},
|
||||
{VolSurfaceGreeks.BSMVega, (volmat, is_call) => compute_bsm_vega (volmat)},
|
||||
{VolSurfaceGreeks.Bsmvega, (volmat, is_call) => compute_bsm_vega (volmat)},
|
||||
{VolSurfaceGreeks.Theta, (volmat, is_call) => compute_theta (volmat, is_call)},
|
||||
{VolSurfaceGreeks.TimeValue, (volmat, is_call) => compute_time_value (volmat, is_call)},
|
||||
|
||||
{VolSurfaceGreeks.Gamma, (volmat, is_call) => compute_gamma (volmat, is_call)},
|
||||
{VolSurfaceGreeks.BSMGamma, (volmat, is_call) => compute_bsm_gamma (volmat)},
|
||||
{VolSurfaceGreeks.OEV, (volmat, is_call) => compute_oev (volmat, is_call)},
|
||||
{VolSurfaceGreeks.BSMOEV, (volmat, is_call) => compute_bsm_oev (volmat, is_call)},
|
||||
{VolSurfaceGreeks.Bsmgamma, (volmat, is_call) => compute_bsm_gamma (volmat)},
|
||||
{VolSurfaceGreeks.Oev, (volmat, is_call) => compute_oev (volmat, is_call)},
|
||||
{VolSurfaceGreeks.Bsmoev, (volmat, is_call) => compute_bsm_oev (volmat, is_call)},
|
||||
{VolSurfaceGreeks.Vanna, (volmat, is_call) => compute_vanna (volmat, is_call)},
|
||||
{VolSurfaceGreeks.Volga, (volmat, is_call) => compute_volga (volmat, is_call)},
|
||||
{VolSurfaceGreeks.Charm, (volmat, is_call) => compute_charm (volmat, is_call)},
|
||||
{VolSurfaceGreeks.DVolDUnd, (volmat, is_call) => compute_dVolDUnd (volmat)},
|
||||
{VolSurfaceGreeks.D2VolDUnd2, (volmat, is_call) => compute_d2VolDUnd2 (volmat)},
|
||||
{VolSurfaceGreeks.DvolDund, (volmat, is_call) => compute_dVolDUnd (volmat)},
|
||||
{VolSurfaceGreeks.D2VolDund2, (volmat, is_call) => compute_d2VolDUnd2 (volmat)},
|
||||
|
||||
{VolSurfaceGreeks.DPriceDRiskReversal, (volmat, is_call) => compute_dPriceDRiskReversal (volmat, is_call)},
|
||||
{VolSurfaceGreeks.DPriceDStrangle, (volmat, is_call) => compute_dPriceDStrangle (volmat, is_call)},
|
||||
{VolSurfaceGreeks.DPriceDPutTilt, (volmat, is_call) => compute_dPriceDPutTilt (volmat, is_call)},
|
||||
{VolSurfaceGreeks.DPriceDCallTilt, (volmat, is_call) => compute_dPriceDCallTilt (volmat, is_call)},
|
||||
{VolSurfaceGreeks.DpriceDriskReversal, (volmat, is_call) => compute_dPriceDRiskReversal (volmat, is_call)},
|
||||
{VolSurfaceGreeks.DpriceDstrangle, (volmat, is_call) => compute_dPriceDStrangle (volmat, is_call)},
|
||||
{VolSurfaceGreeks.DpriceDputTilt, (volmat, is_call) => compute_dPriceDPutTilt (volmat, is_call)},
|
||||
{VolSurfaceGreeks.DpriceDcallTilt, (volmat, is_call) => compute_dPriceDCallTilt (volmat, is_call)},
|
||||
|
||||
};
|
||||
}
|
||||
@@ -443,4 +442,4 @@ namespace mbts.analytics
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,72 @@
|
||||
using System;
|
||||
|
||||
using bts.api;
|
||||
using bts.api.analytics;
|
||||
|
||||
#nullable enable
|
||||
namespace mbts.analytics {
|
||||
using bts.api.analytics;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.spark;
|
||||
|
||||
using System;
|
||||
|
||||
[Serializable]
|
||||
public class AnalyticsExpirationParameters : IEquatable<AnalyticsExpirationParameters> {
|
||||
public readonly SecurityExpiration security,
|
||||
driver_security,
|
||||
deliverable_security,
|
||||
roll_schedule_expiration;
|
||||
public readonly DriverCalculationType driver_calc_type =
|
||||
DriverCalculationType.Direct;
|
||||
public readonly double roll_target, riskless_rate, settlement_price;
|
||||
public readonly double driver_addend, driver_factor,
|
||||
driver_translation_target, delta_beta, vega_beta;
|
||||
public static readonly AnalyticsExpirationParameters empty =
|
||||
new(
|
||||
SecurityExpiration.empty,
|
||||
null,
|
||||
SecurityExpiration.empty,
|
||||
SecurityExpiration.empty,
|
||||
double.NaN,
|
||||
double.NaN,
|
||||
double.NaN,
|
||||
double.NaN,
|
||||
double.NaN);
|
||||
|
||||
public static AnalyticsExpirationParameters empty =
|
||||
new AnalyticsExpirationParameters(SecurityExpiration.empty,
|
||||
SecurityExpiration.empty,
|
||||
SecurityExpiration.empty,
|
||||
SecurityExpiration.empty,
|
||||
Double.NaN, Double.NaN, Double.NaN,
|
||||
Double.NaN, Double.NaN);
|
||||
public readonly ExpirationDriverImmutable driver;
|
||||
public readonly double roll_target;
|
||||
public readonly double riskless_rate;
|
||||
public readonly double settlement_price;
|
||||
public readonly double driver_addend;
|
||||
public readonly double driver_factor;
|
||||
public readonly double driver_translation_target;
|
||||
public readonly double delta_beta;
|
||||
public readonly double vega_beta;
|
||||
|
||||
readonly bts.api.domain.SecurityExpirationImmutable _security;
|
||||
readonly bts.api.domain.SecurityExpirationImmutable _deliverable;
|
||||
readonly bts.api.domain.SecurityExpirationImmutable _roll_schedule;
|
||||
|
||||
public AnalyticsExpirationParameters(
|
||||
SecurityExpiration this_security,
|
||||
SecurityExpiration this_driver_security,
|
||||
SecurityExpiration this_deliverable_security,
|
||||
SecurityExpiration this_roll_schedule_expiration,
|
||||
double this_roll_target, double this_riskless_rate,
|
||||
double this_settlement_price, double this_delta_beta,
|
||||
double this_vega_beta) {
|
||||
security = this_security;
|
||||
driver_security = this_driver_security ?? SecurityExpiration.empty;
|
||||
deliverable_security = this_deliverable_security ?? SecurityExpiration.empty;
|
||||
roll_schedule_expiration = this_roll_schedule_expiration ??
|
||||
SecurityExpiration.empty;
|
||||
roll_target = this_roll_target;
|
||||
riskless_rate = this_riskless_rate;
|
||||
settlement_price = this_settlement_price;
|
||||
delta_beta = this_delta_beta;
|
||||
vega_beta = this_vega_beta;
|
||||
SecurityExpiration security,
|
||||
ExpirationDriverImmutable? driver,
|
||||
SecurityExpiration? deliverable_security,
|
||||
SecurityExpiration? roll_schedule_expiration,
|
||||
double roll_target,
|
||||
double riskless_rate,
|
||||
double settlement_price,
|
||||
double delta_beta,
|
||||
double vega_beta
|
||||
) {
|
||||
_security = security.to_api_sec_expi();
|
||||
deliverable_security ??= SecurityExpiration.empty;
|
||||
roll_schedule_expiration ??= SecurityExpiration.empty;
|
||||
|
||||
this.driver = driver ?? new(new() {
|
||||
TimeWeightedInterpolated = new()
|
||||
});
|
||||
_deliverable = deliverable_security.to_api_sec_expi();
|
||||
_roll_schedule = roll_schedule_expiration.to_api_sec_expi();
|
||||
this.roll_target = roll_target;
|
||||
this.riskless_rate = riskless_rate;
|
||||
this.settlement_price = settlement_price;
|
||||
this.delta_beta = delta_beta;
|
||||
this.vega_beta = vega_beta;
|
||||
}
|
||||
|
||||
public AnalyticsExpirationParameters(ExpirationParameterEntryImmutable db_i) {
|
||||
Func<ExpirationImmutable, DateTime> to_dt = (ExpirationImmutable e) =>
|
||||
new DateTime((int)e.ExpirationYear, (int)e.ExpirationMonth,
|
||||
(int)e.ExpirationDay);
|
||||
security = new SecurityExpiration(db_i.Exchange,
|
||||
db_i.SecurityType, db_i.Symbol, to_dt(db_i.Expiration));
|
||||
driver_calc_type = db_i.DriverCalc;
|
||||
driver_security = new SecurityExpiration(db_i.DriverExchange,
|
||||
db_i.DriverSecurityType, db_i.DriverSymbol,
|
||||
to_dt(db_i.DriverExpiration));
|
||||
deliverable_security = new SecurityExpiration(db_i.DeliverableExchange,
|
||||
db_i.DeliverableSecurityType, db_i.DeliverableSymbol,
|
||||
to_dt(db_i.DeliverableExpiration));
|
||||
roll_schedule_expiration = new SecurityExpiration(db_i.RollScheduleExchange,
|
||||
db_i.RollScheduleSecurityType, db_i.RollScheduleSymbol,
|
||||
to_dt(db_i.RollScheduleExpiration));
|
||||
_security = db_i.SecurityExpiration;
|
||||
_deliverable = db_i.Deliverable;
|
||||
_roll_schedule = db_i.RollSchedule;
|
||||
driver = db_i.Driver;
|
||||
roll_target = db_i.RollTarget;
|
||||
riskless_rate = db_i.RisklessRate;
|
||||
settlement_price = db_i.SettlementPrice;
|
||||
@@ -67,66 +74,41 @@ namespace mbts.analytics {
|
||||
vega_beta = db_i.VegaBeta;
|
||||
}
|
||||
|
||||
public bts.api.analytics.ExpirationParameterEntry to_exp_param_entry() {
|
||||
Func<DateTime, Expiration> to_exp = (DateTime e) => {
|
||||
Expiration rv = new Expiration();
|
||||
rv.ExpirationDay = (uint)e.Day;
|
||||
rv.ExpirationMonth = (uint)e.Month;
|
||||
rv.ExpirationYear = (uint)e.Year;
|
||||
return rv;
|
||||
public SecurityExpiration security => _security.to_sec_exp();
|
||||
public SecurityExpiration driver_security => driver.HasSecurity ?
|
||||
driver.Security.to_sec_exp() : SecurityExpiration.empty;
|
||||
public SecurityExpiration deliverable_security => _deliverable.to_sec_exp();
|
||||
public SecurityExpiration roll_schedule_expiration => _roll_schedule.to_sec_exp();
|
||||
|
||||
public ExpirationParameterEntry to_exp_param_entry() =>
|
||||
new() {
|
||||
SecurityExpiration = _security.CloneToMutable(),
|
||||
Driver = driver.CloneToMutable(),
|
||||
Deliverable = _deliverable.CloneToMutable(),
|
||||
RollSchedule = _roll_schedule.CloneToMutable(),
|
||||
RollTarget = roll_target,
|
||||
RisklessRate = riskless_rate,
|
||||
SettlementPrice = settlement_price,
|
||||
DeltaBeta = delta_beta,
|
||||
VegaBeta = vega_beta
|
||||
};
|
||||
bts.api.analytics.ExpirationParameterEntry rv = new ExpirationParameterEntry();
|
||||
rv.SecurityType = security.security_type;
|
||||
rv.Symbol = security.symbol;
|
||||
rv.Expiration = to_exp(security.expiration);
|
||||
rv.Exchange = security.exchange;
|
||||
|
||||
rv.DriverSecurityType = driver_security.security_type;
|
||||
rv.DriverSymbol = driver_security.symbol;
|
||||
rv.DriverExpiration = to_exp(driver_security.expiration);
|
||||
rv.DriverExchange = driver_security.exchange;
|
||||
public bool Equals(AnalyticsExpirationParameters? other) =>
|
||||
other is not null &&
|
||||
security.Equals(other.security) &&
|
||||
driver_security.safe_equals(other.driver_security) &&
|
||||
deliverable_security.safe_equals(other.deliverable_security) &&
|
||||
roll_schedule_expiration.safe_equals(other.roll_schedule_expiration) &&
|
||||
roll_target.Equals(other.roll_target) &&
|
||||
riskless_rate.Equals(other.riskless_rate) &&
|
||||
settlement_price.Equals(other.settlement_price) &&
|
||||
delta_beta.Equals(other.delta_beta) &&
|
||||
vega_beta.Equals(other.vega_beta);
|
||||
|
||||
rv.DeliverableSecurityType = deliverable_security.security_type;
|
||||
rv.DeliverableSymbol = deliverable_security.symbol;
|
||||
rv.DeliverableExpiration = to_exp(deliverable_security.expiration);
|
||||
rv.DeliverableExchange = deliverable_security.exchange;
|
||||
public override bool Equals(object? obj) =>
|
||||
Equals(obj as AnalyticsExpirationParameters);
|
||||
|
||||
rv.RollScheduleSecurityType = roll_schedule_expiration.security_type;
|
||||
rv.RollScheduleSymbol = roll_schedule_expiration.symbol;
|
||||
rv.RollScheduleExpiration = to_exp(roll_schedule_expiration.expiration);
|
||||
rv.RollScheduleExchange = roll_schedule_expiration.exchange;
|
||||
|
||||
rv.RollTarget = roll_target;
|
||||
rv.RisklessRate = riskless_rate;
|
||||
rv.SettlementPrice = settlement_price;
|
||||
rv.DeltaBeta = delta_beta;
|
||||
rv.VegaBeta = vega_beta;
|
||||
return rv;
|
||||
}
|
||||
|
||||
public bool Equals(AnalyticsExpirationParameters other) {
|
||||
return other != null &&
|
||||
security.Equals(other.security) &&
|
||||
_null_equal(driver_security, other.driver_security) &&
|
||||
_null_equal(deliverable_security, other.deliverable_security) &&
|
||||
_null_equal(roll_schedule_expiration, other.roll_schedule_expiration) &&
|
||||
roll_target.Equals(other.roll_target) &&
|
||||
riskless_rate.Equals(other.riskless_rate) &&
|
||||
settlement_price.Equals(other.settlement_price) &&
|
||||
delta_beta.Equals(other.delta_beta) &&
|
||||
vega_beta.Equals(other.vega_beta);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return Equals(obj as AnalyticsExpirationParameters);
|
||||
}
|
||||
|
||||
public override int GetHashCode() { return security.GetHashCode(); }
|
||||
|
||||
private static bool _null_equal(SecurityExpiration a, SecurityExpiration b) {
|
||||
a = a == null ? null : (a.is_empty() ? null : a);
|
||||
b = b == null ? null : (b.is_empty() ? null : b);
|
||||
return (a == null && b == null) || (a != null && b != null && a.Equals(b));
|
||||
}
|
||||
public override int GetHashCode() =>
|
||||
security.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace mbts.analytics {
|
||||
}
|
||||
|
||||
public IDictionary<mbts.SecurityTuple, double> get_cpp_map() {
|
||||
return input.get_cpp_list().ToDictionary(i => new mbts.SecurityTuple(i.Item1, i.Item2, Venue.Unset), i => i.Item3);
|
||||
return input.get_cpp_list().ToDictionaryCrushed(i => new mbts.SecurityTuple(i.Item1, i.Item2, Venue.Sim), i => i.Item3);
|
||||
}
|
||||
|
||||
private static mbts.SecurityExpiration _unset = mbts.SecurityExpiration.empty;
|
||||
@@ -59,4 +59,4 @@ namespace mbts.analytics {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,13 +51,12 @@ namespace mbts.analytics {
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<Tuple<SecurityType, string, double>> get_cpp_list() {
|
||||
List<Tuple<SecurityType, string, double>> rv = new List<Tuple<SecurityType, string, double>>();
|
||||
public IEnumerable<Tuple<SecurityType, string, double>> get_cpp_list() =>
|
||||
// TODO, cpp
|
||||
return
|
||||
exp_input_map.ToList().Select(kv =>
|
||||
new Tuple<SecurityType, string, double>(kv.Key.security_type, kv.Key.symbol, 1));
|
||||
}
|
||||
exp_input_map
|
||||
.Select(kv => Tuple.Create(kv.Key.security_type, kv.Key.symbol, 1.0))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
public IDictionary<mbts.SecurityTuple, double> get_cpp_map() {
|
||||
return get_cpp_list().ToDictionary(
|
||||
@@ -68,4 +67,4 @@ namespace mbts.analytics {
|
||||
return exp_input_map.Where(kv => filter(kv.Key)).ToDictionary(kv => kv.Key, kv => kv.Value.roll_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using bts.api.analytics;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
namespace mbts.analytics
|
||||
{
|
||||
[TestFixture]
|
||||
@@ -21,16 +26,16 @@ namespace mbts.analytics
|
||||
VolSurfaceGreeks.Gamma,
|
||||
VolSurfaceGreeks.Vol,
|
||||
VolSurfaceGreeks.AdjustedVega,
|
||||
VolSurfaceGreeks.OEV,
|
||||
VolSurfaceGreeks.Oev,
|
||||
VolSurfaceGreeks.Theta,
|
||||
VolSurfaceGreeks.DPriceDRiskReversal,
|
||||
VolSurfaceGreeks.DpriceDriskReversal,
|
||||
VolSurfaceGreeks.Vanna,
|
||||
VolSurfaceGreeks.BSMVega,
|
||||
VolSurfaceGreeks.Bsmvega,
|
||||
VolSurfaceGreeks.Moneyness,
|
||||
};
|
||||
|
||||
Theos t = new Theos();
|
||||
VolSurfaceUtilityFunctions.serialize_proto(t);
|
||||
ProtobufSerializationHelper.serialize(t);
|
||||
|
||||
VolSurfaceTimer timer = new VolSurfaceTimer ();
|
||||
List<VolSurfaceStrikeTheo> all_theos = new List<VolSurfaceStrikeTheo>();
|
||||
@@ -65,7 +70,7 @@ namespace mbts.analytics
|
||||
{
|
||||
Console.WriteLine("\nStrikes {0} Handles {1} Total {2}", n_exps * 1.0 * (strike_end - strike_beg) / strike_inc, n_handles, n_handles * n_exps * 1.0 * (strike_end - strike_beg) / strike_inc);
|
||||
byte[] uncomp, comp;
|
||||
timer.start (); uncomp = VolSurfaceUtilityFunctions.serialize_proto(sym_theos); timer.stop();
|
||||
timer.start (); uncomp = ProtobufSerializationHelper.serialize(sym_theos); timer.stop();
|
||||
Console.WriteLine("Serialization time {0}", timer.last_interval_time_ms);
|
||||
timer.start(); comp = VolSurfaceUtilityFunctions.compress(uncomp); timer.stop();
|
||||
Console.WriteLine("Compression time {0}", timer.last_interval_time_ms);
|
||||
@@ -122,4 +127,4 @@ namespace mbts.analytics
|
||||
Console.WriteLine(String.Format("Min time {0} Max time {1} Median time {2} msecs", times.First(), times.Last(), times[times.Count / 2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#nullable enable
|
||||
namespace mbts.ui {
|
||||
interface ITemplateSelectorKey {
|
||||
string key { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#nullable enable
|
||||
namespace mbts.ui {
|
||||
using bts.collections;
|
||||
|
||||
using System.Collections;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Markup;
|
||||
|
||||
[ContentProperty(nameof(Map))]
|
||||
class KeyedTemplateSelector : DataTemplateSelector {
|
||||
readonly KeyedCollection<TemplateSelectorKeyValue, object?> _map = new(kv => kv.Key);
|
||||
|
||||
public IList Map => _map;
|
||||
public DataTemplate? Default { get; set; }
|
||||
public bool TryMatchType { get; set; }
|
||||
|
||||
public override DataTemplate? SelectTemplate(object? item, DependencyObject container) {
|
||||
if (item is null) {
|
||||
return Default;
|
||||
}
|
||||
var k = _get_key(item);
|
||||
return k is not null && _map.TryGetValue(k, out var result) ?
|
||||
result.Value : Default;
|
||||
}
|
||||
|
||||
object? _get_key(object? item) {
|
||||
if (item is null) {
|
||||
return null;
|
||||
}
|
||||
var key = item;
|
||||
if (key is ITemplateSelectorKey tsk) {
|
||||
key = tsk.key;
|
||||
}
|
||||
if (key is null && TryMatchType) {
|
||||
key = item.GetType();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#nullable enable
|
||||
namespace mbts.ui {
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
|
||||
[ContentProperty(nameof(Value))]
|
||||
class TemplateSelectorKeyValue {
|
||||
public object? Key { get; set; }
|
||||
public DataTemplate? Value { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using bts.api.analytics;
|
||||
using BCG;
|
||||
|
||||
using Disposable = mbts.Disposable;
|
||||
|
||||
@@ -24,6 +25,9 @@ namespace bts {
|
||||
private readonly IBus _bus;
|
||||
private readonly AnalyticsService _analytics_service;
|
||||
private readonly VolSurfaceBusManager _vs_manager;
|
||||
private readonly SpreadManager _spread_manager;
|
||||
private readonly SpreadCalculationSynchronizer _spread_calc;
|
||||
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
@@ -32,6 +36,8 @@ namespace bts {
|
||||
_bus = bus;
|
||||
_analytics_service = analytics_service;
|
||||
_vs_manager = new VolSurfaceBusManager(_bus, _analytics_service);
|
||||
_spread_manager = new SpreadManager(_vs_manager.bb(), "xxx");
|
||||
_spread_calc = new SpreadCalculationSynchronizer(_vs_manager.bb());
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace bts {
|
||||
return;
|
||||
}
|
||||
log.info($"received request to delete the following {keys.Count} securities: {string.Join(", ", keys)}");
|
||||
var (orders, orders_success) = await _orders_model.security_order_groups.FirstAsyncTask(SparkUtils.DefaultTimeOut);
|
||||
var (orders, orders_success) = await _orders_model.security_orders.FirstAsyncTask(SparkUtils.DefaultTimeOut);
|
||||
if (!orders_success) {
|
||||
var msg = "Unable to delete synthetic securities: " +
|
||||
$"timed out waiting for orders table (must verify that a synthetic security does not have any working orders before it can be deleted).";
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
#nullable enable
|
||||
namespace bts.daemons {
|
||||
using bts.api;
|
||||
using bts.utils;
|
||||
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
|
||||
[Export(typeof(IShellDaemon))]
|
||||
public sealed class FakeSymbolDriverDaemon : IShellDaemon, IDisposable {
|
||||
IDisposable _dispose;
|
||||
|
||||
public FakeSymbolDriverDaemon(IBus bus) {
|
||||
int tid_counter = 1;
|
||||
IDictionary<int, string> vol_surfaces = new Dictionary<int, string>();
|
||||
var sec_trades = new Dictionary<uint, List<ITradeLogRow>>();
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.VolSurfaceListTag).Subscribe(vss => vol_surfaces = vss),
|
||||
bus.subscribe(SparkTags.SecuritiesTag).SubscribeEach(secs => {
|
||||
var btc_secs = secs.Values.Where(
|
||||
s => s.security.Symbol == "BTC" &&
|
||||
s.security.ContractCase == Security.ContractOneofCase.Future)
|
||||
.ToList();
|
||||
var fake_trades = btc_secs.Select(
|
||||
s => get_fake_trade(to_sec_exp(s.security), 4200)).ToList();
|
||||
bus.post(SparkTags.TradeLogTag, fake_trades);
|
||||
var bbo_upd_refs = btc_secs.ToDictionary(s => s.security.Id, _ => 0UL);
|
||||
var driver_sym_prices = btc_secs.ToImmutableDictionary(
|
||||
s => to_sec_exp(s.security), get_driver_price);
|
||||
|
||||
double get_driver_price(SecurityMD smd) {
|
||||
var upd_ref = bbo_upd_refs[smd.security.Id];
|
||||
var rv = double.NaN;
|
||||
if (smd.md.get_bbo(ref upd_ref, out var bid, out var ask)) {
|
||||
rv = bid.price.if_nan(ask.price);
|
||||
}
|
||||
bbo_upd_refs[smd.security.Id] = upd_ref;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return Observable.Interval(TimeSpan.FromSeconds(2)).Subscribe(_ => {
|
||||
var bd = driver_sym_prices.ToBuilder();
|
||||
foreach (var sec in btc_secs) {
|
||||
var sec_exp = to_sec_exp(sec.security);
|
||||
bd[sec_exp] = get_driver_price(sec).if_nan(driver_sym_prices[sec_exp]);
|
||||
}
|
||||
driver_sym_prices = bd.ToImmutable();
|
||||
foreach (var vsid in vol_surfaces.Keys) {
|
||||
bus.post(SparkTags.VolSurfaceDriverSymbolPriceTag(vsid), driver_sym_prices);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
static SecurityExpiration to_sec_exp(SecurityImmutable sec) =>
|
||||
new(sec.Exchange, sec.ContractCase, sec.Symbol, sec.security_expiration() ?? DateTime.MinValue, sec.Id);
|
||||
|
||||
ITradeLogRow get_fake_trade(
|
||||
SecurityExpiration sec_exp,
|
||||
double price,
|
||||
int size = 1,
|
||||
string account = "ACC A",
|
||||
double? strike = null,
|
||||
PutOrCall? cp = null,
|
||||
double? theo = null
|
||||
) {
|
||||
var t1 = new FakeTradeLogRow(
|
||||
Interlocked.Increment(ref tid_counter),
|
||||
sec_exp,
|
||||
strike,
|
||||
cp,
|
||||
size,
|
||||
price,
|
||||
theo ?? price + 10 * new Random().NextDouble(),
|
||||
account,
|
||||
true,
|
||||
DateTime.Now,
|
||||
"FakeTrader");
|
||||
return t1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,147 @@
|
||||
using bts.api;
|
||||
using bts.api.synthetic;
|
||||
using bts.md;
|
||||
|
||||
using mbts;
|
||||
using mbts.co2;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using SecurityKey = System.UInt32;
|
||||
|
||||
namespace bts {
|
||||
[Export(typeof(IInitializable))]
|
||||
class MDDaemon : md.MDSource, IObserver<List<SecurityAndHandle>>, IInitializable, IDisposable {
|
||||
readonly Lazy<IBus> _bus;
|
||||
readonly Lazy<AuthTokenImmutable> _token;
|
||||
readonly Lazy<SyntheticSecurityService> _ss;
|
||||
readonly Dictionary<SecurityKey, SecurityMD> _deleted_synthetics = new Dictionary<SecurityKey, SecurityMD>();
|
||||
|
||||
[ImportingConstructor]
|
||||
public MDDaemon(Lazy<IBus> bus, Lazy<AuthTokenImmutable> token, Lazy<SyntheticSecurityService> ss, Lazy<UserTuple> user)
|
||||
: base(mbts.config.Configuration.get<md.RelayOptions>().naggle_ms, MDSourceOptions.DeferInit, user.Value) {
|
||||
_bus = bus;
|
||||
_token = token;
|
||||
_ss = ss;
|
||||
}
|
||||
|
||||
public int priority => 55;
|
||||
|
||||
[Export]
|
||||
public MDConsumer consumer => _consumer;
|
||||
IBus bus => _bus.Value;
|
||||
|
||||
public override void initialize() {
|
||||
_relay.set_token(_token.Value);
|
||||
_dispose.Add(
|
||||
_ss.Value.subscribei_metadata()
|
||||
.Select(upd => upd.all.Where(i => i.HasStrategy).ToDictionary(i => i.SecurityKey, i => i.Strategy))
|
||||
.Subscribe(t => {
|
||||
var deleted_synthetic_keys = new HashSet<uint>(t.Where(kv => kv.Value.Deleted).Select(kv => kv.Key));
|
||||
// add back 'undeleted' synthetics
|
||||
foreach (var kvp in _deleted_synthetics.ToList()) {
|
||||
if (!deleted_synthetic_keys.Contains(kvp.Key)) {
|
||||
_exchange_sec_map.TryAdd(kvp.Key, kvp.Value);
|
||||
_deleted_synthetics.Remove(kvp.Key);
|
||||
_version = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
// Remove any newly deleted synthetics and store them for potential undelete
|
||||
foreach (var key in deleted_synthetic_keys) {
|
||||
if (_exchange_sec_map.TryRemove(key, out SecurityMD deleted)) {
|
||||
_deleted_synthetics[key] = deleted;
|
||||
_version = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
// Post full dictionary of syntheics here
|
||||
bus.post(SparkTags.SyntheticstrategiesTag, t);
|
||||
}));
|
||||
_dispose.Add(on_securities.Subscribe(md => bus.post(SparkTags.ServerSecuritiesTag, md)));
|
||||
Task.Run(run);
|
||||
}
|
||||
}
|
||||
}
|
||||
#nullable enable
|
||||
namespace bts {
|
||||
using bts.api;
|
||||
using bts.api.domain;
|
||||
using bts.api.synthetic;
|
||||
using bts.md;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
|
||||
using mbts;
|
||||
using mbts.co2;
|
||||
using mbts.config;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Core.Data;
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
using SecurityKey = System.UInt32;
|
||||
|
||||
[Export(typeof(IInitializable))]
|
||||
class MDDaemon : IInitializable, IDisposable {
|
||||
readonly Lazy<IBus> _bus_backing;
|
||||
readonly Lazy<UserTuple> _user;
|
||||
readonly Lazy<AuthTokenImmutable> _token;
|
||||
readonly Lazy<SyntheticSecurityService> _ss;
|
||||
readonly Lazy<MDSource> _md_source_backing;
|
||||
readonly ISecurityMetadataStore _security_metadata;
|
||||
readonly TimeSpan _relay_throttle;
|
||||
readonly TimeSpan _output_throttle;
|
||||
readonly List<IDisposable> _resources = new(2);
|
||||
|
||||
[ImportingConstructor]
|
||||
public MDDaemon(
|
||||
Lazy<IBus> bus,
|
||||
Lazy<AuthTokenImmutable> token,
|
||||
Lazy<SyntheticSecurityService> ss,
|
||||
Lazy<UserTuple> user,
|
||||
ISecurityMetadataStore security_metadata)
|
||||
: this(
|
||||
bus,
|
||||
token,
|
||||
ss,
|
||||
user,
|
||||
security_metadata,
|
||||
TimeSpan.FromMilliseconds(Configuration.get<RelayOptions>().naggle_ms),
|
||||
TimeSpan.FromSeconds(1)) { }
|
||||
|
||||
public MDDaemon(
|
||||
Lazy<IBus> bus,
|
||||
Lazy<AuthTokenImmutable> token,
|
||||
Lazy<SyntheticSecurityService> ss,
|
||||
Lazy<UserTuple> user,
|
||||
ISecurityMetadataStore security_metadata,
|
||||
TimeSpan relay_throttle,
|
||||
TimeSpan output_throttle
|
||||
) {
|
||||
_bus_backing = bus;
|
||||
_user = user;
|
||||
_token = token;
|
||||
_ss = ss;
|
||||
_relay_throttle = relay_throttle;
|
||||
_output_throttle = output_throttle;
|
||||
_security_metadata = security_metadata;
|
||||
_md_source_backing = new(() => new(_user.Value, _relay_throttle, _output_throttle));
|
||||
}
|
||||
|
||||
public int priority => 55;
|
||||
|
||||
[Export]
|
||||
public MDConsumer consumer => _md_source.consumer;
|
||||
|
||||
MDSource _md_source => _md_source_backing.Value;
|
||||
IBus _bus => _bus_backing.Value;
|
||||
|
||||
public void initialize() {
|
||||
_md_source.set_token(_token.Value);
|
||||
var synth_src = _ss.Value.subscribei_metadata()
|
||||
.Select(upd => upd.all.Where(m => m.HasStrategy)
|
||||
.ToDictionary(m => m.SecurityKey, m => m.Strategy))
|
||||
.Replay(1)
|
||||
.RefCount();
|
||||
|
||||
// post synth strategies to bus
|
||||
_resources.Add(synth_src.Subscribe(_bus.post_callback(SparkTags.SyntheticstrategiesTag)));
|
||||
|
||||
// state required to detect 'undeleted' synthetics on updates, and add them back to the
|
||||
// security map
|
||||
var deleted_synth_cache = new Dictionary<SecurityKey, SecurityMD>();
|
||||
var last_synth_strategies = new Dictionary<SecurityKey, ExecutionStrategyImmutable>();
|
||||
var synth_id_comparer =
|
||||
EqualityComparer.Create<KeyValuePair<SecurityKey, ExecutionStrategyImmutable>, SecurityKey>(
|
||||
kv => kv.Key);
|
||||
|
||||
_resources.Add(
|
||||
_md_source.subscribe_securities().CombineLatest(
|
||||
synth_src.StartWith(new Dictionary<SecurityKey, ExecutionStrategyImmutable>()))
|
||||
.BufferLatest(_output_throttle)
|
||||
.Subscribe(upd => on_md(upd.First, upd.Second)));
|
||||
|
||||
void on_md(
|
||||
ConcurrentDictionary<SecurityKey, SecurityMD> exch_secs,
|
||||
Dictionary<SecurityKey, ExecutionStrategyImmutable> synth_strategies
|
||||
) {
|
||||
synth_strategies.Where(kv => kv.Value.Deleted).difference_hard(
|
||||
last_synth_strategies.Where(kv => kv.Value.Deleted),
|
||||
out var deleted_synths,
|
||||
out var undeleted_synths,
|
||||
synth_id_comparer);
|
||||
last_synth_strategies = synth_strategies;
|
||||
|
||||
// add back 'undeleted' synthetics
|
||||
foreach (var (id, strategy) in undeleted_synths) {
|
||||
if (deleted_synth_cache.TryPopValue(id, out var synth)) {
|
||||
exch_secs.TryAdd(id, synth);
|
||||
}
|
||||
}
|
||||
|
||||
// remove newly deleted synthetics, and store them for potential undelete
|
||||
foreach (var (id, _) in deleted_synths) {
|
||||
if (exch_secs.TryRemove(id, out var deleted)) {
|
||||
deleted_synth_cache[id] = deleted;
|
||||
}
|
||||
}
|
||||
|
||||
// set 'unit quantity' for each sec
|
||||
foreach (var smd in exch_secs.Values) {
|
||||
var meta = _security_metadata.get(smd.security);
|
||||
if (meta.HasValue) {
|
||||
exch_secs[smd.id] = new(
|
||||
smd.security, smd.md, meta.Value.PriceDisplay, meta.Value.QuantityDisplay);
|
||||
}
|
||||
}
|
||||
_bus.post(SparkTags.ServerSecuritiesTag, exch_secs);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(_resources);
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,17 @@ namespace bts {
|
||||
}
|
||||
|
||||
private void _merge_and_post() {
|
||||
Dictionary<uint, SecurityMD> to_pub = new Dictionary<uint, SecurityMD>(_server_sec_map);
|
||||
Func<SecurityMD, bool> is_active = (sec_md) => {
|
||||
if (sec_md.security.HasFuture || sec_md.security.HasOption) {
|
||||
var exp = sec_md.security.security_expiration();
|
||||
if (exp.HasValue) {
|
||||
return exp.Value.Date >= DateTime.Now.Date.AddDays(-1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
var to_pub = _server_sec_map.Where(kv => is_active(kv.Value)).ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||
foreach (var kv in _fake_sec_map) {
|
||||
to_pub[kv.Key] = kv.Value;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using bts.spark;
|
||||
|
||||
using mbts;
|
||||
using mbts.config;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.spark;
|
||||
@@ -23,6 +24,7 @@ namespace bts.shell {
|
||||
class OrderTicketDaemon : IShellDaemon {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _model;
|
||||
readonly UserSettingsHub _settings_hub;
|
||||
readonly ErrorReporter _error_reporter;
|
||||
readonly bool _close_on_send;
|
||||
IReadOnlyDictionary<uint, SecurityMD> _md_map;
|
||||
@@ -30,12 +32,13 @@ namespace bts.shell {
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
public OrderTicketDaemon(IBus bus, OrdersModel model, ErrorReporter error_reporter)
|
||||
: this(bus, model, error_reporter, Configuration.get<bts.config.OrderTicketOptions>().close_on_send) { }
|
||||
public OrderTicketDaemon(IBus bus, OrdersModel model, UserSettingsHub settings_hub, ErrorReporter error_reporter)
|
||||
: this(bus, model, settings_hub, error_reporter, Configuration.get<bts.config.OrderTicketOptions>().close_on_send) { }
|
||||
|
||||
public OrderTicketDaemon(IBus bus, OrdersModel model, ErrorReporter error_reporter, bool close_on_send) {
|
||||
public OrderTicketDaemon(IBus bus, OrdersModel model, UserSettingsHub settings_hub, ErrorReporter error_reporter, bool close_on_send) {
|
||||
_bus = bus;
|
||||
_model = model;
|
||||
_settings_hub = settings_hub;
|
||||
_error_reporter = error_reporter;
|
||||
_close_on_send = close_on_send;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -62,22 +65,39 @@ namespace bts.shell {
|
||||
window.Show(-200, -160, true);
|
||||
}
|
||||
|
||||
void _show_order_ticket(SecurityMD smd) =>
|
||||
void _show_order_ticket(SecurityMD smd) {
|
||||
var settings_src = _settings_hub.subscribe_default_settings()
|
||||
.Select(s => s.get_ladder_profile(smd.security)?.value ?? LadderSettings.Default);
|
||||
_show_order_window(new OrderTicket {
|
||||
DataContext = new NewOrderTicketVM(_model, smd,
|
||||
Observable.Return(LadderSettings.Default)) {
|
||||
DataContext = new NewOrderTicketVM(_model, smd, settings_src) {
|
||||
close_on_send = _close_on_send
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _show_order_ticket((OrderDefinitionImmutable def, double price, uint size) ops) {
|
||||
if (_md_map.TryGetValue(ops.def.SecurityKey, out SecurityMD smd))
|
||||
_show_order_window(new OrderTicket {
|
||||
DataContext = new NewOrderTicketVM(_model, smd, ops.def, ops.price, ops.size,
|
||||
Observable.Return(LadderSettings.Default.calculator_buttons)) {
|
||||
close_on_send = _close_on_send
|
||||
}
|
||||
});
|
||||
var (def, price, size) = ops;
|
||||
if (!_md_map.TryGetValue(def.SecurityKey, out SecurityMD smd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (def.Account.is_null_or_empty() && def.AccountId == 0) {
|
||||
var settings = _settings_hub.current.get_ladder_profile(smd.security);
|
||||
var default_account = settings?.value?.default_account;
|
||||
if (default_account.HasValue) {
|
||||
var mdef = def.CloneToMutable();
|
||||
// The order ticket will resolve AccountId to a name
|
||||
mdef.AccountId = default_account.Value;
|
||||
def = new(mdef);
|
||||
}
|
||||
}
|
||||
|
||||
_show_order_window(new OrderTicket {
|
||||
DataContext = new NewOrderTicketVM(_model, smd, def, price, size,
|
||||
Observable.Return(LadderSettings.Default.calculator_buttons)) {
|
||||
close_on_send = _close_on_send
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _show_order_tickets(IImmutableSet<uint> securities) {
|
||||
@@ -99,5 +119,14 @@ namespace bts.shell {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
LadderSettings _get_settings(SecurityImmutable sec) {
|
||||
var settings = _settings_hub.current;
|
||||
if (settings.ladder_profiles.TryGetFirst(
|
||||
lsp => lsp.security_criteria.includes(sec), out var lsp)) {
|
||||
return lsp.value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
#nullable enable
|
||||
namespace bts.daemons {
|
||||
using bts.api;
|
||||
using bts.api.analytics;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.api.position;
|
||||
using bts.spark;
|
||||
|
||||
using mbts;
|
||||
using mbts.analytics;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.Core.Collections;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
[Export(typeof(IShellDaemon))]
|
||||
public sealed class ReportDaemon : IShellDaemon, IDisposable {
|
||||
readonly ConcurrentFactory<int, IDisposable> _report_calculators;
|
||||
readonly AnalyticsService _analytics_service;
|
||||
readonly PositionService _position_service;
|
||||
readonly OrderService _order_service;
|
||||
readonly ErrorReporter _error_reporter;
|
||||
readonly IBus _bus;
|
||||
|
||||
IReadOnlyDictionary<int, ReportImmutable> _report_parameters_map =
|
||||
new Dictionary<int, ReportImmutable>();
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ReportDaemon(
|
||||
IBus bus,
|
||||
AnalyticsService analytics_service,
|
||||
PositionService position_service,
|
||||
OrderService order_service,
|
||||
ErrorReporter error_reporter
|
||||
) {
|
||||
_bus = bus;
|
||||
_analytics_service = analytics_service;
|
||||
_position_service = position_service;
|
||||
_error_reporter = error_reporter;
|
||||
_order_service = order_service;
|
||||
_report_calculators = new(mk_report_calculator);
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.ReportBlackboardTags.Create).Subscribe(rp => _create_report(rp).ignore()),
|
||||
bus.subscribe(SparkTags.ReportBlackboardTags.Save).Subscribe(rp => _save_report(rp).ignore()),
|
||||
bus.subscribe(SparkTags.ReportBlackboardTags.Delete).Subscribe(id => _delete_report(id).ignore()),
|
||||
bus.subscribe(SparkTags.ReportBlackboardTags.Activate).Subscribe(_activate_report),
|
||||
bus.subscribe(SparkTags.ReportBlackboardTags.Deactivate).Subscribe(_deactivate_report),
|
||||
_analytics_service.subscribei_reports().Table().Subscribe(_on_reports),
|
||||
_bus.subscribe(SparkTags.SystemContractsTag).CombineLatest(
|
||||
_order_service.subscribe_fills(), _position_service.subscribe_manual_fills())
|
||||
.Subscribe(tup => _on_all_trade_data(tup.First, tup.Second, tup.Third)),
|
||||
_report_calculators);
|
||||
|
||||
IDisposable mk_report_calculator(int id) {
|
||||
if (!_report_parameters_map.TryGetValue(id, out var r)) {
|
||||
Log.Gen.warn($"ReportDaemon failed to find report for id {id}");
|
||||
return Disposable.empty;
|
||||
}
|
||||
Log.Gen.info(
|
||||
$"Activating report calculator for {fmt_rep_title(r)}");
|
||||
var calc = r.ReportType switch {
|
||||
Report.Types.ReportType.Slide => new SlideReportCalculator(_bus, r),
|
||||
Report.Types.ReportType.Combo => new ComboReportCalculator(
|
||||
_bus, r, TimeSpan.FromSeconds(1)),
|
||||
_ => Disposable.empty
|
||||
};
|
||||
|
||||
return Disposable.Create(() => {
|
||||
r = _report_parameters_map.GetValueOrDefault(id, r);
|
||||
Log.Gen.info(
|
||||
$"Deactivating report calculator for {fmt_rep_title(r)}");
|
||||
Disposable.dispose(ref calc);
|
||||
});
|
||||
}
|
||||
|
||||
static string fmt_rep_title(ReportImmutable r) =>
|
||||
$"{r.ReportType} '{r.ReportName}' (id: {r.Id})";
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
|
||||
void _on_reports(TableUpdate<ReportImmutable, int> reports) {
|
||||
_report_parameters_map = reports.state;
|
||||
// recreate calculators with new/changed parameters
|
||||
foreach (var r in reports.added.Concat(reports.changes.New())) {
|
||||
if (_report_calculators.Remove(r.Id)) {
|
||||
_report_calculators.get_or_add(r.Id);
|
||||
}
|
||||
}
|
||||
_bus.post(SparkTags.ReportBlackboardTags.ListTag, _report_parameters_map);
|
||||
}
|
||||
|
||||
async Task _create_report(ReportImmutable report) {
|
||||
Log.Gen.info($"Creating report: {report}");
|
||||
var (new_id, success) = await _error_reporter.safe_execute(
|
||||
() => _analytics_service.get_new_report_id_async(new VoidMessage()),
|
||||
reply => reply.Ok,
|
||||
reply => string.Empty,
|
||||
"creating report");
|
||||
var mreport = report.CloneToMutable();
|
||||
mreport.Id = new_id.NewId;
|
||||
report = new(mreport);
|
||||
await _save_report(report);
|
||||
}
|
||||
|
||||
Task _save_report(ReportImmutable report) {
|
||||
Log.Gen.info($"Saving report: {report}");
|
||||
return _error_reporter.safe_execute(
|
||||
() => _analytics_service.save_report_async(report),
|
||||
rm => rm.Success,
|
||||
rm => rm.ErrorMessage,
|
||||
"saving report");
|
||||
}
|
||||
|
||||
Task _delete_report(int id) {
|
||||
Log.Gen.info($"Deleting report: id={id}");
|
||||
return _error_reporter.safe_execute(
|
||||
() => _analytics_service.delete_report_async(
|
||||
new IntWrapperImmutable(new() { Data = id })),
|
||||
rm => rm.Success,
|
||||
rm => rm.ErrorMessage,
|
||||
"deleting report");
|
||||
}
|
||||
|
||||
void _on_all_trade_data(
|
||||
SystemContractData sc_data, Update<Fill, string> fills, Update<ManualFill, int> man_fills) {
|
||||
if (sc_data is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fills_filtered = fills.all.Where(f => sc_data.sec_md_map.ContainsKey(f.SecurityKey));
|
||||
if (fills.all.Count() > fills_filtered.Count()) {
|
||||
Log.Ui.error(
|
||||
$"ReportDaemon {fills.all.Count() - fills_filtered.Count()} fills could not be " +
|
||||
"resolved");
|
||||
}
|
||||
|
||||
var man_fills_filtered = man_fills.all
|
||||
.Where(m => sc_data.sec_md_map.ContainsKey(m.SecurityKey));
|
||||
if (man_fills.all.Count() > man_fills_filtered.Count()) {
|
||||
Log.Ui.error(
|
||||
$"ReportDaemon {man_fills.all.Count() - man_fills_filtered.Count()} manual fills " +
|
||||
"could not be resolved");
|
||||
}
|
||||
|
||||
var fills_tl = fills_filtered.Select(
|
||||
f => new TradeLogRow(f, sc_data.sec_md_map[f.SecurityKey].security));
|
||||
var m_fills_tl = man_fills_filtered.Select(
|
||||
m => new TradeLogRow(m, sc_data.sec_md_map[m.SecurityKey].security));
|
||||
var merged_fills = fills_tl.Concat(m_fills_tl).ToList();
|
||||
Log.Ui.info($"ReportDaemon posting {merged_fills.Count} trades");
|
||||
_bus.post(SparkTags.TradeLogTag, merged_fills);
|
||||
}
|
||||
|
||||
void _activate_report(int id) =>
|
||||
_report_calculators.get_or_add(id);
|
||||
|
||||
void _deactivate_report(int id) =>
|
||||
_report_calculators.Remove(id);
|
||||
}
|
||||
}
|
||||
+479
-279
@@ -1,279 +1,479 @@
|
||||
using bts;
|
||||
|
||||
using mbts.ui.dpi;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace mbts.ui {
|
||||
public class Icons {
|
||||
public static class Smashicons {
|
||||
public static readonly string
|
||||
AddFileGeometryKey = _fmt("AddFile"),
|
||||
EditDocumentGeometryKey = _fmt("EditDocument"),
|
||||
DeleteDocumentGeometryKey = _fmt("DeleteDocument"),
|
||||
SaveGeometryKey = _fmt("Save");
|
||||
|
||||
static string _fmt(string name) =>
|
||||
$"{nameof(Smashicons)}.{name}.GeometryKey";
|
||||
}
|
||||
public const string
|
||||
AddBoxesGeometryKey = nameof(AddBoxesGeometryKey),
|
||||
AddFileGeometryKey = nameof(AddFileGeometryKey),
|
||||
AddFileThinGeometryKey = nameof(AddFileThinGeometryKey),
|
||||
AttachAlgosKey = nameof(AttachAlgosKey),
|
||||
CheckMarkGeometryKey = nameof(CheckMarkGeometryKey),
|
||||
CopyWFGeometryKey = nameof(CopyWFGeometryKey),
|
||||
CopyBoxesGeometryKey = nameof(CopyBoxesGeometryKey),
|
||||
CurrencyExchangeGeometryKey = nameof(CurrencyExchangeGeometryKey),
|
||||
DataEditGeometryKey = nameof(DataEditGeometryKey),
|
||||
DetachAlgosKey = nameof(DetachAlgosKey),
|
||||
DiskGeometryKey = nameof(DiskGeometryKey),
|
||||
DiskFileGeometryKey = nameof(DiskFileGeometryKey),
|
||||
DocumentGeometryKey = nameof(DocumentGeometryKey),
|
||||
DocumentAddGeometryKey = nameof(DocumentAddGeometryKey),
|
||||
DocumentBrokenGeometryKey = nameof(DocumentBrokenGeometryKey),
|
||||
DocumentCopyGeometryKey = nameof(DocumentCopyGeometryKey),
|
||||
DocumentEmptyGeometryKey = nameof(DocumentEmptyGeometryKey),
|
||||
DocumentPairGeometryKey = nameof(DocumentPairGeometryKey),
|
||||
DocumentStarGeometryKey = nameof(DocumentStarGeometryKey),
|
||||
DraggableGeometryKey = nameof(DraggableGeometryKey),
|
||||
EditDocumentAltGeometryKey = nameof(EditDocumentAltGeometryKey),
|
||||
EditDocumentGeometryKey = nameof(EditDocumentGeometryKey),
|
||||
GearGeometryKey = nameof(GearGeometryKey),
|
||||
HamburgerMenuGeometryKey = nameof(HamburgerMenuGeometryKey),
|
||||
HornGeometryKey = nameof(HornGeometryKey),
|
||||
KeyGeometryKey = nameof(KeyGeometryKey),
|
||||
LetterAKey = nameof(LetterAKey),
|
||||
LetterDKey = nameof(LetterDKey),
|
||||
MenuOrientationGeometryKey = nameof(MenuOrientationGeometryKey),
|
||||
OrdersGeometryKey = nameof(OrdersGeometryKey),
|
||||
PauseGeometryKey = nameof(PauseGeometryKey),
|
||||
PaymentGeometryKey = nameof(PaymentGeometryKey),
|
||||
PencilGeometryKey = nameof(PencilGeometryKey),
|
||||
PlayGeometryKey = nameof(PlayGeometryKey),
|
||||
ProhibitedGeometryKey = nameof(ProhibitedGeometryKey),
|
||||
RubberStampGeometryKey = nameof(RubberStampGeometryKey),
|
||||
SpeakerGeometryKey = nameof(SpeakerGeometryKey),
|
||||
StarGeometryKey = nameof(StarGeometryKey),
|
||||
ToolsGeometryKey = nameof(ToolsGeometryKey),
|
||||
TrashGeometryKey = nameof(TrashGeometryKey),
|
||||
UndoCircularGeometryKey = nameof(UndoCircularGeometryKey),
|
||||
WarningExclamationGeometryKey = nameof(WarningExclamationGeometryKey),
|
||||
WarningGeometryKey = nameof(WarningGeometryKey);
|
||||
|
||||
public static Geometry PlayGeometry => Geometry.Parse(get_geometric_path(PlayGeometryKey).FirstOrDefault() ?? string.Empty);
|
||||
|
||||
static readonly Pen _copy_boxes_pen = new Pen(Brushes.Black, HighDpiExtensions.RoundLayoutValue(5)).frozen();
|
||||
|
||||
static readonly string base_uri = "pack://application:,,,/Images/Icons/";
|
||||
private static ResourceDictionary _resources = (ResourceDictionary)Application.LoadComponent(new Uri("/Images/Vector/Icons.xaml", UriKind.RelativeOrAbsolute));
|
||||
|
||||
private static Uri uri(string filename) {
|
||||
return new Uri(base_uri + filename);
|
||||
}
|
||||
|
||||
[Obsolete("Please only add new vector icons using icons.xaml")]
|
||||
private static ImageSource load_image(string filename) {
|
||||
return new BitmapImage(uri(filename)).frozen();
|
||||
}
|
||||
|
||||
private static ImageSource load_resource_and_freeze(string resource_name) {
|
||||
lock (_resources) {
|
||||
return ((DrawingImage)_resources[resource_name]).frozen();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
public static ImageSource Logo { get { return load_image("logo.ico"); } }
|
||||
public static ImageSource Admin { get { return load_resource_and_freeze("Admin"); } }
|
||||
public static ImageSource Analytics { get { return load_resource_and_freeze("Analytics"); } }
|
||||
public static ImageSource Sheets { get { return load_resource_and_freeze("Sheets"); } }
|
||||
public static ImageSource Trades { get { return load_resource_and_freeze("Trades"); } }
|
||||
public static ImageSource Depth { get { return load_resource_and_freeze("Depth"); } }
|
||||
public static ImageSource Ladder { get { return load_resource_and_freeze("Ladder"); } }
|
||||
public static ImageSource SheetsRed { get { return load_image("SheetsRed.png"); } }
|
||||
public static ImageSource SheetsGreen { get { return load_image("SheetsGreen.png"); } }
|
||||
public static ImageSource Spreads { get { return load_resource_and_freeze("Spreads"); } }
|
||||
public static ImageSource BlueSpreads { get { return load_image("BlueSpread.ico"); } }
|
||||
public static ImageSource Reports { get { return load_resource_and_freeze("Reports"); } }
|
||||
public static ImageSource Risk { get { return load_resource_and_freeze("Risk"); } }
|
||||
public static ImageSource Eye { get { return load_resource_and_freeze("Eye"); } }
|
||||
public static ImageSource Coins { get { return load_resource_and_freeze("Coins"); } }
|
||||
|
||||
|
||||
public static ImageSource DataCopy { get { return load_image("DataCopy.ico"); } }
|
||||
public static ImageSource Refresh { get { return load_image("Refresh.png"); } }
|
||||
public static ImageSource FavoriteWindow { get { return load_image("FavoriteWindow.png"); } }
|
||||
|
||||
public static ImageSource Browser => load_resource_and_freeze("Browser");
|
||||
|
||||
public static ImageSource Date { get { return load_image("Date.png"); } }
|
||||
|
||||
public static ImageSource Cube3d { get { return load_resource_and_freeze("Cube"); } }
|
||||
public static ImageSource Algo { get { return Cube3d; } }
|
||||
public static ImageSource Quote { get { return Math; } }
|
||||
public static ImageSource Math { get { return load_resource_and_freeze("Surface"); } }
|
||||
public static ImageSource Abacus { get { return load_image("Abacus.png"); } }
|
||||
|
||||
public static ImageSource Delete { get { return load_image("Delete.png"); } }
|
||||
public static ImageSource Up_Arrow { get { return load_image("Arrowhead-Up.png"); } }
|
||||
public static ImageSource Down_Arrow { get { return load_image("Arrowhead-Down.png"); } }
|
||||
public static ImageSource Zoom_In { get { return load_image("Zoom-In.png"); } }
|
||||
public static ImageSource Zoom_Out { get { return load_image("Zoom-Out.png"); } }
|
||||
public static ImageSource InsertionArrow { get { return load_image("InsertionArrow.png"); } }
|
||||
#pragma warning restore CS0618
|
||||
|
||||
// Vector icons
|
||||
public static ImageSource Bookmarks { get { return load_resource_and_freeze("Bookmarks"); } }
|
||||
public static ImageSource Window { get { return load_resource_and_freeze("Window"); } }
|
||||
public static ImageSource ActiveWindow { get { return load_resource_and_freeze("ActiveWindow"); } }
|
||||
public static ImageSource Crucible { get { return load_resource_and_freeze("Crucible"); } }
|
||||
|
||||
public static ImageSource Accounting { get { return load_resource_and_freeze("Accounting"); } }
|
||||
public static ImageSource Add { get { return load_resource_and_freeze("Add"); } }
|
||||
public static ImageSource AddBoxes => create_icon(AddBoxesGeometryKey);
|
||||
public static ImageSource AddEncircled { get { return load_resource_and_freeze("AddEncircled"); } }
|
||||
public static ImageSource AddFile => create_icon(Smashicons.AddFileGeometryKey);
|
||||
public static ImageSource AttachAlgos => create_icon(AttachAlgosKey);
|
||||
public static ImageSource Beaker { get { return load_resource_and_freeze("Beaker"); } }
|
||||
public static ImageSource Book { get { return load_resource_and_freeze("Book"); } }
|
||||
|
||||
public static ImageSource Orders => create_icon(OrdersGeometryKey);
|
||||
public static ImageSource AddOrders { get { return load_resource_and_freeze("AddOrders"); } }
|
||||
public static ImageSource CancelOrders { get { return load_resource_and_freeze("CancelOrders"); } }
|
||||
public static ImageSource CancelBuys { get { return load_resource_and_freeze("CancelBuys"); } }
|
||||
public static ImageSource CancelSells { get { return load_resource_and_freeze("CancelSells"); } }
|
||||
public static ImageSource CancelSelected { get { return load_resource_and_freeze("CancelSelected"); } }
|
||||
|
||||
public static ImageSource LadderCancelOrders { get { return load_resource_and_freeze("LadderCancelOrders"); } }
|
||||
public static ImageSource LadderCancelBuys { get { return load_resource_and_freeze("LadderCancelBuys"); } }
|
||||
public static ImageSource LadderCancelSells { get { return load_resource_and_freeze("LadderCancelSells"); } }
|
||||
|
||||
public static ImageSource Synthetics { get { return load_resource_and_freeze("Synthetics"); } }
|
||||
public static ImageSource AddSynthetic { get { return load_resource_and_freeze("AddSynthetic"); } }
|
||||
public static ImageSource RemoveSynthetic { get { return load_resource_and_freeze("RemoveSynthetic"); } }
|
||||
public static ImageSource ModifySynthetic { get { return load_resource_and_freeze("ModifySynthetic"); } }
|
||||
public static ImageSource CloneSynthetic { get { return load_resource_and_freeze("CloneSynthetic"); } }
|
||||
|
||||
public static ImageSource analytics => Math;
|
||||
public static ImageSource ModifyOrder { get { return load_resource_and_freeze("ModifyOrder"); } }
|
||||
public static ImageSource CheckMark => create_icon(CheckMarkGeometryKey);
|
||||
public static ImageSource Clock { get { return load_resource_and_freeze("Clock"); } }
|
||||
public static ImageSource CopyWF => create_icon(CopyWFGeometryKey);
|
||||
public static ImageSource CopyBoxes => create_icon(CopyBoxesGeometryKey, null, _copy_boxes_pen);
|
||||
public static ImageSource CurrencyExchange => create_icon(CurrencyExchangeGeometryKey);
|
||||
public static ImageSource DataEdit => create_icon(DataEditGeometryKey);
|
||||
public static ImageSource Defrag { get { return load_resource_and_freeze("Defrag"); } }
|
||||
public static ImageSource DetachAlgos => create_icon_and_rotate(DetachAlgosKey, Brushes.Red);
|
||||
public static ImageSource Disk => create_icon(DiskGeometryKey);
|
||||
public static ImageSource DiskFile => create_icon(DiskFileGeometryKey);
|
||||
public static ImageSource Display { get { return load_resource_and_freeze("Display"); } }
|
||||
public static ImageSource DocumentAdd => create_icon(DocumentAddGeometryKey);
|
||||
public static ImageSource DocumentBroken => create_icon(DocumentBrokenGeometryKey);
|
||||
public static ImageSource DocumentCopy => create_icon(DocumentCopyGeometryKey);
|
||||
public static ImageSource DocumentEmpty => create_icon(DocumentEmptyGeometryKey);
|
||||
public static ImageSource DocumentPair => create_icon(DocumentPairGeometryKey);
|
||||
public static ImageSource DocumentStar => create_icon(DocumentStarGeometryKey);
|
||||
public static ImageSource Draggable => create_icon(DraggableGeometryKey);
|
||||
public static ImageSource EditDocument => create_icon("Smashicons.EditDocument.GeometryKey");
|
||||
public static ImageSource Export => load_resource_and_freeze("Export");
|
||||
public static ImageSource Filter { get { return load_resource_and_freeze("Filter"); } }
|
||||
public static ImageSource FilterActive { get { return load_resource_and_freeze("FilterActive"); } }
|
||||
public static ImageSource FilterError { get { return load_resource_and_freeze("FilterError"); } }
|
||||
public static ImageSource Gear => create_icon(GearGeometryKey);
|
||||
public static ImageSource HamburgerMenu => create_icon(HamburgerMenuGeometryKey);
|
||||
public static ImageSource Horn => create_icon(HornGeometryKey);
|
||||
public static ImageSource Import => load_resource_and_freeze("Import");
|
||||
public static ImageSource Key => create_icon(KeyGeometryKey);
|
||||
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
|
||||
public static ImageSource MenuOrientation => create_icon(MenuOrientationGeometryKey);
|
||||
public static ImageSource options_view => Sheets;
|
||||
public static ImageSource Pause => create_icon(PauseGeometryKey);
|
||||
public static ImageSource Payment => create_icon(PaymentGeometryKey);
|
||||
public static ImageSource Pencil => create_icon(PencilGeometryKey);
|
||||
public static ImageSource Play => create_icon(PlayGeometryKey);
|
||||
public static ImageSource Prohibited => create_icon(ProhibitedGeometryKey);
|
||||
public static ImageSource RubberStamp => create_icon(RubberStampGeometryKey);
|
||||
public static ImageSource RedoCircular => create_icon(UndoCircularGeometryKey, Brushes.Black, null, new ScaleTransform(-1, 1));
|
||||
public static ImageSource Spawn { get { return load_resource_and_freeze("Spawn"); } }
|
||||
public static ImageSource Speaker => create_icon(SpeakerGeometryKey);
|
||||
public static ImageSource Star => create_icon(StarGeometryKey);
|
||||
public static ImageSource Target { get { return load_resource_and_freeze("Target"); } }
|
||||
public static ImageSource Tetris { get { return load_resource_and_freeze("Tetris"); } }
|
||||
public static ImageSource Tools => create_icon(ToolsGeometryKey);
|
||||
public static ImageSource Trash => create_icon(TrashGeometryKey);
|
||||
public static ImageSource Warning => create_icon(WarningGeometryKey, Brushes.IndianRed);
|
||||
public static ImageSource WarningExclamation => create_icon(WarningExclamationGeometryKey, Brushes.IndianRed);
|
||||
public static ImageSource UndoCircular => create_icon(UndoCircularGeometryKey);
|
||||
public static ImageSource ViewSettings => load_resource_and_freeze("ViewSettings");
|
||||
public static ImageSource vol_surface => Math;
|
||||
public static ImageSource Wrench { get { return load_resource_and_freeze("Wrench"); } }
|
||||
public static ImageSource RightArrow { get { return (load_resource_and_freeze("RightArrow")); } }
|
||||
public static ImageSource DownArrow { get { return (load_resource_and_freeze("DownArrow")); } }
|
||||
public static ImageSource Factory { get { return (load_resource_and_freeze("Factory")); } }
|
||||
public static ImageSource DotsVertical { get { return (load_resource_and_freeze("DotsVertical")); } }
|
||||
public static ImageSource HandPointingOverDocument => load_resource_and_freeze("HandPointingOverDocument");
|
||||
public static ImageSource Support => load_resource_and_freeze("SupportIcon");
|
||||
public static ImageSource FloppyDisk => create_icon("FloppyDisk");
|
||||
|
||||
|
||||
public static ImageSource create_icon(string geometric_path_key) =>
|
||||
create_icon(geometric_path_key, Brushes.Black);
|
||||
|
||||
public static ImageSource create_icon(string geometric_path_key, Brush brush, Pen pen = null, Transform transform = null) {
|
||||
var rv = new DrawingImage(create_drawing(geometric_path_key, brush, pen, transform));
|
||||
rv.Freeze();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static ImageSource create_icon_and_rotate(string geometric_path_key, Brush brush = null, Pen pen = null, Transform transform = null, double degrees = 90) {
|
||||
if (brush == null) {
|
||||
brush = Brushes.Black;
|
||||
}
|
||||
var drawing = create_drawing(geometric_path_key, brush, pen, transform);
|
||||
var dg = new DrawingGroup();
|
||||
dg.Children.Add(drawing);
|
||||
dg.Transform = new RotateTransform(degrees);
|
||||
var rv = new DrawingImage(dg);
|
||||
rv.Freeze();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static Drawing create_drawing(string geometric_path_key) =>
|
||||
create_drawing(geometric_path_key, Brushes.Black);
|
||||
|
||||
public static Drawing create_drawing(string geometric_path_key, Brush brush, Pen pen = null, Transform transform = null) =>
|
||||
create_drawing(get_geometric_path(geometric_path_key), brush, pen, transform);
|
||||
|
||||
public static Drawing create_drawing(IEnumerable<string> paths, Brush brush, Pen pen = null, Transform transform = null) {
|
||||
var drawing_group = new DrawingGroup();
|
||||
foreach (string p in paths) {
|
||||
drawing_group.Children.Add(new GeometryDrawing(brush, pen, Geometry.Parse(p)));
|
||||
}
|
||||
if (transform != null)
|
||||
drawing_group.Transform = transform;
|
||||
return drawing_group;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> get_geometric_path(string geometric_path_key) {
|
||||
object obj;
|
||||
lock (_resources) {
|
||||
obj = _resources[geometric_path_key];
|
||||
}
|
||||
if (obj is string path) {
|
||||
if (string.IsNullOrEmpty(path)) {
|
||||
Log.Api.error($"Resource lookup of '{geometric_path_key}' did not return a valid string.", nameof(geometric_path_key));
|
||||
path = string.Empty;
|
||||
}
|
||||
return new[] { path };
|
||||
} else if (obj is string[] paths) {
|
||||
return paths.Where(p => !string.IsNullOrEmpty(p)).ToArray();
|
||||
} else return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
using bts;
|
||||
using bts.collections;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.ui.dpi;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace mbts.ui {
|
||||
public class Icons {
|
||||
public static class Smashicons {
|
||||
public static readonly string
|
||||
AddFileGeometryKey = _fmt("AddFile"),
|
||||
EditDocumentGeometryKey = _fmt("EditDocument"),
|
||||
DeleteDocumentGeometryKey = _fmt("DeleteDocument"),
|
||||
SaveGeometryKey = _fmt("Save");
|
||||
|
||||
static string _fmt(string name) =>
|
||||
$"{nameof(Smashicons)}.{name}.GeometryKey";
|
||||
}
|
||||
public const string
|
||||
AddBoxesGeometryKey = nameof(AddBoxesGeometryKey),
|
||||
AddFileGeometryKey = nameof(AddFileGeometryKey),
|
||||
AddFileThinGeometryKey = nameof(AddFileThinGeometryKey),
|
||||
ArrowGeometryKey = nameof(ArrowGeometryKey),
|
||||
AttachAlgosKey = nameof(AttachAlgosKey),
|
||||
CallPutGeometryKey = nameof(CallPutGeometryKey),
|
||||
CandidateGeometryKey = nameof(CandidateGeometryKey),
|
||||
CandidateWithAudienceGeometryKey = nameof(CandidateWithAudienceGeometryKey),
|
||||
CheckMarkGeometryKey = nameof(CheckMarkGeometryKey),
|
||||
CopyWFGeometryKey = nameof(CopyWFGeometryKey),
|
||||
CopyBoxesGeometryKey = nameof(CopyBoxesGeometryKey),
|
||||
CurrencyExchangeGeometryKey = nameof(CurrencyExchangeGeometryKey),
|
||||
DataEditGeometryKey = nameof(DataEditGeometryKey),
|
||||
DetachAlgosKey = nameof(DetachAlgosKey),
|
||||
DiskGeometryKey = nameof(DiskGeometryKey),
|
||||
DiskFileGeometryKey = nameof(DiskFileGeometryKey),
|
||||
DocumentGeometryKey = nameof(DocumentGeometryKey),
|
||||
DocumentAddGeometryKey = nameof(DocumentAddGeometryKey),
|
||||
DocumentBrokenGeometryKey = nameof(DocumentBrokenGeometryKey),
|
||||
DocumentCopyGeometryKey = nameof(DocumentCopyGeometryKey),
|
||||
DocumentEmptyGeometryKey = nameof(DocumentEmptyGeometryKey),
|
||||
DocumentPairGeometryKey = nameof(DocumentPairGeometryKey),
|
||||
DocumentStarGeometryKey = nameof(DocumentStarGeometryKey),
|
||||
DraggableGeometryKey = nameof(DraggableGeometryKey),
|
||||
EditDocumentAltGeometryKey = nameof(EditDocumentAltGeometryKey),
|
||||
EditDocumentGeometryKey = nameof(EditDocumentGeometryKey),
|
||||
VolCurveGeometryKey = nameof(VolCurveGeometryKey),
|
||||
FinancialGrowthGeometryKey = nameof(FinancialGrowthGeometryKey),
|
||||
GearGeometryKey = nameof(GearGeometryKey),
|
||||
HamburgerMenuGeometryKey = nameof(HamburgerMenuGeometryKey),
|
||||
HornGeometryKey = nameof(HornGeometryKey),
|
||||
KeyGeometryKey = nameof(KeyGeometryKey),
|
||||
LetterAKey = nameof(LetterAKey),
|
||||
LetterDKey = nameof(LetterDKey),
|
||||
MenuOrientationGeometryKey = nameof(MenuOrientationGeometryKey),
|
||||
OrdersGeometryKey = nameof(OrdersGeometryKey),
|
||||
PauseGeometryKey = nameof(PauseGeometryKey),
|
||||
PaymentGeometryKey = nameof(PaymentGeometryKey),
|
||||
PencilGeometryKey = nameof(PencilGeometryKey),
|
||||
PlayGeometryKey = nameof(PlayGeometryKey),
|
||||
ProhibitedGeometryKey = nameof(ProhibitedGeometryKey),
|
||||
RubberStampGeometryKey = nameof(RubberStampGeometryKey),
|
||||
RecurringScheduleGeometryKey = nameof(RecurringScheduleGeometryKey),
|
||||
SpeakerGeometryKey = nameof(SpeakerGeometryKey),
|
||||
StarGeometryKey = nameof(StarGeometryKey),
|
||||
ToolsGeometryKey = nameof(ToolsGeometryKey),
|
||||
TrashGeometryKey = nameof(TrashGeometryKey),
|
||||
UndoCircularGeometryKey = nameof(UndoCircularGeometryKey),
|
||||
WarningExclamationGeometryKey = nameof(WarningExclamationGeometryKey),
|
||||
WarningGeometryKey = nameof(WarningGeometryKey);
|
||||
|
||||
public static Geometry PlayGeometry => Geometry.Parse(get_geometric_path(PlayGeometryKey).FirstOrDefault() ?? string.Empty);
|
||||
|
||||
static readonly Pen _copy_boxes_pen = new Pen(Brushes.Black, HighDpiExtensions.RoundLayoutValue(5)).frozen();
|
||||
|
||||
static readonly string base_uri = "pack://application:,,,/Images/Icons/";
|
||||
private static ResourceDictionary _resources = (ResourceDictionary)Application.LoadComponent(new Uri("/Images/Vector/Icons.xaml", UriKind.RelativeOrAbsolute));
|
||||
|
||||
private static Uri uri(string filename) {
|
||||
return new Uri(base_uri + filename);
|
||||
}
|
||||
|
||||
[Obsolete("Please only add new vector icons using icons.xaml")]
|
||||
private static ImageSource load_image(string filename) {
|
||||
return new BitmapImage(uri(filename)).frozen();
|
||||
}
|
||||
|
||||
private static ImageSource load_resource_and_freeze(string resource_name) {
|
||||
lock (_resources) {
|
||||
return ((DrawingImage)_resources[resource_name]).frozen();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
public static ImageSource Logo { get { return load_image("logo.ico"); } }
|
||||
public static ImageSource Admin { get { return load_resource_and_freeze("Admin"); } }
|
||||
public static ImageSource Analytics { get { return load_resource_and_freeze("Analytics"); } }
|
||||
public static ImageSource Sheets { get { return load_resource_and_freeze("Sheets"); } }
|
||||
public static ImageSource Trades { get { return load_resource_and_freeze("Trades"); } }
|
||||
public static ImageSource Depth { get { return load_resource_and_freeze("Depth"); } }
|
||||
public static ImageSource Ladder { get { return load_resource_and_freeze("Ladder"); } }
|
||||
public static ImageSource SheetsRed { get { return load_image("SheetsRed.png"); } }
|
||||
public static ImageSource SheetsGreen { get { return load_image("SheetsGreen.png"); } }
|
||||
public static ImageSource Spreads { get { return load_resource_and_freeze("Spreads"); } }
|
||||
public static ImageSource BlueSpreads { get { return load_image("BlueSpread.ico"); } }
|
||||
public static ImageSource Reports { get { return load_resource_and_freeze("Reports"); } }
|
||||
public static ImageSource Risk { get { return load_resource_and_freeze("Risk"); } }
|
||||
public static ImageSource Eye { get { return load_resource_and_freeze("Eye"); } }
|
||||
public static ImageSource Coins { get { return load_resource_and_freeze("Coins"); } }
|
||||
|
||||
|
||||
public static ImageSource DataCopy { get { return load_image("DataCopy.ico"); } }
|
||||
public static ImageSource Refresh { get { return load_image("Refresh.png"); } }
|
||||
public static ImageSource FavoriteWindow { get { return load_image("FavoriteWindow.png"); } }
|
||||
|
||||
public static ImageSource Browser => load_resource_and_freeze("Browser");
|
||||
|
||||
public static ImageSource Date { get { return load_image("Date.png"); } }
|
||||
|
||||
public static ImageSource Cube3d { get { return load_resource_and_freeze("Cube"); } }
|
||||
public static ImageSource Algo { get { return Cube3d; } }
|
||||
public static ImageSource Quote { get { return Math; } }
|
||||
public static ImageSource Math { get { return load_resource_and_freeze("Surface"); } }
|
||||
public static ImageSource Abacus { get { return load_image("Abacus.png"); } }
|
||||
|
||||
public static ImageSource Delete { get { return load_image("Delete.png"); } }
|
||||
public static ImageSource Up_Arrow { get { return load_image("Arrowhead-Up.png"); } }
|
||||
public static ImageSource Down_Arrow { get { return load_image("Arrowhead-Down.png"); } }
|
||||
public static ImageSource Zoom_In { get { return load_image("Zoom-In.png"); } }
|
||||
public static ImageSource Zoom_Out { get { return load_image("Zoom-Out.png"); } }
|
||||
public static ImageSource InsertionArrow { get { return load_image("InsertionArrow.png"); } }
|
||||
#pragma warning restore CS0618
|
||||
|
||||
// Vector icons
|
||||
public static ImageSource Bookmarks { get { return load_resource_and_freeze("Bookmarks"); } }
|
||||
public static ImageSource Window { get { return load_resource_and_freeze("Window"); } }
|
||||
public static ImageSource ActiveWindow { get { return load_resource_and_freeze("ActiveWindow"); } }
|
||||
public static ImageSource Crucible { get { return load_resource_and_freeze("Crucible"); } }
|
||||
|
||||
public static ImageSource Accounting { get { return load_resource_and_freeze("Accounting"); } }
|
||||
public static ImageSource Add { get { return load_resource_and_freeze("Add"); } }
|
||||
public static ImageSource AddBoxes => create_icon(AddBoxesGeometryKey);
|
||||
public static ImageSource AddEncircled { get { return load_resource_and_freeze("AddEncircled"); } }
|
||||
public static ImageSource AddFile => create_icon(Smashicons.AddFileGeometryKey);
|
||||
public static ImageSource AttachAlgos => create_icon(AttachAlgosKey);
|
||||
public static ImageSource Beaker { get { return load_resource_and_freeze("Beaker"); } }
|
||||
public static ImageSource Book { get { return load_resource_and_freeze("Book"); } }
|
||||
|
||||
public static ImageSource Orders => create_icon(OrdersGeometryKey);
|
||||
public static ImageSource AddOrders { get { return load_resource_and_freeze("AddOrders"); } }
|
||||
public static ImageSource CancelOrders { get { return load_resource_and_freeze("CancelOrders"); } }
|
||||
public static ImageSource CancelBuys { get { return load_resource_and_freeze("CancelBuys"); } }
|
||||
public static ImageSource CancelSells { get { return load_resource_and_freeze("CancelSells"); } }
|
||||
public static ImageSource CancelSelected { get { return load_resource_and_freeze("CancelSelected"); } }
|
||||
|
||||
public static ImageSource LadderCancelOrders { get { return load_resource_and_freeze("LadderCancelOrders"); } }
|
||||
public static ImageSource LadderCancelBuys { get { return load_resource_and_freeze("LadderCancelBuys"); } }
|
||||
public static ImageSource LadderCancelSells { get { return load_resource_and_freeze("LadderCancelSells"); } }
|
||||
|
||||
public static ImageSource Synthetics { get { return load_resource_and_freeze("Synthetics"); } }
|
||||
public static ImageSource AddSynthetic { get { return load_resource_and_freeze("AddSynthetic"); } }
|
||||
public static ImageSource RemoveSynthetic { get { return load_resource_and_freeze("RemoveSynthetic"); } }
|
||||
public static ImageSource ModifySynthetic { get { return load_resource_and_freeze("ModifySynthetic"); } }
|
||||
public static ImageSource CloneSynthetic { get { return load_resource_and_freeze("CloneSynthetic"); } }
|
||||
|
||||
public static ImageSource analytics => Math;
|
||||
public static ImageSource ModifyOrder { get { return load_resource_and_freeze("ModifyOrder"); } }
|
||||
|
||||
public static ImageSource call_put => create_icon(CallPutGeometryKey);
|
||||
public static ImageSource call_put_checked =>
|
||||
create_checked_icon(
|
||||
CallPutGeometryKey,
|
||||
brush: Brushes.Black,
|
||||
check_brush: Brushes.Black,
|
||||
check_off_x_pct: 0.9);
|
||||
|
||||
public static ImageSource call_put_unchecked =>
|
||||
create_checked_icon(
|
||||
CallPutGeometryKey,
|
||||
brush: Brushes.Black,
|
||||
check_brush: Brushes.Transparent,
|
||||
check_off_x_pct: 0.9);
|
||||
|
||||
public static ImageSource candidate => create_icon(CandidateGeometryKey);
|
||||
public static ImageSource candidate_unchecked =>
|
||||
create_checked_icon(CandidateGeometryKey, Brushes.Black, check_brush: Brushes.Transparent);
|
||||
public static ImageSource candidate_checked =>
|
||||
create_checked_icon(CandidateGeometryKey, Brushes.Black, check_brush: Brushes.Black);
|
||||
public static ImageSource candidate_with_audience =>
|
||||
create_icon(CandidateWithAudienceGeometryKey);
|
||||
|
||||
public static ImageSource CheckMark => create_icon(CheckMarkGeometryKey);
|
||||
public static ImageSource ChevronRight => new DrawingImage(create_chevron_right_drawing());
|
||||
public static ImageSource ChevronDown => new DrawingImage(create_chevron_down_drawing());
|
||||
public static ImageSource Clock { get { return load_resource_and_freeze("Clock"); } }
|
||||
public static ImageSource CopyWF => create_icon(CopyWFGeometryKey);
|
||||
public static ImageSource CopyBoxes => create_icon(CopyBoxesGeometryKey, null, _copy_boxes_pen);
|
||||
public static ImageSource CurrencyExchange => create_icon(CurrencyExchangeGeometryKey);
|
||||
public static ImageSource DataEdit => create_icon(DataEditGeometryKey);
|
||||
public static ImageSource Defrag { get { return load_resource_and_freeze("Defrag"); } }
|
||||
public static ImageSource DetachAlgos => create_icon_and_rotate(DetachAlgosKey, Brushes.Red);
|
||||
public static ImageSource Disk => create_icon(DiskGeometryKey);
|
||||
public static ImageSource DiskFile => create_icon(DiskFileGeometryKey);
|
||||
public static ImageSource Display { get { return load_resource_and_freeze("Display"); } }
|
||||
public static ImageSource DocumentAdd => create_icon(DocumentAddGeometryKey);
|
||||
public static ImageSource DocumentBroken => create_icon(DocumentBrokenGeometryKey);
|
||||
public static ImageSource DocumentCopy => create_icon(DocumentCopyGeometryKey);
|
||||
public static ImageSource DocumentEmpty => create_icon(DocumentEmptyGeometryKey);
|
||||
public static ImageSource DocumentPair => create_icon(DocumentPairGeometryKey);
|
||||
public static ImageSource DocumentStar => create_icon(DocumentStarGeometryKey);
|
||||
public static ImageSource Draggable => create_icon(DraggableGeometryKey);
|
||||
public static ImageSource EditDocument => create_icon("Smashicons.EditDocument.GeometryKey");
|
||||
public static ImageSource VolCurve => create_icon(VolCurveGeometryKey);
|
||||
public static ImageSource Export => load_resource_and_freeze("Export");
|
||||
public static ImageSource Filter { get { return load_resource_and_freeze("Filter"); } }
|
||||
public static ImageSource FilterActive { get { return load_resource_and_freeze("FilterActive"); } }
|
||||
public static ImageSource FilterError { get { return load_resource_and_freeze("FilterError"); } }
|
||||
public static ImageSource FinancialGrowth => create_icon(FinancialGrowthGeometryKey);
|
||||
public static ImageSource FinancialGrowthGray => create_icon(FinancialGrowthGeometryKey, Brushes.Gray);
|
||||
public static ImageSource Gear => create_icon(GearGeometryKey);
|
||||
public static ImageSource HamburgerMenu => create_icon(HamburgerMenuGeometryKey);
|
||||
public static ImageSource Horn => create_icon(HornGeometryKey);
|
||||
public static ImageSource Import => load_resource_and_freeze("Import");
|
||||
public static ImageSource Key => create_icon(KeyGeometryKey);
|
||||
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
|
||||
public static ImageSource MenuOrientation => create_icon(MenuOrientationGeometryKey);
|
||||
public static ImageSource options_view => Sheets;
|
||||
public static ImageSource Pause => create_icon(PauseGeometryKey);
|
||||
public static ImageSource Payment => create_icon(PaymentGeometryKey);
|
||||
public static ImageSource Pencil => create_icon(PencilGeometryKey);
|
||||
public static ImageSource Play => create_icon(PlayGeometryKey);
|
||||
public static ImageSource Prohibited => create_icon(ProhibitedGeometryKey);
|
||||
public static ImageSource RecurringSchedule => create_icon(RecurringScheduleGeometryKey);
|
||||
public static ImageSource RecurringScheduleGray => create_icon(RecurringScheduleGeometryKey, Brushes.Gray);
|
||||
public static ImageSource RubberStamp => create_icon(RubberStampGeometryKey);
|
||||
public static ImageSource RedoCircular => create_icon(UndoCircularGeometryKey, Brushes.Black, null, new ScaleTransform(-1, 1));
|
||||
public static ImageSource Spawn { get { return load_resource_and_freeze("Spawn"); } }
|
||||
public static ImageSource Speaker => create_icon(SpeakerGeometryKey);
|
||||
public static ImageSource Star => create_icon(StarGeometryKey);
|
||||
public static ImageSource Target { get { return load_resource_and_freeze("Target"); } }
|
||||
public static ImageSource Tetris { get { return load_resource_and_freeze("Tetris"); } }
|
||||
public static ImageSource Tools => create_icon(ToolsGeometryKey);
|
||||
public static ImageSource Trash => create_icon(TrashGeometryKey);
|
||||
public static ImageSource Warning => create_icon(WarningGeometryKey, Brushes.IndianRed);
|
||||
public static ImageSource WarningExclamation => create_icon(WarningExclamationGeometryKey, Brushes.IndianRed);
|
||||
public static ImageSource UndoCircular => create_icon(UndoCircularGeometryKey);
|
||||
|
||||
public static ImageSource ViewSettings => load_resource_and_freeze("ViewSettings");
|
||||
|
||||
public static ImageSource vol_curve_unchecked => create_checked_icon(
|
||||
VolCurveGeometryKey, Brushes.Black, null, Brushes.Transparent, null, check_off_x_pct: 0.8);
|
||||
|
||||
public static ImageSource vol_curve_checked => create_checked_icon(
|
||||
VolCurveGeometryKey, Brushes.Black, null, Brushes.Black, null, check_off_x_pct: 0.8);
|
||||
|
||||
public static ImageSource vol_surface => Math;
|
||||
public static ImageSource Wrench { get { return load_resource_and_freeze("Wrench"); } }
|
||||
public static ImageSource RightArrow => create_icon(
|
||||
ArrowGeometryKey, null, new(Brushes.Black, 3));
|
||||
public static ImageSource DownArrow => create_icon(
|
||||
ArrowGeometryKey, null, new(Brushes.Black, 3), new RotateTransform(90, 0.5, 0.5));
|
||||
public static ImageSource Factory { get { return (load_resource_and_freeze("Factory")); } }
|
||||
public static ImageSource DotsVertical { get { return (load_resource_and_freeze("DotsVertical")); } }
|
||||
public static ImageSource HandPointingOverDocument => load_resource_and_freeze("HandPointingOverDocument");
|
||||
public static ImageSource Support => load_resource_and_freeze("SupportIcon");
|
||||
public static ImageSource FloppyDisk => create_icon("FloppyDisk");
|
||||
|
||||
static Brush _default_chevron_brush => null;
|
||||
static Pen _default_chevron_pen { get; } = new Pen(Brushes.Black, 3).frozen();
|
||||
|
||||
public static ImageSource create_icon(string geometric_path_key) =>
|
||||
create_icon(geometric_path_key, Brushes.Black);
|
||||
|
||||
public static ImageSource create_icon(string geometric_path_key, Brush brush, Pen pen = null, Transform transform = null) {
|
||||
var rv = new DrawingImage(create_drawing(geometric_path_key, brush, pen, transform));
|
||||
rv.Freeze();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static ImageSource create_icon_and_rotate(string geometric_path_key, Brush brush = null, Pen pen = null, Transform transform = null, double degrees = 90) {
|
||||
if (brush == null) {
|
||||
brush = Brushes.Black;
|
||||
}
|
||||
var drawing = create_drawing(geometric_path_key, brush, pen, transform);
|
||||
var dg = new DrawingGroup();
|
||||
dg.Children.Add(drawing);
|
||||
dg.Transform = new RotateTransform(degrees);
|
||||
var rv = new DrawingImage(dg);
|
||||
rv.Freeze();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static Drawing create_drawing(string geometric_path_key) =>
|
||||
create_drawing(geometric_path_key, Brushes.Black);
|
||||
|
||||
public static Drawing create_drawing(string geometric_path_key, Brush brush, Pen pen = null, Transform transform = null) =>
|
||||
create_drawing(get_geometric_path(geometric_path_key), brush, pen, transform);
|
||||
|
||||
public static Drawing create_drawing(IEnumerable<string> paths, Brush brush, Pen pen = null, Transform transform = null) {
|
||||
var drawing_group = new DrawingGroup();
|
||||
foreach (string p in paths) {
|
||||
drawing_group.Children.Add(new GeometryDrawing(brush, pen, Geometry.Parse(p)));
|
||||
}
|
||||
if (transform != null)
|
||||
drawing_group.Transform = transform;
|
||||
return drawing_group;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> get_geometric_path(string geometric_path_key) {
|
||||
object obj;
|
||||
lock (_resources) {
|
||||
obj = _resources[geometric_path_key];
|
||||
}
|
||||
if (obj is string path) {
|
||||
if (string.IsNullOrEmpty(path)) {
|
||||
Log.Api.error($"Resource lookup of '{geometric_path_key}' did not return a valid string.", nameof(geometric_path_key));
|
||||
path = string.Empty;
|
||||
}
|
||||
return new[] { path };
|
||||
} else if (obj is string[] paths) {
|
||||
return paths.Where(p => !string.IsNullOrEmpty(p)).ToArray();
|
||||
} else return null;
|
||||
}
|
||||
|
||||
public static Drawing create_chevron_right_drawing() =>
|
||||
create_chevron_right_drawing(_default_chevron_brush, _default_chevron_pen);
|
||||
|
||||
public static Drawing create_chevron_right_drawing(Brush brush, Pen pen) {
|
||||
var drawing_group = new DrawingGroup();
|
||||
var (left, right) = create_chevron_right_geometry();
|
||||
drawing_group.Children.Add(new GeometryDrawing(brush, pen, left));
|
||||
drawing_group.Children.Add(new GeometryDrawing(brush, pen, right));
|
||||
return drawing_group;
|
||||
}
|
||||
|
||||
public static (Geometry left, Geometry right) create_chevron_right_geometry() {
|
||||
var arrow_geometry_str = _resources[ArrowGeometryKey] as string ?? throw new InvalidOperationException();
|
||||
// Must clone bc for some reason Geometry.Parse returns a frozen Geometry
|
||||
var left = Geometry.Parse(arrow_geometry_str).Clone();
|
||||
var right = left.Clone();
|
||||
// bump 2nd arrow to the right
|
||||
right.Transform = new TranslateTransform(24.0.RoundLayoutValue(), 0);
|
||||
return (left, right);
|
||||
}
|
||||
|
||||
public static Drawing create_chevron_down_drawing() =>
|
||||
create_chevron_down_drawing(_default_chevron_brush, _default_chevron_pen);
|
||||
|
||||
public static Drawing create_chevron_down_drawing(Brush brush, Pen pen) {
|
||||
// Transform arrow1 (left -> top), arrow2 (right -> bottom)
|
||||
var (arrow1, arrow2) = create_chevron_right_geometry();
|
||||
arrow1.Transform = new RotateTransform(90, 0.5, 0.5);
|
||||
|
||||
var arrow2_transform = new TransformGroup();
|
||||
arrow2_transform.Children.Add(new RotateTransform(90, 0.5, 0.5));
|
||||
arrow2_transform.Children.Add(new TranslateTransform(0, -24.0.RoundLayoutValue()));
|
||||
arrow2.Transform = arrow2_transform;
|
||||
|
||||
var drawing_group = new DrawingGroup();
|
||||
drawing_group.Children.Add(new GeometryDrawing(brush, pen, arrow1));
|
||||
drawing_group.Children.Add(new GeometryDrawing(brush, pen, arrow2));
|
||||
return drawing_group;
|
||||
}
|
||||
|
||||
public static ImageSource create_checked_icon(string geometric_path_key) =>
|
||||
create_checked_icon(get_geometric_path(geometric_path_key));
|
||||
|
||||
public static ImageSource create_checked_icon(
|
||||
string geometric_path_key,
|
||||
Brush brush = null,
|
||||
Pen pen = null,
|
||||
Brush check_brush = null,
|
||||
Pen check_pen = null,
|
||||
double check_size_ratio = 0.3,
|
||||
double check_off_x_pct = 1,
|
||||
double check_off_y_pct = 1) =>
|
||||
create_checked_icon(
|
||||
get_geometric_path(geometric_path_key), brush, pen, check_brush, check_pen,
|
||||
check_size_ratio, check_off_x_pct, check_off_y_pct);
|
||||
|
||||
public static ImageSource create_checked_icon(IEnumerable<string> paths) =>
|
||||
create_checked_icon(paths, Brushes.Black, null);
|
||||
|
||||
public static ImageSource create_checked_icon(
|
||||
IEnumerable<string> paths, Brush brush, Pen pen) =>
|
||||
create_checked_icon(paths, brush, pen, Brushes.Black, null);
|
||||
|
||||
public static ImageSource create_checked_icon(
|
||||
IEnumerable<string> paths,
|
||||
Brush brush,
|
||||
Pen pen,
|
||||
Brush check_brush,
|
||||
Pen check_pen,
|
||||
double check_size_ratio = 0.3,
|
||||
double check_off_x_pct = 1,
|
||||
double check_off_y_pct = 1) {
|
||||
var drawing = create_checked_drawing(
|
||||
paths, brush,
|
||||
pen,
|
||||
check_brush,
|
||||
check_pen,
|
||||
check_size_ratio,
|
||||
check_off_x_pct,
|
||||
check_off_y_pct);
|
||||
var rv = new DrawingImage(drawing);
|
||||
rv.Freeze();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static Drawing create_checked_drawing(IEnumerable<string> paths) =>
|
||||
create_checked_drawing(paths, Brushes.Black, null, Brushes.Black, null);
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="DrawingGroup"/> whose children are a 'main drawing' and a check-mark
|
||||
/// drawing
|
||||
/// </summary>
|
||||
/// <param name="brush">
|
||||
/// The <see cref="Brush"/> to use for the main <see cref="Drawing"/>
|
||||
/// </param>
|
||||
/// <param name="pen">
|
||||
/// The <see cref="Pen"/> to use for the main <see cref="Drawing"/>
|
||||
/// </param>
|
||||
/// <param name="check_brush">
|
||||
/// The <see cref="Brush"/> to use for the check-mark <see cref="Drawing"/>
|
||||
/// </param>
|
||||
/// <param name="check_pen">
|
||||
/// The <see cref="Pen"/> to use for the check-mark <see cref="Drawing"/>
|
||||
/// </param>
|
||||
/// <param name="check_size_ratio">
|
||||
/// The size ratio of the check-mark and main drawing
|
||||
/// </param>
|
||||
/// <param name="check_off_x_pct">
|
||||
/// The check-mark horizontal offset, defined as a percentage of the main drawing's width
|
||||
/// </param>
|
||||
/// <param name="check_off_y_pct">
|
||||
/// The check-mark vertical offset, defined as a percentage of the main drawing's height
|
||||
/// </param>
|
||||
public static DrawingGroup create_checked_drawing(
|
||||
IEnumerable<string> main_drawing_paths,
|
||||
Brush brush,
|
||||
Pen pen,
|
||||
Brush check_brush,
|
||||
Pen check_pen,
|
||||
double check_size_ratio = 0.3,
|
||||
double check_off_x_pct = 1,
|
||||
double check_off_y_pct = 1
|
||||
) {
|
||||
var main_drawing_group = new DrawingGroup();
|
||||
main_drawing_group.Children.AddRange(
|
||||
main_drawing_paths.Select(p => new GeometryDrawing(brush, pen, Geometry.Parse(p))));
|
||||
|
||||
var check_geometry = _check_mark_geometry().Clone();
|
||||
var main_right = main_drawing_group.Bounds.Right;
|
||||
var main_bottom = main_drawing_group.Bounds.Bottom;
|
||||
|
||||
var check_scale = (main_right / check_geometry.Bounds.Width)
|
||||
.max(main_bottom / check_geometry.Bounds.Height);
|
||||
var check_net_scale = check_size_ratio * check_scale;
|
||||
var check_transform = new TransformGroup();
|
||||
check_transform.Children.AddRange(
|
||||
new ScaleTransform(check_net_scale, check_net_scale),
|
||||
new TranslateTransform(main_right * check_off_x_pct, main_bottom * check_off_y_pct));
|
||||
check_geometry.Transform = check_transform;
|
||||
|
||||
main_drawing_group.Children.Add(new GeometryDrawing(check_brush, check_pen, check_geometry));
|
||||
return main_drawing_group;
|
||||
}
|
||||
|
||||
static Geometry _check_mark_geometry() =>
|
||||
Geometry.Parse(_resources[CheckMarkGeometryKey] as string ??
|
||||
throw new InvalidOperationException());
|
||||
}
|
||||
}
|
||||
@@ -166,6 +166,22 @@
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<!-- Icon by <a href="https://freeicons.io/profile/6156">Reda</a> on <a href="https://freeicons.io">freeicons.io</a> -->
|
||||
<x:Array x:Key="{x:Static mbts:Icons.RecurringScheduleGeometryKey}" Type="sys:String">
|
||||
<sys:String>
|
||||
M69.66,4.05c0-2.23,2.2-4.05,4.94-4.05c2.74,0,4.94,1.81,4.94,4.05v17.72c0,2.23-2.2,4.05-4.94,4.05 c-2.74,0-4.94-1.81-4.94-4.05V4.05L69.66,4.05z M91.37,57.03c4.26,0,8.33,0.85,12.05,2.39c3.87,1.6,7.34,3.94,10.24,6.84 c2.9,2.9,5.24,6.38,6.84,10.23c1.54,3.72,2.39,7.79,2.39,12.05c0,4.26-0.85,8.33-2.39,12.05c-1.6,3.87-3.94,7.34-6.84,10.24 c-2.9,2.9-6.38,5.24-10.23,6.84c-3.72,1.54-7.79,2.39-12.05,2.39c-4.26,0-8.33-0.85-12.05-2.39c-3.87-1.6-7.34-3.94-10.24-6.84 c-2.9-2.9-5.24-6.38-6.84-10.24c-1.54-3.72-2.39-7.79-2.39-12.05c0-4.26,0.85-8.33,2.39-12.05c1.6-3.87,3.94-7.34,6.84-10.24 c2.9-2.9,6.38-5.24,10.23-6.84C83.04,57.88,87.1,57.03,91.37,57.03L91.37,57.03z M89.01,75.37c0-0.76,0.31-1.45,0.81-1.95l0,0l0,0 c0.5-0.5,1.19-0.81,1.96-0.81c0.77,0,1.46,0.31,1.96,0.81c0.5,0.5,0.81,1.19,0.81,1.96v14.74l11.02,6.54l0.09,0.06 c0.61,0.39,1.01,0.98,1.17,1.63c0.17,0.68,0.09,1.42-0.28,2.06l-0.02,0.03c-0.02,0.04-0.04,0.07-0.07,0.1 c-0.39,0.6-0.98,1-1.62,1.16c-0.68,0.17-1.42,0.09-2.06-0.28l-12.32-7.29c-0.43-0.23-0.79-0.58-1.05-0.99 c-0.26-0.42-0.41-0.91-0.41-1.43h0L89.01,75.37L89.01,75.37L89.01,75.37z M109.75,70.16c-2.4-2.4-5.26-4.33-8.43-5.64 c-3.06-1.27-6.42-1.96-9.95-1.96s-6.89,0.7-9.95,1.96c-3.17,1.31-6.03,3.24-8.43,5.64c-2.4,2.4-4.33,5.26-5.64,8.43 c-1.27,3.06-1.96,6.42-1.96,9.95c0,3.53,0.7,6.89,1.96,9.95c1.31,3.17,3.24,6.03,5.64,8.43c2.4,2.4,5.26,4.33,8.43,5.64 c3.06,1.27,6.42,1.96,9.95,1.96s6.89-0.7,9.95-1.96c3.17-1.31,6.03-3.24,8.43-5.64c4.71-4.71,7.61-11.2,7.61-18.38 c0-3.53-0.7-6.89-1.96-9.95C114.08,75.42,112.15,72.56,109.75,70.16L109.75,70.16z M13.45,57.36c-0.28,0-0.53-1.23-0.53-2.74 c0-1.51,0.22-2.73,0.53-2.73h13.48c0.28,0,0.53,1.23,0.53,2.73c0,1.51-0.22,2.74-0.53,2.74H13.45L13.45,57.36z M34.94,57.36 c-0.28,0-0.53-1.23-0.53-2.74c0-1.51,0.22-2.73,0.53-2.73h13.48c0.28,0,0.53,1.23,0.53,2.73c0,1.51-0.22,2.74-0.53,2.74H34.94 L34.94,57.36z M56.43,57.36c-0.28,0-0.53-1.23-0.53-2.74c0-1.51,0.22-2.73,0.53-2.73h13.48c0.28,0,0.53,1.22,0.53,2.72 c-1.35,0.84-2.65,1.76-3.89,2.75H56.43L56.43,57.36z M13.48,73.04c-0.28,0-0.53-1.23-0.53-2.74c0-1.51,0.22-2.74,0.53-2.74h13.48 c0.28,0,0.53,1.23,0.53,2.74c0,1.51-0.22,2.74-0.53,2.74H13.48L13.48,73.04z M34.97,73.04c-0.28,0-0.53-1.23-0.53-2.74 c0-1.51,0.22-2.74,0.53-2.74h13.48c0.28,0,0.53,1.23,0.53,2.74c0,1.51-0.22,2.74-0.53,2.74H34.97L34.97,73.04z M13.51,88.73 c-0.28,0-0.53-1.23-0.53-2.74c0-1.51,0.22-2.74,0.53-2.74h13.48c0.28,0,0.53,1.23,0.53,2.74c0,1.51-0.22,2.74-0.53,2.74H13.51 L13.51,88.73z M35,88.73c-0.28,0-0.53-1.23-0.53-2.74c0-1.51,0.22-2.74,0.53-2.74h13.48c0.28,0,0.53,1.23,0.53,2.74 c0,1.51-0.22,2.74-0.53,2.74H35L35,88.73z M25.29,4.05c0-2.23,2.2-4.05,4.94-4.05c2.74,0,4.94,1.81,4.94,4.05v17.72 c0,2.23-2.21,4.05-4.94,4.05c-2.74,0-4.94-1.81-4.94-4.05V4.05L25.29,4.05z M5.44,38.74h94.08v-20.4c0-0.7-0.28-1.31-0.73-1.76 c-0.45-0.45-1.09-0.73-1.76-0.73h-9.02c-1.51,0-2.74-1.23-2.74-2.74c0-1.51,1.23-2.74,2.74-2.74h9.02c2.21,0,4.19,0.89,5.64,2.34 c1.45,1.45,2.34,3.43,2.34,5.64v32.39c-1.8-0.62-3.65-1.12-5.55-1.49v-5.06h0.06H5.44v52.83c0,0.7,0.28,1.31,0.73,1.76 c0.45,0.45,1.09,0.73,1.76,0.73h44.71c0.51,1.9,1.15,3.75,1.92,5.53H7.98c-2.2,0-4.19-0.89-5.64-2.34C0.89,101.26,0,99.28,0,97.07 V18.36c0-2.2,0.89-4.19,2.34-5.64c1.45-1.45,3.43-2.34,5.64-2.34h9.63c1.51,0,2.74,1.23,2.74,2.74c0,1.51-1.23,2.74-2.74,2.74H7.98 c-0.7,0-1.31,0.28-1.76,0.73c-0.45,0.45-0.73,1.09-0.73,1.76v20.4H5.44L5.44,38.74z M43.07,15.85c-1.51,0-2.74-1.23-2.74-2.74 c0-1.51,1.23-2.74,2.74-2.74h18.36c1.51,0,2.74,1.23,2.74,2.74c0,1.51-1.23,2.74-2.74,2.74H43.07L43.07,15.85z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<!-- Icon by <a href="https://freeicons.io/profile/75801">Hilmy Abiyyu Asad</a> on <a href="https://freeicons.io">freeicons.io</a> -->
|
||||
<x:Array x:Key="{x:Static mbts:Icons.FinancialGrowthGeometryKey}" Type="sys:String">
|
||||
<sys:String>
|
||||
M29,25H27V13a2,2,0,0,0-2-2H23a2,2,0,0,0-2,2V25H19V16a2,2,0,0,0-2-2H15a2,2,0,0,0-2,2v9H11V19a2,2,0,0,0-2-2H7a2,2,0,0,0-2,2v6H3a1,1,0,0,0,0,2H29a1,1,0,0,0,0-2ZM7,25V19H9v6Zm8,0V16h2v9Zm8,0V13h2V25Z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
M5,15a1,1,0,0,0,.346-.062L24.062,8.052l-.241.513a1,1,0,1,0,1.811.849L26.905,6.7a1,1,0,0,0-.481-1.33L23.707,4.094a1,1,0,0,0-.848,1.812l.547.256-18.752,6.9A1,1,0,0,0,5,15Z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
<!--File (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/freepik
|
||||
Ttile: File free icon
|
||||
@@ -388,6 +404,55 @@
|
||||
c-84.15,0-153-68.85-153-153h-51c0,112.2,91.8,204,204,204s204-91.8,204-204S360.825,89.25,248.625,89.25z
|
||||
</sys:String>
|
||||
|
||||
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
|
||||
<x:Array x:Key="{x:Static mbts:Icons.VolCurveGeometryKey}" Type="sys:String">
|
||||
<sys:String>
|
||||
M952.6,30.6C893.8,30.6,842.2,49.2,799,86c-36.8,31.3-67,75.1-92.4,133.7c-44.4,102.4-65.7,230.7-86.2,354.7C594.3,731.8,567.3,894.6,500,894.6c-24.3,0-60.5-10.5-94.2-101.6c-26.3-71.2-41.6-166-56.4-257.8C337,457.9,325.2,384.9,307.3,329c-10.5-32.8-22.5-57.5-36.6-75.4c-24.8-31.5-53-38.1-72.4-38.1c-27.9,0-54.2,11.6-76.2,33.6c-17.3,17.3-32.1,40.7-45.4,71.5c-20.1,46.8-36,111.1-47.4,190.9C10.2,644.7,10,775.4,10,780.9h0c0,0.2,0,0.3,0,0.5c0,20.7,16.8,37.4,37.4,37.4c20.7,0,37.4-16.8,37.4-37.4c0-0.5,0-1,0-1.5c0.1-12.6,1.1-136.5,18.6-258.3c10.5-73.2,24.7-131,42.1-171.6c9.6-22.3,29.1-59.6,52.8-59.6c1.7,0,6.2,0,13.7,9.5c8.4,10.7,16.5,28.1,24.2,52c16.2,50.5,27.5,120.8,39.5,195.3c15.3,95.3,31.2,193.8,60.1,271.8c16.7,45.2,36.2,79.2,59.6,103.8c29.4,31,64.7,46.7,104.7,46.7c34.4,0,64.5-13.7,89.5-40.8c19.5-21.2,35.9-50.6,49.9-90c24.2-67.8,39.1-157.3,54.8-252c19.8-119.5,40.3-243.2,81.1-337.2c42.4-97.8,98.4-143.6,176.1-144c0.4,0,0.8,0,1.2,0c20.7,0,37.4-16.7,37.4-37.4C990,47.4,973.3,30.6,952.6,30.6z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<x:Array x:Key="{x:Static mbts:Icons.CandidateGeometryKey}" Type="sys:String">
|
||||
<sys:String>
|
||||
m350.17 14.219c24.742 0.21484 44.621 20.449 44.402 45.191-0.21484 24.742-20.449 44.625-45.191 44.406-24.742-0.21484-44.625-20.449-44.406-45.191 0.21875-24.746 20.449-44.625 45.195-44.406
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m235.2 92.961c8.9609 28 30.238 49.84 57.68 59.922 3.3594 1.1211 6.7188 1.6797 10.078 2.8008v54.32h92.961v-54.32c3.3594-1.1211 6.7188-1.6797 10.641-2.8008 27.441-10.078 48.719-31.922 57.68-59.922l10.641-32.48c3.3594-11.199-2.8008-22.961-13.441-26.879-11.199-3.3594-22.961 2.8008-26.879 13.441l-10.641 32.48c-5.0391 15.68-16.801 28-31.922 33.602-7.2812 2.8008-14.559 3.9219-22.398 3.9219h-40.32c-7.8398 0-15.121-1.1211-22.398-3.9219-15.121-5.6016-26.879-17.922-31.922-33.602l-10.641-32.48c-3.3594-11.199-15.68-17.359-26.879-13.441-11.199 3.3594-17.359 15.68-13.441 26.879z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m465.92 271.6c3.9219 0 7.2812-3.3594 7.2812-7.2812v-25.762c0-3.9219-3.3594-7.2812-7.2812-7.2812l-231.84 0.003906c-3.9219 0-7.2812 3.3594-7.2812 7.2812v25.762c0 3.9219 3.3594 7.2812 7.2812 7.2812h30.801v60.48c0 3.9219 3.3594 7.2812 7.2812 7.2812h156.8c3.9219 0 7.2812-3.3594 7.2812-7.2812l-0.003907-60.484z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<x:Array x:Key="{x:Static mbts:Icons.CandidateWithAudienceGeometryKey}" Type="sys:String">
|
||||
<sys:String>
|
||||
m350.17 14.219c24.742 0.21484 44.621 20.449 44.402 45.191-0.21484 24.742-20.449 44.625-45.191 44.406-24.742-0.21484-44.625-20.449-44.406-45.191 0.21875-24.746 20.449-44.625 45.195-44.406
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m235.2 92.961c8.9609 28 30.238 49.84 57.68 59.922 3.3594 1.1211 6.7188 1.6797 10.078 2.8008v54.32h92.961v-54.32c3.3594-1.1211 6.7188-1.6797 10.641-2.8008 27.441-10.078 48.719-31.922 57.68-59.922l10.641-32.48c3.3594-11.199-2.8008-22.961-13.441-26.879-11.199-3.3594-22.961 2.8008-26.879 13.441l-10.641 32.48c-5.0391 15.68-16.801 28-31.922 33.602-7.2812 2.8008-14.559 3.9219-22.398 3.9219h-40.32c-7.8398 0-15.121-1.1211-22.398-3.9219-15.121-5.6016-26.879-17.922-31.922-33.602l-10.641-32.48c-3.3594-11.199-15.68-17.359-26.879-13.441-11.199 3.3594-17.359 15.68-13.441 26.879z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m210.56 468.16h-31.359c-24.641 0-45.359 17.922-48.719 42.559l-3.3594 23.52c-1.1211 6.1602 3.9219 11.762 10.078 11.762h114.24c6.1602 0 11.199-5.6016 10.078-11.762l-3.3594-23.52c-2.7969-24.078-23.516-42.559-47.598-42.559z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m239.12 411.6c0 24.43-19.809 44.238-44.242 44.238s-44.238-19.809-44.238-44.238c0-24.434 19.805-44.242 44.238-44.242s44.242 19.809 44.242 44.242
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m365.68 468.16h-31.359c-24.641 0-45.359 17.922-48.719 42.559l-2.8008 23.52c-1.1211 6.1602 3.9219 11.762 10.078 11.762h114.24c6.1602 0 11.199-5.6016 10.078-11.762l-3.3594-23.52c-2.7969-24.078-23.516-42.559-48.156-42.559z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m394.24 411.6c0 24.43-19.805 44.238-44.238 44.238s-44.238-19.809-44.238-44.238c0-24.434 19.805-44.242 44.238-44.242s44.238 19.809 44.238 44.242
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m569.52 510.72c-3.3594-24.078-24.078-42.559-48.719-42.559h-31.359c-24.641 0-45.359 17.922-48.719 42.559l-3.3594 23.52c-1.1211 6.1602 3.9219 11.762 10.078 11.762h114.24c6.1602 0 11.199-5.6016 10.078-11.762z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m549.36 411.6c0 24.43-19.805 44.238-44.238 44.238s-44.242-19.809-44.242-44.238c0-24.434 19.809-44.242 44.242-44.242s44.238 19.809 44.238 44.242
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
m465.92 271.6c3.9219 0 7.2812-3.3594 7.2812-7.2812v-25.762c0-3.9219-3.3594-7.2812-7.2812-7.2812l-231.84 0.003906c-3.9219 0-7.2812 3.3594-7.2812 7.2812v25.762c0 3.9219 3.3594 7.2812 7.2812 7.2812h30.801v60.48c0 3.9219 3.3594 7.2812 7.2812 7.2812h156.8c3.9219 0 7.2812-3.3594 7.2812-7.2812l-0.003907-60.484z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<sys:String x:Key="{x:Static mbts:Icons.WarningExclamationGeometryKey}">
|
||||
M15.998999,17.988002C16.551,17.988002 16.999,18.437009 16.999,18.990018 16.999,19.543027 16.551,19.992034 15.998999,19.992034 15.446999,19.992034 14.998999,19.543027 14.998999,18.990018 14.998999,18.437009 15.446999,17.988002 15.998999,17.988002z
|
||||
M15.998999,8.0630169C16.552,8.0630169,16.999,8.5110159,16.999,9.0650149L16.999,16.167001C16.999,16.721 16.552,17.168998 15.998999,17.168998 15.446,17.168998 14.998999,16.721 14.998999,16.167001L14.998999,9.0650149C14.998999,8.5110159,15.446,8.0630169,15.998999,8.0630169z
|
||||
@@ -400,6 +465,12 @@
|
||||
M15.999513,0C16.50052,-3.8678991E-07,17.001274,0.21826346,17.288297,0.65479128L31.74151,22.585975C32.057495,23.064982 32.084472,23.677966 31.812494,24.184989 31.541494,24.687009 31.02049,24.999999 30.450475,24.999999L1.5480773,24.999999C0.97903845,24.999999 0.45705819,24.687009 0.18703459,24.184989 -0.085979476,23.677966 -0.057963934,23.064982 0.25704309,22.585975L14.709219,0.65479128C14.997247,0.21826346,15.498506,-3.8678991E-07,15.999513,0z
|
||||
</sys:String>
|
||||
|
||||
<x:Array x:Key="{x:Static mbts:Icons.CallPutGeometryKey}" Type="sys:String">
|
||||
<sys:String>
|
||||
M 11.736 2.328 L 11.04 4.248 Q 10.296 3.888 9.516 3.672 Q 8.846 3.486 7.972 3.46 A 9.769 9.769 0 0 0 7.68 3.456 A 5.081 5.081 0 0 0 5.783 3.805 A 4.795 4.795 0 0 0 4.932 4.248 A 5.08 5.08 0 0 0 3.404 5.827 A 6.475 6.475 0 0 0 3.024 6.528 Q 2.383 7.9 2.332 9.802 A 12.382 12.382 0 0 0 2.328 10.128 A 10.047 10.047 0 0 0 2.476 11.893 A 7.423 7.423 0 0 0 3.012 13.656 A 6.163 6.163 0 0 0 3.873 15.055 A 5.142 5.142 0 0 0 4.908 16.008 A 4.729 4.729 0 0 0 7.583 16.847 A 5.734 5.734 0 0 0 7.68 16.848 A 8.092 8.092 0 0 0 8.633 16.795 Q 9.227 16.724 9.72 16.56 Q 10.584 16.272 11.4 15.816 L 12.096 17.712 A 9.015 9.015 0 0 1 11.017 18.203 A 11.495 11.495 0 0 1 10.068 18.516 Q 9.002 18.819 7.61 18.839 A 13.78 13.78 0 0 1 7.416 18.84 Q 5.232 18.84 3.552 17.796 A 6.864 6.864 0 0 1 1.182 15.289 A 8.38 8.38 0 0 1 0.936 14.82 A 9.027 9.027 0 0 1 0.192 12.488 Q 0.006 11.464 0 10.299 A 15.082 15.082 0 0 1 0 10.224 Q 0 7.656 0.948 5.676 Q 1.896 3.696 3.636 2.58 A 7.049 7.049 0 0 1 6.679 1.521 A 8.95 8.95 0 0 1 7.704 1.464 A 10.953 10.953 0 0 1 8.918 1.528 A 8.448 8.448 0 0 1 9.888 1.692 A 9.174 9.174 0 0 1 11.682 2.303 A 8.552 8.552 0 0 1 11.736 2.328 Z M 28.296 18.552 L 26.016 18.552 L 26.016 1.8 Q 27 1.68 28.188 1.62 A 48.193 48.193 0 0 1 30.331 1.561 A 53.263 53.263 0 0 1 30.648 1.56 Q 32.736 1.56 34.2 2.22 A 5.906 5.906 0 0 1 35.42 2.947 A 4.65 4.65 0 0 1 36.432 4.032 Q 37.2 5.184 37.2 6.672 A 6.062 6.062 0 0 1 37.03 8.141 A 4.624 4.624 0 0 1 36.312 9.72 A 5.355 5.355 0 0 1 34.5 11.311 A 6.736 6.736 0 0 1 33.84 11.628 A 8.346 8.346 0 0 1 31.899 12.162 A 11.327 11.327 0 0 1 30.168 12.288 A 24.966 24.966 0 0 1 29.08 12.265 A 19.903 19.903 0 0 1 28.296 12.216 L 28.296 18.552 Z M 22.728 0.6 L 15.96 20.256 L 14.184 19.656 L 20.952 0 L 22.728 0.6 Z M 28.296 3.576 L 28.296 10.272 A 11.512 11.512 0 0 0 28.608 10.306 Q 28.747 10.32 28.904 10.332 A 23.1 23.1 0 0 0 29.232 10.356 Q 29.784 10.392 30.456 10.392 Q 32.592 10.392 33.768 9.432 A 3.095 3.095 0 0 0 34.933 7.166 A 4.157 4.157 0 0 0 34.944 6.864 A 3.468 3.468 0 0 0 34.762 5.714 A 2.913 2.913 0 0 0 33.816 4.392 A 3.711 3.711 0 0 0 32.597 3.753 Q 31.742 3.48 30.6 3.48 Q 29.952 3.48 29.376 3.504 Q 28.8 3.528 28.296 3.576 Z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<!--
|
||||
'y' for equilateral triangle of side length x:
|
||||
(x/2)^2 + Y^2 = x^2
|
||||
@@ -422,9 +493,9 @@
|
||||
M0,7.79422863406 L3,12.9903810568 6,7.79422863406 Z
|
||||
</sys:String>
|
||||
|
||||
<Geometry x:Key="arrow_geometry">
|
||||
<sys:String x:Key="{x:Static mbts:Icons.ArrowGeometryKey}">
|
||||
F1 M 78.1398,324.18L 52.5865,298.626C 51.6892,297.728 50.3385,297.459 49.1678,297.946C 47.9971,298.43 47.2305,299.574 47.2305,300.843L 60.6385,326.398L 47.2305,351.952C 47.2305,353.223 47.9971,354.364 49.1678,354.848C 49.5558,355.011 49.9638,355.09 50.3678,355.09C 51.1838,355.09 51.9852,354.77 52.5865,354.171L 78.1398,328.616C 78.7238,328.032 79.0598,327.224 79.0598,326.398C 79.0598,325.572 78.7238,324.764 78.1398,324.18 Z
|
||||
</Geometry>
|
||||
</sys:String>
|
||||
|
||||
<DrawingImage x:Key="Bookmarks">
|
||||
<DrawingImage.Drawing>
|
||||
@@ -1133,7 +1204,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing Geometry="{StaticResource arrow_geometry}">
|
||||
<GeometryDrawing Geometry="{Binding Source={StaticResource {x:Static mbts:Icons.ArrowGeometryKey}}}">
|
||||
<GeometryDrawing.Pen>
|
||||
<Pen Thickness="3" Brush="Black"/>
|
||||
</GeometryDrawing.Pen>
|
||||
@@ -1147,7 +1218,7 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing Geometry="{StaticResource arrow_geometry}">
|
||||
<GeometryDrawing Geometry="{Binding Source={StaticResource {x:Static mbts:Icons.ArrowGeometryKey}}}">
|
||||
<GeometryDrawing.Pen>
|
||||
<Pen Thickness="3" Brush="Black"/>
|
||||
</GeometryDrawing.Pen>
|
||||
|
||||
+37
-35
@@ -1,35 +1,37 @@
|
||||
using bts.api.core;
|
||||
using bts.api.md.auxiliary;
|
||||
|
||||
using mbts.utils;
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using System.Reactive.Concurrency;
|
||||
|
||||
namespace mbts.spark {
|
||||
[Export]
|
||||
class MarketDataAuxiliaryModel : IDisposable {
|
||||
readonly MarketDataAuxiliaryService _service;
|
||||
readonly LegacyTableUpdateDemuxer<VolumeAtPriceLevelImmutable, int, uint> _security_orders_demuxer;
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
public MarketDataAuxiliaryModel(MarketDataAuxiliaryService service) {
|
||||
_service = service;
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
volume_at_price = service.subscribei_volume_at_price().Naggle(interval, scheduler).Table().Cache();
|
||||
_security_orders_demuxer = new LegacyTableUpdateDemuxer<VolumeAtPriceLevelImmutable, int, uint>(o => o.RecordId, v => v.SecurityId);
|
||||
_dispose = volume_at_price.AddRemoveOnly().Subscribe(_security_orders_demuxer);
|
||||
}
|
||||
|
||||
public IObservable<TableUpdate<VolumeAtPriceLevelImmutable, int>> volume_at_price { get; }
|
||||
|
||||
public IObservable<ITableUpdateTransform<VolumeAtPriceLevelImmutable, int>> security_volume_at_price(uint security) =>
|
||||
_security_orders_demuxer.subscribe(security);
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
namespace mbts.spark {
|
||||
using bts.api.core;
|
||||
using bts.api.md.auxiliary;
|
||||
|
||||
using mbts.utils;
|
||||
|
||||
using Spark.Core.Reactive;
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
using System.Reactive.Concurrency;
|
||||
|
||||
[Export]
|
||||
class MarketDataAuxiliaryModel : IDisposable {
|
||||
readonly MarketDataAuxiliaryService _service;
|
||||
readonly TableUpdateDemuxer<VolumeAtPriceLevelImmutable, int, uint> _security_orders_demuxer;
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
public MarketDataAuxiliaryModel(MarketDataAuxiliaryService service) {
|
||||
_service = service;
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
volume_at_price = service.subscribei_volume_at_price().Naggle(interval, scheduler).Table().Cache();
|
||||
_security_orders_demuxer = new(o => o.RecordId, v => v.SecurityId);
|
||||
_dispose = volume_at_price.Subscribe(_security_orders_demuxer);
|
||||
}
|
||||
|
||||
public IObservable<TableUpdate<VolumeAtPriceLevelImmutable, int>> volume_at_price { get; }
|
||||
|
||||
public IObservable<TableUpdate<VolumeAtPriceLevelImmutable, int>> security_volume_at_price(uint security) =>
|
||||
_security_orders_demuxer.subscribe(security);
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
+31
-31
@@ -1,31 +1,31 @@
|
||||
using bts.utils;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
[ProtoContract]
|
||||
class GlobalAlertSettings : IEquatable<GlobalAlertSettings> {
|
||||
[ProtoMember(1)]
|
||||
TradeAlert[] _trade_alerts;
|
||||
|
||||
public GlobalAlertSettings() { }
|
||||
public GlobalAlertSettings(IEnumerable<TradeAlert> trade_alerts) =>
|
||||
_trade_alerts = trade_alerts.ToArray();
|
||||
|
||||
public IReadOnlyList<TradeAlert> trade_alerts => _trade_alerts ?? Array.Empty<TradeAlert>();
|
||||
|
||||
public bool Equals(GlobalAlertSettings other) =>
|
||||
other != null &&
|
||||
trade_alerts.SequenceEqual(other.trade_alerts);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GlobalAlertSettings);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.auto_phase(trade_alerts);
|
||||
}
|
||||
}
|
||||
using bts.utils;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
[ProtoContract]
|
||||
class GlobalAlertSettings : IEquatable<GlobalAlertSettings> {
|
||||
[ProtoMember(1)]
|
||||
TradeAlert[] _trade_alerts;
|
||||
|
||||
public GlobalAlertSettings() { }
|
||||
public GlobalAlertSettings(IEnumerable<TradeAlert> trade_alerts) =>
|
||||
_trade_alerts = trade_alerts.ToArray();
|
||||
|
||||
public IReadOnlyList<TradeAlert> trade_alerts => _trade_alerts ?? Array.Empty<TradeAlert>();
|
||||
|
||||
public bool Equals(GlobalAlertSettings other) =>
|
||||
other != null &&
|
||||
trade_alerts.SequenceEqual(other.trade_alerts);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GlobalAlertSettings);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.auto_phase(trade_alerts);
|
||||
}
|
||||
}
|
||||
+88
-88
@@ -1,88 +1,88 @@
|
||||
using bts;
|
||||
|
||||
using mbts.ui.shell;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.ui.settings {
|
||||
public interface ISavedWindowFactory {
|
||||
Task<IShellWindow> restore(WindowState window);
|
||||
}
|
||||
|
||||
[Export(typeof(ISavedWindowFactory))]
|
||||
public class WindowFactory : ISavedWindowFactory {
|
||||
IShellWindowMigrationRule[] _compat;
|
||||
|
||||
[ImportingConstructor]
|
||||
public WindowFactory([ImportMany] IShellWindowMigrationRule[] migration) {
|
||||
_compat = migration;
|
||||
}
|
||||
|
||||
[Import]
|
||||
public ObservableCollection<IShellModule> modules { get; set; }
|
||||
|
||||
public Task<IShellWindow> restore(WindowState window) {
|
||||
Log.Ui.info("Loading window '{0}'".fmt(window.title));
|
||||
bool changed;
|
||||
int count = 0;
|
||||
do {
|
||||
count++;
|
||||
changed = apply_compatability_migrations(ref window);
|
||||
} while (count < 100 && changed);
|
||||
|
||||
if (count == 100) {
|
||||
Log.Persist.error("Error: Window state migration still changing after 100 iterations. Please check IShellWindowMigrationRule logic.");
|
||||
}
|
||||
|
||||
return restore(window.owner, window.app_data, window.position);
|
||||
}
|
||||
|
||||
/// When we apply the migration changes to a window we stop as soon as one "matches" and updates the state
|
||||
/// Since it's possible that we have multiple levels of migration we run this function until the data stops changing (or 100 iterations)
|
||||
/// ex: If change the data format twice from A to B to C then we would write 2 IShellWindowMigration instances.
|
||||
/// Starting in state A one of the _compat tests changes to state B. Now we need to re-run
|
||||
/// the migrations because state B will match a different rule and be converted to state C.
|
||||
private bool apply_compatability_migrations(ref WindowState window) {
|
||||
foreach (var test in _compat) {
|
||||
try {
|
||||
if (test.apply(ref window)) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.Persist.error("Exception running compat rule {0}.\n{1}".fmt(test.GetType().Name, e.ToString()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<IShellWindow> restore(string owner_name, byte[] state, WindowPosition windowPosition) {
|
||||
IShellModule owner = modules.empty_if_null().SingleOrDefault(m => m.key() == owner_name);
|
||||
if (owner == null) {
|
||||
Log.Ui.warn("Could not locate module '{0}'".fmt(owner_name));
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Task<IShellWindow> task = owner.create(new MemoryStream(state));
|
||||
if (task != null) {
|
||||
IShellWindow window = await task;
|
||||
if (window != null) {
|
||||
window.set_start_position(windowPosition);
|
||||
Log.Ui.info($"Restoring window {owner_name} - {window.diagnostic_name()}.");
|
||||
}
|
||||
return window;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.Ui.error("Failure to re-create window for module {0}. Error: {1}".fmt(owner_name, e.Message));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
using bts;
|
||||
|
||||
using mbts.ui.shell;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.ui.settings {
|
||||
public interface ISavedWindowFactory {
|
||||
Task<IShellWindow> restore(WindowState window);
|
||||
}
|
||||
|
||||
[Export(typeof(ISavedWindowFactory))]
|
||||
public class WindowFactory : ISavedWindowFactory {
|
||||
IShellWindowMigrationRule[] _compat;
|
||||
|
||||
[ImportingConstructor]
|
||||
public WindowFactory([ImportMany] IShellWindowMigrationRule[] migration) {
|
||||
_compat = migration;
|
||||
}
|
||||
|
||||
[Import]
|
||||
public ObservableCollection<IShellModule> modules { get; set; }
|
||||
|
||||
public Task<IShellWindow> restore(WindowState window) {
|
||||
Log.Ui.info("Loading window '{0}'".fmt(window.title));
|
||||
bool changed;
|
||||
int count = 0;
|
||||
do {
|
||||
count++;
|
||||
changed = apply_compatability_migrations(ref window);
|
||||
} while (count < 100 && changed);
|
||||
|
||||
if (count == 100) {
|
||||
Log.Persist.error("Error: Window state migration still changing after 100 iterations. Please check IShellWindowMigrationRule logic.");
|
||||
}
|
||||
|
||||
return restore(window.owner, window.app_data, window.position);
|
||||
}
|
||||
|
||||
/// When we apply the migration changes to a window we stop as soon as one "matches" and updates the state
|
||||
/// Since it's possible that we have multiple levels of migration we run this function until the data stops changing (or 100 iterations)
|
||||
/// ex: If change the data format twice from A to B to C then we would write 2 IShellWindowMigration instances.
|
||||
/// Starting in state A one of the _compat tests changes to state B. Now we need to re-run
|
||||
/// the migrations because state B will match a different rule and be converted to state C.
|
||||
private bool apply_compatability_migrations(ref WindowState window) {
|
||||
foreach (var test in _compat) {
|
||||
try {
|
||||
if (test.apply(ref window)) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.Persist.error("Exception running compat rule {0}.\n{1}".fmt(test.GetType().Name, e.ToString()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<IShellWindow> restore(string owner_name, byte[] state, WindowPosition windowPosition) {
|
||||
IShellModule owner = modules.empty_if_null().SingleOrDefault(m => m.key() == owner_name);
|
||||
if (owner == null) {
|
||||
Log.Ui.warn("Could not locate module '{0}'".fmt(owner_name));
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Task<IShellWindow> task = owner.create(new MemoryStream(state));
|
||||
if (task != null) {
|
||||
IShellWindow window = await task;
|
||||
if (window != null) {
|
||||
window.set_start_position(windowPosition);
|
||||
Log.Ui.info($"Restoring window {owner_name} - {window.diagnostic_name()}.");
|
||||
}
|
||||
return window;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.Ui.error("Failure to re-create window for module {0}. Error: {1}".fmt(owner_name, e.Message));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
-86
@@ -1,86 +1,113 @@
|
||||
using bts;
|
||||
|
||||
using mbts.ui.shell;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.ui.settings {
|
||||
public interface ISettingsCollector {
|
||||
// Get the save state for every open window
|
||||
IEnumerable<WindowState> get_window_state();
|
||||
WindowState get_window_state(IShellWindow window);
|
||||
IEnumerable<ModuleState> get_module_state();
|
||||
WindowPosition get_launcher_state();
|
||||
}
|
||||
|
||||
[Export(typeof(ISettingsCollector))]
|
||||
class SettingsCollector : ISettingsCollector {
|
||||
IWindowsInterop _interop;
|
||||
IWindowManager _wm;
|
||||
ILauncher _launcher;
|
||||
|
||||
[ImportingConstructor]
|
||||
public SettingsCollector(IWindowManager wm, IWindowsInterop interop, ILauncher launcher) {
|
||||
_wm = wm;
|
||||
_interop = interop;
|
||||
_launcher = launcher;
|
||||
}
|
||||
|
||||
[Import]
|
||||
public ObservableCollection<IShellModule> modules { get; set; }
|
||||
|
||||
public IEnumerable<WindowState> get_window_state() {
|
||||
foreach (var window in _wm.list_windows()) {
|
||||
// Check that the window is still open since we might be iterating asyncronously
|
||||
if (_wm.contains(window)) {
|
||||
var state = get_window_state(window);
|
||||
if (state != null) {
|
||||
yield return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ModuleState> get_module_state() {
|
||||
//Note: I'm assuming that the list of modules is stable and I don't have to protect against it changing here
|
||||
foreach (var module in modules.OfTypeSafe<IPersistShellModule>()) {
|
||||
MemoryStream out_stream = new MemoryStream();
|
||||
module.get_state(out_stream);
|
||||
yield return new ModuleState() {
|
||||
name = module.key(),
|
||||
app_data = out_stream.ToArray(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public WindowPosition get_launcher_state() {
|
||||
return _launcher.get_position();
|
||||
}
|
||||
|
||||
public WindowState get_window_state(IShellWindow window) {
|
||||
try {
|
||||
if (window == null || window.Owner == null) return null;
|
||||
MemoryStream out_stream = new MemoryStream();
|
||||
window.GetState(out_stream);
|
||||
|
||||
var pos = _interop.GetPosition(window);
|
||||
var state = new WindowState() {
|
||||
owner = window.Owner.key(),
|
||||
app_data = out_stream.ToArray(),
|
||||
position = pos,
|
||||
title = string.IsNullOrEmpty(pos.custom_title) ? window.Title : pos.custom_title
|
||||
};
|
||||
return state;
|
||||
} catch (Exception e) {
|
||||
Log.Ui.error("Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
using bts;
|
||||
|
||||
using mbts.ui.shell;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace mbts.ui.settings {
|
||||
public interface ISettingsCollector {
|
||||
// Get the save state for every open window
|
||||
Task<List<WindowState>> get_window_state();
|
||||
Task<WindowState> get_window_state(IShellWindow window);
|
||||
IEnumerable<ModuleState> get_module_state();
|
||||
WindowPosition get_launcher_state();
|
||||
}
|
||||
|
||||
[Export(typeof(ISettingsCollector))]
|
||||
class SettingsCollector : ISettingsCollector {
|
||||
WindowManager _wm;
|
||||
ILauncher _launcher;
|
||||
|
||||
[ImportingConstructor]
|
||||
public SettingsCollector(WindowManager wm, ILauncher launcher) {
|
||||
_wm = wm;
|
||||
_launcher = launcher;
|
||||
}
|
||||
|
||||
[Import]
|
||||
public ObservableCollection<IShellModule> modules { get; set; }
|
||||
|
||||
[Import]
|
||||
public IShellThreadFactory thread_factory { get; set; }
|
||||
|
||||
IWindowsInterop _interop => _wm;
|
||||
|
||||
public async Task<List<WindowState>> get_window_state() {
|
||||
var windows = _wm.list_windows();
|
||||
var rv = new List<WindowState>(windows.Count);
|
||||
foreach (var window in windows) {
|
||||
// Check that the window is still open since we might be iterating asyncronously
|
||||
if (_wm.contains(window)) {
|
||||
var state = await get_window_state(window);
|
||||
if (state != null) {
|
||||
rv.Add(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public IEnumerable<ModuleState> get_module_state() {
|
||||
//Note: I'm assuming that the list of modules is stable and I don't have to protect against it changing here
|
||||
foreach (var module in modules.OfTypeSafe<IPersistShellModule>()) {
|
||||
MemoryStream out_stream = new MemoryStream();
|
||||
module.get_state(out_stream);
|
||||
yield return new ModuleState() {
|
||||
name = module.key(),
|
||||
app_data = out_stream.ToArray(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public WindowPosition get_launcher_state() {
|
||||
return _launcher.get_position();
|
||||
}
|
||||
|
||||
public async Task<WindowState> get_window_state(IShellWindow window) {
|
||||
try {
|
||||
if (window == null || window.Owner == null) return null;
|
||||
Dispatcher dispatcher = null;
|
||||
if (window is ISlowWindow) {
|
||||
var tf = thread_factory;
|
||||
if (tf is not null) {
|
||||
var thread_id = (window as ISharedWindow)?.thread_id;
|
||||
dispatcher = tf.get_dispatcher(thread_id);
|
||||
if (dispatcher is null) {
|
||||
Log.Gen.error(
|
||||
$"Unable to get window state for '{window.Title}.' " +
|
||||
$"Didn't find dispatcher for thread {thread_id}.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pos = _interop.GetPosition(window);
|
||||
var state = new WindowState() {
|
||||
owner = window.Owner.key(),
|
||||
app_data = dispatcher is null ?
|
||||
(await dispatcher.InvokeAsync(serialize)) : serialize(),
|
||||
position = pos,
|
||||
title = string.IsNullOrEmpty(pos.custom_title) ? window.Title : pos.custom_title
|
||||
};
|
||||
return state;
|
||||
|
||||
byte[] serialize() {
|
||||
using var ms = new MemoryStream();
|
||||
window.GetState(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.Ui.error("Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+95
-94
@@ -1,94 +1,95 @@
|
||||
using bts.shell.pnl;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.ui.settings;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.settings {
|
||||
interface ISettingsMigrationRule<T> {
|
||||
bool apply(T settings, out T new_settings);
|
||||
}
|
||||
|
||||
// Migration rules of type ISettingsMigrationRule<T>, where 'T' is a common type, such as GridSettings,
|
||||
// should be exposed through explicitly named interfaces that inherit ISettingsMigrationRule<T>.
|
||||
// This avoids magic const names in the MEF import/export contracts, which would otherwise be needed to
|
||||
// determine the various windows to which such rules apply.
|
||||
interface IFillsGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
interface IMarketViewGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
interface IPAndLGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
interface IPNLSettingsMigrationRule : ISettingsMigrationRule<PNLSettings> { }
|
||||
interface ISyntheticManagementMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
|
||||
static class SettingsMigrationHelper {
|
||||
public static bool apply<T>(T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, out T new_settings) {
|
||||
bool applied = false;
|
||||
foreach (var r in migration_rules) {
|
||||
if (r.apply(settings, out var migrated)) {
|
||||
settings = migrated;
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
new_settings = applied ? settings : default;
|
||||
return applied;
|
||||
}
|
||||
|
||||
public static void apply_quiet<T>(ref T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules) {
|
||||
if (apply(settings, migration_rules, out var new_settings))
|
||||
settings = new_settings;
|
||||
}
|
||||
|
||||
public static ISettingsMigrationRule<T> merge<T>(this IEnumerable<ISettingsMigrationRule<T>> rules) =>
|
||||
new MergeWrapper<T>(rules);
|
||||
|
||||
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, IImmutableProperty<TOuter, TInner> propi) =>
|
||||
new SelectorWrapper<TInner, TOuter>(rule, propi.get, propi.set);
|
||||
|
||||
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) =>
|
||||
new SelectorWrapper<TInner, TOuter>(rule, get, set);
|
||||
|
||||
class MergeWrapper<T> : ISettingsMigrationRule<T> {
|
||||
readonly IEnumerable<ISettingsMigrationRule<T>> _rules;
|
||||
|
||||
public MergeWrapper(IEnumerable<ISettingsMigrationRule<T>> rules) =>
|
||||
_rules = rules;
|
||||
|
||||
public bool apply(T settings, out T new_settings) {
|
||||
bool applied = false;
|
||||
foreach (var r in _rules) {
|
||||
if (r.apply(settings, out var migrated)) {
|
||||
settings = migrated;
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
new_settings = applied ? settings : default;
|
||||
return applied;
|
||||
}
|
||||
}
|
||||
|
||||
class SelectorWrapper<TInner, TOuter> : ISettingsMigrationRule<TOuter> {
|
||||
readonly ISettingsMigrationRule<TInner> _rule;
|
||||
readonly Func<TOuter, TInner> _get;
|
||||
readonly Func<TOuter, TInner, TOuter> _set;
|
||||
|
||||
public SelectorWrapper(ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) {
|
||||
_rule = rule;
|
||||
_get = get;
|
||||
_set = set;
|
||||
}
|
||||
|
||||
public bool apply(TOuter settings, out TOuter new_settings) {
|
||||
if (settings != null) {
|
||||
var ival = _get(settings);
|
||||
if (_rule.apply(ival, out var new_ival)) {
|
||||
new_settings = _set(settings, new_ival);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
new_settings = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using bts.shell.pnl;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.ui.settings;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.settings {
|
||||
interface ISettingsMigrationRule<T> {
|
||||
bool apply(T settings, out T new_settings);
|
||||
}
|
||||
|
||||
// Migration rules of type ISettingsMigrationRule<T>, where 'T' is a common type, such as GridSettings,
|
||||
// should be exposed through explicitly named interfaces that inherit ISettingsMigrationRule<T>.
|
||||
// This avoids magic const names in the MEF import/export contracts, which would otherwise be needed to
|
||||
// determine the various windows to which such rules apply.
|
||||
interface IFillsGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
interface IMarketViewGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
interface IPAndLGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
interface IPNLSettingsMigrationRule : ISettingsMigrationRule<PNLSettings> { }
|
||||
interface ISyntheticManagementMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
interface IReconcileReportMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
|
||||
static class SettingsMigrationHelper {
|
||||
public static bool apply<T>(T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, out T new_settings) {
|
||||
bool applied = false;
|
||||
foreach (var r in migration_rules) {
|
||||
if (r.apply(settings, out var migrated)) {
|
||||
settings = migrated;
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
new_settings = applied ? settings : default;
|
||||
return applied;
|
||||
}
|
||||
|
||||
public static void apply_quiet<T>(ref T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules) {
|
||||
if (apply(settings, migration_rules, out var new_settings))
|
||||
settings = new_settings;
|
||||
}
|
||||
|
||||
public static ISettingsMigrationRule<T> merge<T>(this IEnumerable<ISettingsMigrationRule<T>> rules) =>
|
||||
new MergeWrapper<T>(rules);
|
||||
|
||||
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, IImmutableProperty<TOuter, TInner> propi) =>
|
||||
new SelectorWrapper<TInner, TOuter>(rule, propi.get, propi.set);
|
||||
|
||||
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) =>
|
||||
new SelectorWrapper<TInner, TOuter>(rule, get, set);
|
||||
|
||||
class MergeWrapper<T> : ISettingsMigrationRule<T> {
|
||||
readonly IEnumerable<ISettingsMigrationRule<T>> _rules;
|
||||
|
||||
public MergeWrapper(IEnumerable<ISettingsMigrationRule<T>> rules) =>
|
||||
_rules = rules;
|
||||
|
||||
public bool apply(T settings, out T new_settings) {
|
||||
bool applied = false;
|
||||
foreach (var r in _rules) {
|
||||
if (r.apply(settings, out var migrated)) {
|
||||
settings = migrated;
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
new_settings = applied ? settings : default;
|
||||
return applied;
|
||||
}
|
||||
}
|
||||
|
||||
class SelectorWrapper<TInner, TOuter> : ISettingsMigrationRule<TOuter> {
|
||||
readonly ISettingsMigrationRule<TInner> _rule;
|
||||
readonly Func<TOuter, TInner> _get;
|
||||
readonly Func<TOuter, TInner, TOuter> _set;
|
||||
|
||||
public SelectorWrapper(ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) {
|
||||
_rule = rule;
|
||||
_get = get;
|
||||
_set = set;
|
||||
}
|
||||
|
||||
public bool apply(TOuter settings, out TOuter new_settings) {
|
||||
if (settings != null) {
|
||||
var ival = _get(settings);
|
||||
if (_rule.apply(ival, out var new_ival)) {
|
||||
new_settings = _set(settings, new_ival);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
new_settings = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
+153
-171
@@ -1,171 +1,153 @@
|
||||
using mbts.spark;
|
||||
using mbts.utils;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.persist {
|
||||
interface IWindowSettingsManager<T> where T : class {
|
||||
T instance_settings { get; set; }
|
||||
T default_settings { get; }
|
||||
event Action<T> instance_settings_changed;
|
||||
IObservable<T> subscribe_default();
|
||||
Task save_default_settings(T value);
|
||||
IWindowSettingsManagerNonShared<T> as_non_shared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An implementation of IWindowSettingsManager that uses a clone of the actual default settings;
|
||||
/// provides a convenient way to avoid problems that arise from sharing a single default settings object among multiple window instances.
|
||||
/// </summary>
|
||||
interface IWindowSettingsManagerNonShared<T> : IWindowSettingsManager<T>, IDisposable where T : class { }
|
||||
|
||||
class WindowSettingsManager<T> : IWindowSettingsManager<T> where T : class {
|
||||
readonly UserSettingsHub _hub;
|
||||
readonly Func<UserSettings, T> _get;
|
||||
readonly Func<UserSettings, T, UserSettings> _set;
|
||||
T _instance_settings;
|
||||
|
||||
public WindowSettingsManager(UserSettingsHub hub, T instance_settings, Func<UserSettings, T> get, Func<UserSettings, T, UserSettings> set) {
|
||||
_hub = hub;
|
||||
_instance_settings = instance_settings;
|
||||
_get = get;
|
||||
_set = set;
|
||||
}
|
||||
|
||||
public T instance_settings {
|
||||
get => _instance_settings;
|
||||
set {
|
||||
_instance_settings = value;
|
||||
instance_settings_changed?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
public T default_settings => _get(_hub.current);
|
||||
|
||||
public event Action<T> instance_settings_changed;
|
||||
|
||||
public async Task save_default_settings(T value) =>
|
||||
await _hub.save_default(_set(_hub.current, value));
|
||||
|
||||
public IObservable<T> subscribe_default() =>
|
||||
_hub.subscribe_default_settings().Select(_get).DistinctUntilChanged();
|
||||
|
||||
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
|
||||
new NonSharedWrapper(this);
|
||||
|
||||
class NonSharedWrapper : IWindowSettingsManagerNonShared<T> {
|
||||
readonly IWindowSettingsManager<T> _wsm;
|
||||
readonly BehaviorSubject<T> _default_subject;
|
||||
IDisposable _dispose;
|
||||
|
||||
public NonSharedWrapper(IWindowSettingsManager<T> wsm) {
|
||||
_wsm = wsm;
|
||||
_default_subject = new BehaviorSubject<T>(_wsm.default_settings);
|
||||
_dispose = wsm.subscribe_default().Subscribe(s =>
|
||||
_default_subject.OnNext(default_settings = MiscUtils.clone_proto(s)));
|
||||
}
|
||||
|
||||
public T instance_settings {
|
||||
get => _wsm.instance_settings;
|
||||
set => _wsm.instance_settings = value;
|
||||
}
|
||||
public T default_settings { get; private set; }
|
||||
public event Action<T> instance_settings_changed {
|
||||
add => _wsm.instance_settings_changed += value;
|
||||
remove => _wsm.instance_settings_changed -= value;
|
||||
}
|
||||
public async Task save_default_settings(T value) =>
|
||||
await _wsm.save_default_settings(value);
|
||||
|
||||
public IObservable<T> subscribe_default() =>
|
||||
_default_subject.AsObservable();
|
||||
|
||||
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
|
||||
new NonSharedWrapper(_wsm);
|
||||
|
||||
public void Dispose() => Disposable.dispose(ref _dispose);
|
||||
}
|
||||
|
||||
public class DisconnectedImpl : IWindowSettingsManager<T> {
|
||||
readonly BehaviorSubject<T> _default_settings_subject;
|
||||
T _instance_settings, _default_settings;
|
||||
|
||||
public DisconnectedImpl(T settings) : this(settings, settings) { }
|
||||
public DisconnectedImpl(T instance_settings, T default_settings) {
|
||||
_instance_settings = instance_settings;
|
||||
_default_settings = default_settings;
|
||||
_default_settings_subject = new BehaviorSubject<T>(default_settings);
|
||||
}
|
||||
|
||||
public T instance_settings {
|
||||
get => _instance_settings;
|
||||
set {
|
||||
if (!Equals(_instance_settings, value)) {
|
||||
_instance_settings = value;
|
||||
instance_settings_changed?.Invoke(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
public T default_settings {
|
||||
get => _default_settings;
|
||||
set {
|
||||
if (!Equals(_default_settings, _default_settings)) {
|
||||
_default_settings = value;
|
||||
_default_settings_subject.OnNext(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<T> instance_settings_changed;
|
||||
|
||||
public IWindowSettingsManagerNonShared<T> as_non_shared() => new NonSharedWrapper(this);
|
||||
public Task save_default_settings(T value) {
|
||||
default_settings = value;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public IObservable<T> subscribe_default() => _default_settings_subject;
|
||||
}
|
||||
}
|
||||
|
||||
static class WindowSettingsManagerExtensions {
|
||||
/// <summary>
|
||||
/// Returns an observable sequence of the settings object used by a particular window.
|
||||
/// <para/>Note: 'Instance' settings take priority over 'default' settings. While the former is present, any changes to the latter will be ignored.
|
||||
/// </summary>
|
||||
public static IObservable<T> subscribe_current<T>(this IWindowSettingsManager<T> wsm) where T : class =>
|
||||
Observable.CombineLatest(
|
||||
Observable.FromEvent<Action<T>, T>(cb => wsm.instance_settings_changed += cb, cb => wsm.instance_settings_changed -= cb)
|
||||
.StartWith(wsm.instance_settings),
|
||||
wsm.subscribe_default(),
|
||||
(i, d) => i ?? d
|
||||
).Where(v => v != null)
|
||||
.DistinctUntilChanged();
|
||||
|
||||
public static IObservable<T> subscribe_current<T>(this IWindowSettingsManager<T> wsm, Func<(T defaults, T instance), T> merge_settings) where T : class, IDefaultableSettings {
|
||||
return Observable.Create<T>(o => {
|
||||
IDisposable defaults_sub = wsm.subscribe_default().Subscribe(_ => o.OnNext(get_current()));
|
||||
wsm.instance_settings_changed += on_instance_settings_changed;
|
||||
return Disposable.Create(() => {
|
||||
defaults_sub.Dispose();
|
||||
wsm.instance_settings_changed -= on_instance_settings_changed;
|
||||
});
|
||||
void on_instance_settings_changed(T _) =>
|
||||
o.OnNext(get_current());
|
||||
});
|
||||
T get_current() {
|
||||
var inst = wsm.instance_settings;
|
||||
if (inst == null) return wsm.default_settings;
|
||||
if (inst.use_defaults) return merge_settings((wsm.default_settings, inst));
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
|
||||
public static T current_settings<T>(this IWindowSettingsManager<T> wsm) where T : class =>
|
||||
wsm.instance_settings ?? wsm.default_settings;
|
||||
}
|
||||
}
|
||||
using mbts.spark;
|
||||
using mbts.utils;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.persist {
|
||||
interface IWindowSettingsManager<T> where T : class {
|
||||
T instance_settings { get; set; }
|
||||
T default_settings { get; }
|
||||
event Action<T> instance_settings_changed;
|
||||
IObservable<T> subscribe_default();
|
||||
Task save_default_settings(T value);
|
||||
IWindowSettingsManagerNonShared<T> as_non_shared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An implementation of IWindowSettingsManager that uses a clone of the actual default settings;
|
||||
/// provides a convenient way to avoid problems that arise from sharing a single default settings object among multiple window instances.
|
||||
/// </summary>
|
||||
interface IWindowSettingsManagerNonShared<T> : IWindowSettingsManager<T>, IDisposable where T : class { }
|
||||
|
||||
class WindowSettingsManager<T> : IWindowSettingsManager<T> where T : class {
|
||||
readonly UserSettingsHub _hub;
|
||||
readonly Func<UserSettings, T> _get;
|
||||
readonly Func<UserSettings, T, UserSettings> _set;
|
||||
T _instance_settings;
|
||||
|
||||
public WindowSettingsManager(UserSettingsHub hub, T instance_settings, Func<UserSettings, T> get, Func<UserSettings, T, UserSettings> set) {
|
||||
_hub = hub;
|
||||
_instance_settings = instance_settings;
|
||||
_get = get;
|
||||
_set = set;
|
||||
hub.request_clear_instance_settings += () => instance_settings = null;
|
||||
}
|
||||
|
||||
public T instance_settings {
|
||||
get => _instance_settings;
|
||||
set {
|
||||
_instance_settings = value;
|
||||
instance_settings_changed?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
public T default_settings => _get(_hub.current);
|
||||
|
||||
public event Action<T> instance_settings_changed;
|
||||
|
||||
public async Task save_default_settings(T value) =>
|
||||
await _hub.save_default(_set(_hub.current, value));
|
||||
|
||||
public IObservable<T> subscribe_default() =>
|
||||
_hub.subscribe_default_settings().Select(_get).DistinctUntilChanged();
|
||||
|
||||
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
|
||||
new NonSharedWrapper(this);
|
||||
|
||||
class NonSharedWrapper : IWindowSettingsManagerNonShared<T> {
|
||||
readonly IWindowSettingsManager<T> _wsm;
|
||||
readonly BehaviorSubject<T> _default_subject;
|
||||
IDisposable _dispose;
|
||||
|
||||
public NonSharedWrapper(IWindowSettingsManager<T> wsm) {
|
||||
_wsm = wsm;
|
||||
_default_subject = new BehaviorSubject<T>(_wsm.default_settings);
|
||||
_dispose = wsm.subscribe_default().Subscribe(s =>
|
||||
_default_subject.OnNext(default_settings = MiscUtils.clone_proto(s)));
|
||||
}
|
||||
|
||||
public T instance_settings {
|
||||
get => _wsm.instance_settings;
|
||||
set => _wsm.instance_settings = value;
|
||||
}
|
||||
public T default_settings { get; private set; }
|
||||
public event Action<T> instance_settings_changed {
|
||||
add => _wsm.instance_settings_changed += value;
|
||||
remove => _wsm.instance_settings_changed -= value;
|
||||
}
|
||||
public async Task save_default_settings(T value) =>
|
||||
await _wsm.save_default_settings(value);
|
||||
|
||||
public IObservable<T> subscribe_default() =>
|
||||
_default_subject.AsObservable();
|
||||
|
||||
public IWindowSettingsManagerNonShared<T> as_non_shared() =>
|
||||
new NonSharedWrapper(_wsm);
|
||||
|
||||
public void Dispose() => Disposable.dispose(ref _dispose);
|
||||
}
|
||||
|
||||
public class DisconnectedImpl : IWindowSettingsManager<T> {
|
||||
readonly BehaviorSubject<T> _default_settings_subject;
|
||||
T _instance_settings, _default_settings;
|
||||
|
||||
public DisconnectedImpl(T settings) : this(settings, settings) { }
|
||||
public DisconnectedImpl(T instance_settings, T default_settings) {
|
||||
_instance_settings = instance_settings;
|
||||
_default_settings = default_settings;
|
||||
_default_settings_subject = new BehaviorSubject<T>(default_settings);
|
||||
}
|
||||
|
||||
public T instance_settings {
|
||||
get => _instance_settings;
|
||||
set {
|
||||
if (!Equals(_instance_settings, value)) {
|
||||
_instance_settings = value;
|
||||
instance_settings_changed?.Invoke(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
public T default_settings {
|
||||
get => _default_settings;
|
||||
set {
|
||||
if (!Equals(_default_settings, _default_settings)) {
|
||||
_default_settings = value;
|
||||
_default_settings_subject.OnNext(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<T> instance_settings_changed;
|
||||
|
||||
public IWindowSettingsManagerNonShared<T> as_non_shared() => new NonSharedWrapper(this);
|
||||
public Task save_default_settings(T value) {
|
||||
default_settings = value;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public IObservable<T> subscribe_default() => _default_settings_subject;
|
||||
}
|
||||
}
|
||||
|
||||
static class WindowSettingsManagerExtensions {
|
||||
/// <summary>
|
||||
/// Returns an observable sequence of the settings object used by a particular window.
|
||||
/// <para/>Note: 'Instance' settings take priority over 'default' settings. While the former is present, any changes to the latter will be ignored.
|
||||
/// </summary>
|
||||
public static IObservable<T> subscribe_current<T>(this IWindowSettingsManager<T> wsm) where T : class =>
|
||||
Observable.CombineLatest(
|
||||
Observable.FromEvent<Action<T>, T>(cb => wsm.instance_settings_changed += cb, cb => wsm.instance_settings_changed -= cb)
|
||||
.StartWith(wsm.instance_settings),
|
||||
wsm.subscribe_default(),
|
||||
(i, d) => i ?? d
|
||||
).Where(v => v != null)
|
||||
.DistinctUntilChanged();
|
||||
|
||||
public static T current_settings<T>(this IWindowSettingsManager<T> wsm) where T : class =>
|
||||
wsm.instance_settings ?? wsm.default_settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
#nullable enable
|
||||
namespace mbts.persist {
|
||||
using bts;
|
||||
using bts.api.core;
|
||||
using bts.api.persistence;
|
||||
using bts.spark;
|
||||
|
||||
using Spark.Core.Protobuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
public class SettingsPersistenceModel : IPersist, IUserPersist {
|
||||
readonly PersistenceService _persist;
|
||||
readonly ErrorReporter _errors;
|
||||
readonly string _user;
|
||||
|
||||
public SettingsPersistenceModel(
|
||||
string user, PersistenceService persist, ErrorReporter errors) {
|
||||
_user = user;
|
||||
_persist = persist;
|
||||
_errors = errors;
|
||||
}
|
||||
|
||||
public Task set<T>(string ns, string key, T value) {
|
||||
return _persist.store_async(new StoreRequest() {
|
||||
Ns = _user_ns(ns),
|
||||
Record = {
|
||||
new Record() {
|
||||
Key = key,
|
||||
Value = ProtobufSerializationHelper.serialize_to_byte_string(value)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Task clear(string ns, string key) {
|
||||
return _persist.remove_async(new RemoveRequest() {
|
||||
Ns = _user_ns(ns),
|
||||
Key = { key }
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<T?> get<T>(string ns, string key) {
|
||||
var ret = await _persist.retrieve_async(new RetrieveRequest() {
|
||||
Ns = _user_ns(ns),
|
||||
Key = { key }
|
||||
});
|
||||
_check_error(ret, ns, key, out var empty);
|
||||
if (empty) {
|
||||
return default;
|
||||
}
|
||||
return ProtobufSerializationHelper.deserialize<T>(ret.value.Record.Single().Value);
|
||||
}
|
||||
|
||||
public async Task<T[]> get_many<T>(string ns, string key) {
|
||||
var ret = await _persist.retrieve_async(new RetrieveRequest() {
|
||||
Ns = _user_ns(ns),
|
||||
Key = { key }
|
||||
});
|
||||
_check_error(ret, ns, key, out bool empty);
|
||||
if (empty) {
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
return ProtobufSerializationHelper.deserialize_many<T>(
|
||||
ret.value.Record.Single().Value.ToByteArray());
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<PersistResult<T>>> query<T>(string ns, string pattern) {
|
||||
var regex = new Regex(pattern);
|
||||
var results = await _persist.retrieve_all_async(
|
||||
new RetrieveAllRequest() {
|
||||
Ns = _user_ns(ns)
|
||||
});
|
||||
_check_error(results, ns, string.Empty, out bool empty);
|
||||
if (empty) {
|
||||
return Enumerable.Empty<PersistResult<T>>();
|
||||
}
|
||||
return results.value.Record
|
||||
.Where(r => regex.IsMatch(r.Key))
|
||||
.Select(r => new PersistResult<T>() {
|
||||
user = _user,
|
||||
ns = ns,
|
||||
key = r.Key,
|
||||
value = ProtobufSerializationHelper.deserialize<T>(r.Value)
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public IDisposable subscribe<T>(
|
||||
string ns, string pattern, Action<IEnumerable<PersistResult<T>>> callback) =>
|
||||
subscribe<T>(ns, pattern).Subscribe(callback);
|
||||
|
||||
public IObservable<IEnumerable<PersistResult<T>>> subscribe<T>(string ns, string pattern) {
|
||||
var ctx = SynchronizationContext.Current ??
|
||||
throw new InvalidOperationException("Current SynchronizationContext is null");
|
||||
var re = new Regex(pattern);
|
||||
return _persist
|
||||
.subscribe_ns(_user_ns(ns))
|
||||
.Select(update =>
|
||||
update.added
|
||||
.Where(item => re.IsMatch(item.Key))
|
||||
.Select(item => new PersistResult<T>() {
|
||||
user = _user,
|
||||
ns = ns,
|
||||
key = item.Key,
|
||||
value = ProtobufSerializationHelper.deserialize<T>(item.Value)
|
||||
}))
|
||||
.SubscribeOn(ctx);
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
string _user_ns(string ns) =>
|
||||
$"{_user}#{ns}";
|
||||
|
||||
void _check_error(Result<RetrieveResponse> result, string ns, string key, out bool empty) {
|
||||
if (result.success) {
|
||||
switch (result.value.Result) {
|
||||
case RetrieveResponse.Types.Result.EmptyNs:
|
||||
case RetrieveResponse.Types.Result.NoSuchNs:
|
||||
case RetrieveResponse.Types.Result.NoSuchKey:
|
||||
empty = true;
|
||||
return;
|
||||
case RetrieveResponse.Types.Result.EmptyKey:
|
||||
case RetrieveResponse.Types.Result.DuplicateKey:
|
||||
_errors?.report_error(
|
||||
$"Invalid Key {result} '{key}'",
|
||||
"Requested Invalid Settings Key",
|
||||
MessageBoxImage.Information);
|
||||
empty = true;
|
||||
return;
|
||||
default:
|
||||
case RetrieveResponse.Types.Result.Ok:
|
||||
empty = false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_errors?.report_error(
|
||||
$"There was an error communicating with the settings server: {result.error_message}",
|
||||
"Failed to retrieve settings from the server");
|
||||
empty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(IPersist))]
|
||||
public class UISettings : SettingsPersistenceModel {
|
||||
[ImportingConstructor]
|
||||
public UISettings([Import("settings")] PersistenceService persist, ErrorReporter err)
|
||||
: base("all_users", persist, err) { }
|
||||
}
|
||||
|
||||
[Export(typeof(IUserPersist))]
|
||||
public class UserUISettings : SettingsPersistenceModel {
|
||||
public UserUISettings(
|
||||
uint user_id, [Import("settings")] PersistenceService persist, ErrorReporter err)
|
||||
: base($"User {user_id}", persist, err) { }
|
||||
|
||||
[ImportingConstructor]
|
||||
public UserUISettings(
|
||||
Lazy<UserTuple> user, [Import("settings")] PersistenceService persist, ErrorReporter err)
|
||||
: this(user.Value.id, persist, err) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts.utils;
|
||||
|
||||
using System;
|
||||
|
||||
interface ISettingsProfile<T> {
|
||||
uint id { get; }
|
||||
string? name { get; }
|
||||
T? value { get; }
|
||||
SecurityCriteria? security_criteria { get; }
|
||||
}
|
||||
|
||||
readonly struct SettingsProfile<T> : IEquatable<SettingsProfile<T>> where T : class, IEquatable<T> {
|
||||
public static readonly Func<SettingsProfile<T>, uint> key_fn = ns => ns.id;
|
||||
|
||||
public readonly uint id;
|
||||
public readonly string? name;
|
||||
public readonly T? value;
|
||||
public readonly SecurityCriteria? security_criteria;
|
||||
|
||||
public SettingsProfile(uint id, string? name, T? value, SecurityCriteria? security_criteria = null) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.security_criteria = security_criteria;
|
||||
}
|
||||
|
||||
public SettingsProfile<T> with(uint? id = null, string? name = null, T? value = null, SecurityCriteria? security_criteria = null) =>
|
||||
new(id ?? this.id, name ?? this.name, value ?? this.value, security_criteria ?? this.security_criteria);
|
||||
|
||||
public bool Equals(SettingsProfile<T> other) =>
|
||||
id == other.id &&
|
||||
name == other.name &&
|
||||
value.safe_equals(other.value) &&
|
||||
security_criteria.safe_equals(other.security_criteria);
|
||||
|
||||
public override bool Equals(object? obj) =>
|
||||
obj is SettingsProfile<T> other && Equals(other);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
id.GetHashCode();
|
||||
}
|
||||
}
|
||||
+73
-63
@@ -1,63 +1,73 @@
|
||||
using mbts.persist;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.ui.tests {
|
||||
// Stub persistance store for shell unit tests
|
||||
public class TestPersist : IPersist, IUserPersist {
|
||||
public Dictionary<string, Dictionary<string, object>> Values = new Dictionary<string, Dictionary<string, object>>();
|
||||
|
||||
public Task set<T>(string ns, string key, T value) {
|
||||
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
|
||||
try {
|
||||
Values[ns][key] = value;
|
||||
source.TrySetResult(null);
|
||||
} catch (Exception e) {
|
||||
source.TrySetException(e);
|
||||
}
|
||||
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
public Task clear(string ns, string key) {
|
||||
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
|
||||
try {
|
||||
Values[ns].Remove(key);
|
||||
source.TrySetResult(null);
|
||||
} catch (Exception e) {
|
||||
source.TrySetException(e);
|
||||
}
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
public Task<T> get<T>(string ns, string key) {
|
||||
TaskCompletionSource<T> source = new TaskCompletionSource<T>();
|
||||
try {
|
||||
T result = (T)Values[ns][key];
|
||||
source.TrySetResult(result);
|
||||
} catch (Exception e) {
|
||||
source.TrySetException(e);
|
||||
}
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Tuple<T, DateTime>>> get_history<T>(string ns, string key) {
|
||||
var res = await get<T>(ns, key);
|
||||
return new Tuple<T, DateTime>[] { Tuple.Create(res, DateTime.Today) };
|
||||
}
|
||||
|
||||
public Task<IEnumerable<PersistResult<T>>> query<T>(string ns, string pattern) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IDisposable subscribe<T>(string ns, string pattern, Action<IEnumerable<PersistResult<T>>> callback) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
using mbts.persist;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.ui.tests {
|
||||
// Stub persistance store for shell unit tests
|
||||
public class TestPersist : IPersist, IUserPersist {
|
||||
public Dictionary<string, Dictionary<string, object>> Values = new Dictionary<string, Dictionary<string, object>>();
|
||||
|
||||
public Task set<T>(string ns, string key, T value) {
|
||||
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
|
||||
try {
|
||||
Values[ns][key] = value;
|
||||
source.TrySetResult(null);
|
||||
} catch (Exception e) {
|
||||
source.TrySetException(e);
|
||||
}
|
||||
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
public Task clear(string ns, string key) {
|
||||
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
|
||||
try {
|
||||
Values[ns].Remove(key);
|
||||
source.TrySetResult(null);
|
||||
} catch (Exception e) {
|
||||
source.TrySetException(e);
|
||||
}
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
public Task<T> get<T>(string ns, string key) {
|
||||
TaskCompletionSource<T> source = new TaskCompletionSource<T>();
|
||||
try {
|
||||
T result = (T)Values[ns][key];
|
||||
source.TrySetResult(result);
|
||||
} catch (Exception e) {
|
||||
source.TrySetException(e);
|
||||
}
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
public Task<T[]> get_many<T>(string ns, string key) {
|
||||
var source = new TaskCompletionSource<T[]>();
|
||||
try {
|
||||
source.TrySetResult((T[])Values[ns][key]);
|
||||
} catch (Exception e) {
|
||||
source.TrySetException(e);
|
||||
}
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Tuple<T, DateTime>>> get_history<T>(string ns, string key) {
|
||||
var res = await get<T>(ns, key);
|
||||
return new Tuple<T, DateTime>[] { Tuple.Create(res, DateTime.Today) };
|
||||
}
|
||||
|
||||
public Task<IEnumerable<PersistResult<T>>> query<T>(string ns, string pattern) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IDisposable subscribe<T>(string ns, string pattern, Action<IEnumerable<PersistResult<T>>> callback) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
File renamed without changes.
+242
-189
@@ -1,189 +1,242 @@
|
||||
using bts.shell.pnl;
|
||||
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
using mbts.ui.settings;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
interface IDefaultableSettings {
|
||||
bool use_defaults { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The settings Mothership. All window - and any other future user based - settings should have a corresponding default member here.
|
||||
/// </summary>
|
||||
[ProtoContract]
|
||||
class UserSettings {
|
||||
public static UserSettings factory_defaults =>
|
||||
new UserSettings {
|
||||
_ladder = LadderSettings.Default,
|
||||
_notification = NotificationSettings.Default,
|
||||
_market_view_grid = bts.shell.market_view.MarketViewSettings.DefaultGridSettings,
|
||||
_fills_grid = bts.shell.fills.FillsSettings.DefaultGridSettings,
|
||||
_time_and_sales_grid = bts.shell.time_and_sales.TimeAndSalesSettings.DefaultGridSettings,
|
||||
_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings,
|
||||
_order_book_grid = bts.shell.orderbook.OrderBookStartupData.DefaultGrid
|
||||
};
|
||||
|
||||
[ProtoMember(1)]
|
||||
LadderSettings _ladder;
|
||||
[ProtoMember(11)]
|
||||
readonly LadderSettingsProfile[] _ladder_profiles;
|
||||
/// When Profiles Are Deleted, ladders that were using that profile shall copy the profile's settings to a local profile.
|
||||
/// Therefor:
|
||||
/// Deleted profile ids must never be reused.
|
||||
/// If they were reused, consider an unloaded workspace with ladders that were using the deleted profile.
|
||||
/// If such a workspace was reloaded after a new profile was saved with the deleted one's id, the ladders using that deleted profile would suddenly be using the new profile.
|
||||
/// To Prevent IDs From Being Recycled:
|
||||
/// The last profile id is stored in the protobuf (<see cref="_last_ladder_profile_id"/>).
|
||||
/// Anytime a profile is added, it shall use <see cref="next_ladder_profile_id"/>.
|
||||
[ProtoMember(12)]
|
||||
uint _last_ladder_profile_id;
|
||||
// removing NotificationSettings these from proto-serialization, since they are disabled by default for now.
|
||||
// This way, when they are enabled, there won't be any backwards compatability issues to worry about, as no clients will have persisted them yet.
|
||||
NotificationSettings _notification;
|
||||
|
||||
[ProtoMember(5)]
|
||||
GridSettings _market_view_grid;
|
||||
[ProtoMember(6)]
|
||||
GridSettings _time_and_sales_grid;
|
||||
[ProtoMember(7)]
|
||||
GridSettings _fills_grid;
|
||||
[ProtoMember(8)]
|
||||
GridSettings _synthetic_management_grid;
|
||||
[ProtoMember(9)]
|
||||
bts.shell.orderbook.OrderBookGridSettings _order_book_grid;
|
||||
[ProtoMember(10)]
|
||||
PNLSettings _pnl;
|
||||
[ProtoMember(13)]
|
||||
GlobalAlertSettings _alerts;
|
||||
|
||||
public UserSettings() { }
|
||||
|
||||
public UserSettings(LadderSettingsProfile[] ladder_profiles) =>
|
||||
_ladder_profiles = ladder_profiles;
|
||||
|
||||
public LadderSettings ladder => _ladder ?? (_ladder = LadderSettings.Default);
|
||||
public IReadOnlyList<LadderSettingsProfile> ladder_profiles => _ladder_profiles ?? Array.Empty<LadderSettingsProfile>();
|
||||
public uint next_ladder_profile_id => _last_ladder_profile_id + 1;
|
||||
public NotificationSettings notification => _notification ?? (_notification = NotificationSettings.Default);
|
||||
public GridSettings market_view_grid => _market_view_grid ?? (_market_view_grid = bts.shell.market_view.MarketViewSettings.DefaultGridSettings);
|
||||
public GridSettings time_and_sales_grid => _time_and_sales_grid ?? (_time_and_sales_grid = GridSettings.Default.with(bts.shell.time_and_sales.TimeAndSalesSettings.DefaultColumns.all));
|
||||
public GridSettings fills_grid => _fills_grid ?? (_fills_grid = bts.shell.fills.FillsSettings.DefaultGridSettings);
|
||||
public GridSettings synthetic_management_grid => _synthetic_management_grid ?? (_synthetic_management_grid = bts.shell.SyntheticManagementWindowFactory.StartupData.DefaultGridSettings);
|
||||
public PNLSettings pnl => _pnl ?? (_pnl = PNLSettings.Default);
|
||||
public bts.shell.orderbook.OrderBookGridSettings order_book_grid => _order_book_grid ?? (_order_book_grid = bts.shell.orderbook.OrderBookStartupData.Default.grid_settings_wrapper());
|
||||
|
||||
public GlobalAlertSettings alerts => _alerts ?? (_alerts = new GlobalAlertSettings());
|
||||
|
||||
public UserSettings with(LadderSettings ladder = null,
|
||||
LadderSettingsProfile[] ladder_profiles = null,
|
||||
NotificationSettings notification = null,
|
||||
GridSettings provided_market_view_gridsettings = null,
|
||||
GridSettings time_and_sales = null,
|
||||
GridSettings provided_fills_gridsettings = null,
|
||||
GridSettings provided_synthetic_management_gridsettings = null,
|
||||
PNLSettings pnl = null,
|
||||
bts.shell.orderbook.OrderBookGridSettings order_book_grid = null,
|
||||
GlobalAlertSettings alerts = null) {
|
||||
var us = new UserSettings(ladder_profiles ?? _ladder_profiles) {
|
||||
_ladder = ladder ?? _ladder,
|
||||
_notification = notification ?? _notification,
|
||||
_market_view_grid = provided_market_view_gridsettings ?? _market_view_grid,
|
||||
_time_and_sales_grid = time_and_sales ?? _time_and_sales_grid,
|
||||
_fills_grid = provided_fills_gridsettings ?? _fills_grid,
|
||||
_synthetic_management_grid = provided_synthetic_management_gridsettings ?? _synthetic_management_grid,
|
||||
_pnl = pnl ?? _pnl,
|
||||
_order_book_grid = order_book_grid ?? _order_book_grid,
|
||||
_alerts = alerts ?? _alerts
|
||||
};
|
||||
us._last_ladder_profile_id = Math.Max(_last_ladder_profile_id, us.ladder_profiles.Select(p => p.id).MaxOrDefault());
|
||||
return us;
|
||||
}
|
||||
|
||||
public static class FileExtensions {
|
||||
public const string
|
||||
GRID = "grid",
|
||||
LADDER = "ladder",
|
||||
PNL = "pnl";
|
||||
}
|
||||
}
|
||||
|
||||
// - currently all notification-triggers are in GridSettings
|
||||
// - the SoundNotificationSettings below are used to disable/enable SoundNotifications
|
||||
// - the ToastNotificationSettings below are used to disable/enable ToastNotifications, and adjust the flash-duration (if enabled).
|
||||
// - since this is a new feature, the ability to quickly enable/disable both types of notifications may save us the trouble of having to redeploy,
|
||||
// in the unfortunate event that a bug slipped through the cracks, or the performance impact is more significant than expected.
|
||||
// TODO: maybe globalize notification settings?
|
||||
[ProtoContract]
|
||||
class NotificationSettings {
|
||||
[ProtoMember(1)]
|
||||
readonly SoundNotificationSettings _sound_notification;
|
||||
[ProtoMember(2)]
|
||||
readonly ToastNotificationSettings _toast_notification;
|
||||
|
||||
public NotificationSettings() { }
|
||||
public NotificationSettings(SoundNotificationSettings sound_notification, ToastNotificationSettings toast_notification) {
|
||||
_sound_notification = sound_notification;
|
||||
_toast_notification = toast_notification;
|
||||
}
|
||||
|
||||
public static NotificationSettings Default => new NotificationSettings(SoundNotificationSettings.Default, ToastNotificationSettings.Default);
|
||||
|
||||
public SoundNotificationSettings sound_notification => _sound_notification;
|
||||
public ToastNotificationSettings toast_notification => _toast_notification;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class SoundNotificationSettings {
|
||||
[ProtoMember(1)]
|
||||
readonly bool _enabled;
|
||||
|
||||
public SoundNotificationSettings() { }
|
||||
public SoundNotificationSettings(bool enabled) =>
|
||||
_enabled = enabled;
|
||||
|
||||
public static SoundNotificationSettings Default => new SoundNotificationSettings(true);
|
||||
|
||||
public bool enabled => _enabled;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class ToastNotificationSettings {
|
||||
// though it is possible to infer such Modes from Duration values (null = Disabled, max = ManualClose, otherwise Flash)
|
||||
// this Mode enum makes the behavior very clear, and less likely to be misinterpretted down the road.
|
||||
// (without this enum, the meaning of "duration = null," and "duration = 0 seconds," is debatable)
|
||||
public enum Mode {
|
||||
Disabled,
|
||||
ManualClose,
|
||||
Flash
|
||||
}
|
||||
|
||||
public static ToastNotificationSettings Default =>
|
||||
new ToastNotificationSettings(Mode.Flash, new Duration { Seconds = 5 }, 320);
|
||||
|
||||
[ProtoMember(1)]
|
||||
readonly Mode _mode;
|
||||
[ProtoMember(2)]
|
||||
readonly Duration _duration;
|
||||
[ProtoMember(3)]
|
||||
readonly double _max_height;
|
||||
|
||||
public ToastNotificationSettings() { }
|
||||
public ToastNotificationSettings(Mode mode, Duration duration, double max_height) {
|
||||
_mode = mode;
|
||||
_duration = duration;
|
||||
_max_height = max_height;
|
||||
}
|
||||
|
||||
public Mode mode => _mode;
|
||||
public TimeSpan? duration => _mode == Mode.Flash ? _duration?.ToTimeSpan() ?? TimeSpan.FromSeconds(5) : new TimeSpan?();
|
||||
public double max_height => _max_height;
|
||||
}
|
||||
}
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts.shell;
|
||||
using bts.shell.fills;
|
||||
using bts.shell.market_view;
|
||||
using bts.shell.orderbook;
|
||||
using bts.shell.pnl;
|
||||
using bts.shell.time_and_sales;
|
||||
using bts.utils;
|
||||
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
using mbts.ui.settings;
|
||||
|
||||
using ProtoBuf;
|
||||
|
||||
using Spark.Trading;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
interface IDefaultableSettings {
|
||||
bool use_defaults { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The settings Mothership. All window - and any other future user based - settings should have a corresponding default member here.
|
||||
/// </summary>
|
||||
[ProtoContract]
|
||||
class UserSettings {
|
||||
public static UserSettings factory_defaults =>
|
||||
new() {
|
||||
_ladder = LadderSettings.Default,
|
||||
_notification = NotificationSettings.Default,
|
||||
_market_view_grid = MarketViewSettings.DefaultGridSettings,
|
||||
_fills_grid = FillsSettings.DefaultGridSettings,
|
||||
_time_and_sales_grid = TimeAndSalesSettings.DefaultGridSettings,
|
||||
_synthetic_management = SyntheticManagementSettings.default_grid_settings,
|
||||
_reconcile_report = ReconcileReportSettings.Default().grid,
|
||||
_order_book = OrderBookStartupData.DefaultGrid
|
||||
};
|
||||
|
||||
[ProtoMember(1)]
|
||||
LadderSettings? _ladder;
|
||||
[ProtoMember(11)]
|
||||
LadderSettingsProfile[]? _ladder_profiles;
|
||||
/// When Profiles Are Deleted, ladders that were using that profile shall copy the profile's settings to a local profile.
|
||||
/// Therefor:
|
||||
/// Deleted profile ids must never be reused.
|
||||
/// If they were reused, consider an unloaded workspace with ladders that were using the deleted profile.
|
||||
/// If such a workspace was reloaded after a new profile was saved with the deleted one's id, the ladders using that deleted profile would suddenly be using the new profile.
|
||||
/// To Prevent IDs From Being Recycled:
|
||||
/// The last profile id is stored in the protobuf (<see cref="_last_ladder_profile_id"/>).
|
||||
/// Anytime a profile is added, it shall use <see cref="next_ladder_profile_id"/>.
|
||||
[ProtoMember(12)]
|
||||
uint _last_ladder_profile_id;
|
||||
// removing NotificationSettings these from proto-serialization, since they are disabled by default for now.
|
||||
// This way, when they are enabled, there won't be any backwards compatability issues to worry about, as no clients will have persisted them yet.
|
||||
NotificationSettings? _notification;
|
||||
|
||||
[ProtoMember(5)]
|
||||
GridSettings? _market_view_grid;
|
||||
[ProtoMember(6)]
|
||||
GridSettings? _time_and_sales_grid;
|
||||
[ProtoMember(7)]
|
||||
GridSettings? _fills_grid;
|
||||
[ProtoMember(8)]
|
||||
GridSettings? _synthetic_management;
|
||||
[ProtoMember(14)]
|
||||
GridSettings? _reconcile_report;
|
||||
[ProtoMember(9)]
|
||||
OrderBookGridSettings? _order_book;
|
||||
[ProtoMember(10)]
|
||||
PNLSettings? _pnl;
|
||||
[ProtoMember(13)]
|
||||
GlobalAlertSettings? _alerts;
|
||||
|
||||
/// Actual settings may deviate from those of the theme <see cref="_last_picked_theme"/>, as
|
||||
/// the user is free to change specific properties after selecting a theme.
|
||||
[ProtoMember(15)]
|
||||
string? _last_picked_theme;
|
||||
|
||||
public UserSettings() { }
|
||||
|
||||
public LadderSettings ladder =>
|
||||
_ladder ??= LadderSettings.Default;
|
||||
|
||||
public IReadOnlyList<LadderSettingsProfile> ladder_profiles =>
|
||||
_ladder_profiles ?? Array.Empty<LadderSettingsProfile>();
|
||||
|
||||
public uint next_ladder_profile_id =>
|
||||
_last_ladder_profile_id + 1;
|
||||
|
||||
public NotificationSettings notification =>
|
||||
_notification ??= NotificationSettings.Default;
|
||||
|
||||
public GridSettings market_view_grid =>
|
||||
_market_view_grid ??= MarketViewSettings.DefaultGridSettings;
|
||||
|
||||
public GridSettings time_and_sales_grid =>
|
||||
_time_and_sales_grid ??= GridSettings.Default.with(
|
||||
TimeAndSalesSettings.DefaultColumns.all);
|
||||
|
||||
public GridSettings fills_grid =>
|
||||
_fills_grid ??= FillsSettings.DefaultGridSettings;
|
||||
|
||||
public GridSettings synthetic_management_grid =>
|
||||
_synthetic_management ??=
|
||||
SyntheticManagementSettings.default_grid_settings;
|
||||
|
||||
public GridSettings reconcile_report_grid =>
|
||||
_reconcile_report ??= ReconcileReportSettings.Default().grid ??
|
||||
throw new InvalidOperationException();
|
||||
|
||||
public PNLSettings pnl =>
|
||||
_pnl ??= PNLSettings.Default;
|
||||
|
||||
public OrderBookGridSettings order_book_grid =>
|
||||
_order_book ??= OrderBookStartupData.Default.grid_settings_wrapper();
|
||||
|
||||
public GlobalAlertSettings alerts =>
|
||||
_alerts ??= new GlobalAlertSettings();
|
||||
|
||||
public string? last_picked_theme => _last_picked_theme;
|
||||
|
||||
public UserSettings with(
|
||||
LadderSettings? ladder = null,
|
||||
LadderSettingsProfile[]? ladder_profiles = null,
|
||||
NotificationSettings? notification = null,
|
||||
GridSettings? market_view = null,
|
||||
GridSettings? time_and_sales = null,
|
||||
GridSettings? fills = null,
|
||||
GridSettings? synthetic_management = null,
|
||||
GridSettings? reconcile_report = null,
|
||||
PNLSettings? pnl = null,
|
||||
OrderBookGridSettings? order_book_grid = null,
|
||||
GlobalAlertSettings? alerts = null,
|
||||
Optional<string?> last_picked_theme = default) {
|
||||
var us = new UserSettings() {
|
||||
_ladder_profiles = ladder_profiles ?? _ladder_profiles,
|
||||
_ladder = ladder ?? _ladder,
|
||||
_notification = notification ?? _notification,
|
||||
_market_view_grid = market_view ?? _market_view_grid,
|
||||
_time_and_sales_grid = time_and_sales ?? _time_and_sales_grid,
|
||||
_fills_grid = fills ?? _fills_grid,
|
||||
_synthetic_management = synthetic_management ?? _synthetic_management,
|
||||
_reconcile_report = reconcile_report ?? _reconcile_report,
|
||||
_pnl = pnl ?? _pnl,
|
||||
_order_book = order_book_grid ?? _order_book,
|
||||
_alerts = alerts ?? _alerts,
|
||||
_last_picked_theme = last_picked_theme.or(_last_picked_theme)
|
||||
};
|
||||
us._last_ladder_profile_id = Math.Max(_last_ladder_profile_id, us.ladder_profiles.Select(p => p.id).MaxOrDefault());
|
||||
return us;
|
||||
}
|
||||
|
||||
public static class FileExtensions {
|
||||
public const string
|
||||
GRID = "grid",
|
||||
LADDER = "ladder",
|
||||
PNL = "pnl";
|
||||
}
|
||||
}
|
||||
|
||||
// - currently all notification-triggers are in GridSettings
|
||||
// - the SoundNotificationSettings below are used to disable/enable SoundNotifications
|
||||
// - the ToastNotificationSettings below are used to disable/enable ToastNotifications, and adjust the flash-duration (if enabled).
|
||||
// - since this is a new feature, the ability to quickly enable/disable both types of notifications may save us the trouble of having to redeploy,
|
||||
// in the unfortunate event that a bug slipped through the cracks, or the performance impact is more significant than expected.
|
||||
// TODO: maybe globalize notification settings?
|
||||
[ProtoContract]
|
||||
class NotificationSettings {
|
||||
[ProtoMember(1)]
|
||||
readonly SoundNotificationSettings? _sound_notification;
|
||||
[ProtoMember(2)]
|
||||
readonly ToastNotificationSettings? _toast_notification;
|
||||
|
||||
public NotificationSettings() { }
|
||||
public NotificationSettings(SoundNotificationSettings sound_notification, ToastNotificationSettings toast_notification) {
|
||||
_sound_notification = sound_notification;
|
||||
_toast_notification = toast_notification;
|
||||
}
|
||||
|
||||
public static NotificationSettings Default => new NotificationSettings(SoundNotificationSettings.Default, ToastNotificationSettings.Default);
|
||||
|
||||
public SoundNotificationSettings? sound_notification => _sound_notification;
|
||||
public ToastNotificationSettings? toast_notification => _toast_notification;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class SoundNotificationSettings {
|
||||
[ProtoMember(1)]
|
||||
readonly bool _enabled;
|
||||
|
||||
public SoundNotificationSettings() { }
|
||||
public SoundNotificationSettings(bool enabled) =>
|
||||
_enabled = enabled;
|
||||
|
||||
public static SoundNotificationSettings Default => new(true);
|
||||
|
||||
public bool enabled => _enabled;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class ToastNotificationSettings {
|
||||
// though it is possible to infer such Modes from Duration values (null = Disabled, max = ManualClose, otherwise Flash)
|
||||
// this Mode enum makes the behavior very clear, and less likely to be misinterpretted down the road.
|
||||
// (without this enum, the meaning of "duration = null," and "duration = 0 seconds," is debatable)
|
||||
public enum Mode {
|
||||
Disabled,
|
||||
ManualClose,
|
||||
Flash
|
||||
}
|
||||
|
||||
public static ToastNotificationSettings Default =>
|
||||
new(Mode.Flash, new Duration { Seconds = 5 }, 320);
|
||||
|
||||
[ProtoMember(1)]
|
||||
readonly Mode _mode;
|
||||
[ProtoMember(2)]
|
||||
readonly Duration? _duration;
|
||||
[ProtoMember(3)]
|
||||
readonly double _max_height;
|
||||
|
||||
public ToastNotificationSettings() { }
|
||||
|
||||
public ToastNotificationSettings(Mode mode, Duration duration, double max_height) {
|
||||
_mode = mode;
|
||||
_duration = duration;
|
||||
_max_height = max_height;
|
||||
}
|
||||
|
||||
public Mode mode => _mode;
|
||||
public TimeSpan duration =>
|
||||
_mode switch {
|
||||
Mode.Flash => _duration?.ToTimeSpan() ?? TimeSpan.FromSeconds(5),
|
||||
_ => new()
|
||||
};
|
||||
public double max_height => _max_height;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#nullable enable
|
||||
namespace mbts.spark {
|
||||
using bts;
|
||||
using bts.api;
|
||||
|
||||
using mbts.persist;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
static class UserSettingsExtensions {
|
||||
/// <summary>
|
||||
/// Gets the ladder settings profile for a particular security
|
||||
/// </summary>
|
||||
public static LadderSettingsProfile? get_ladder_profile(
|
||||
this UserSettings settings, SecurityImmutable security) =>
|
||||
settings.ladder_profiles.FirstOrDefault(lsp =>
|
||||
lsp?.security_criteria?.includes(security) ?? false);
|
||||
|
||||
public static IObservable<LadderSettingsProfile?> subscribe_ladder_profile(
|
||||
this UserSettingsHub settings_hub, SecurityImmutable security) =>
|
||||
settings_hub.subscribe_default_settings().Select(s =>
|
||||
s.get_ladder_profile(security));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default account for a particular security (from the
|
||||
/// <see cref="LadderSettingsProfile"/> associated with that security)
|
||||
/// </summary>
|
||||
public static IObservable<AccountTuple?> subscribe_default_account(
|
||||
this UserSettingsHub settings_hub, IBus bus, SecurityImmutable security) {
|
||||
var acc_id_src = settings_hub.subscribe_default_settings().Select(s =>
|
||||
s.get_ladder_profile(security)?.value?.default_account);
|
||||
return acc_id_src.CombineLatest(
|
||||
bus.subscribe(SparkTags.AccountsTag),
|
||||
(acc_id, accs) => acc_id.HasValue && accs.TryGetValue(acc_id.Value, out var acc_tup) ?
|
||||
acc_tup : (AccountTuple?)null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+226
-165
@@ -1,165 +1,226 @@
|
||||
using bts;
|
||||
using bts.settings;
|
||||
using bts.shell.pnl;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.ui.settings;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.persist {
|
||||
/// <summary>
|
||||
/// Represents the top level of the persisted settings hierarchy.
|
||||
/// One UserSettings object, which contains the defaults for every SparkWindow, can be read/written on the server by UISettingsHub.
|
||||
/// </summary>
|
||||
[Export(typeof(UserSettingsHub))]
|
||||
class UserSettingsHub {
|
||||
// do NOT CHANGE these consts, they represent a file path on the server
|
||||
const string NAME_SPACE = "settings", DEFAULT_KEY = "default";
|
||||
|
||||
static readonly IImmutableProperty<UserSettings, GridSettings>
|
||||
_fills_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.fills_grid, (us, gs) => us.with(provided_fills_gridsettings: gs)),
|
||||
_mview_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.market_view_grid, (us, gs) => us.with(provided_market_view_gridsettings: gs)),
|
||||
_synth_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.synthetic_management_grid, (us, gs) => us.with(provided_synthetic_management_gridsettings: gs));
|
||||
|
||||
static readonly IImmutableProperty<UserSettings, PNLSettings>
|
||||
_pnl_propi = ImmutableProperty.create<UserSettings, PNLSettings>(us => us.pnl, (us, pnl) => us.with(pnl: pnl));
|
||||
|
||||
readonly object _default_settings_lock = new object();
|
||||
readonly BehaviorSubject<UserSettings> _default_settings_subject = new BehaviorSubject<UserSettings>(UserSettings.factory_defaults);
|
||||
readonly IEnumerable<IFillsGridSettingsMigrationRule> _fills_migration_rules;
|
||||
readonly IEnumerable<IMarketViewGridSettingsMigrationRule> _mview_migration_rules;
|
||||
readonly IEnumerable<IPNLSettingsMigrationRule> _pnl_migration_rules;
|
||||
readonly IEnumerable<ISyntheticManagementMigrationRule> _synth_migration_rules;
|
||||
readonly IUserPersist _persist;
|
||||
|
||||
UserSettings _default_settings;
|
||||
|
||||
[ImportingConstructor]
|
||||
public UserSettingsHub(
|
||||
IUserPersist persist,
|
||||
[ImportMany] IEnumerable<IFillsGridSettingsMigrationRule> fills_migration_rules,
|
||||
[ImportMany] IEnumerable<IMarketViewGridSettingsMigrationRule> mview_migration_rules,
|
||||
[ImportMany] IEnumerable<IPAndLGridSettingsMigrationRule> pandl_migration_rules,
|
||||
[ImportMany] IEnumerable<IPNLSettingsMigrationRule> pnl_migration_rules,
|
||||
[ImportMany] IEnumerable<ISyntheticManagementMigrationRule> synth_migration_rules) {
|
||||
//
|
||||
_persist = persist;
|
||||
_fills_migration_rules = fills_migration_rules ?? Enumerable.Empty<IFillsGridSettingsMigrationRule>();
|
||||
_mview_migration_rules = mview_migration_rules ?? Enumerable.Empty<IMarketViewGridSettingsMigrationRule>();
|
||||
_pnl_migration_rules = pnl_migration_rules ?? Enumerable.Empty<IPNLSettingsMigrationRule>();
|
||||
_synth_migration_rules = synth_migration_rules ?? Enumerable.Empty<ISyntheticManagementMigrationRule>();
|
||||
init();
|
||||
async void init() {
|
||||
UserSettings s;
|
||||
try {
|
||||
s = await _persist.get<UserSettings>(NAME_SPACE, DEFAULT_KEY);
|
||||
if (s != null) {
|
||||
var wrapped_rules = new[] {
|
||||
_fills_migration_rules.merge().wrap(_fills_propi),
|
||||
_mview_migration_rules.merge().wrap(_mview_propi),
|
||||
_pnl_migration_rules.merge().wrap(_pnl_propi),
|
||||
_synth_migration_rules.merge().wrap(_synth_propi)
|
||||
};
|
||||
SettingsMigrationHelper.apply_quiet(ref s, wrapped_rules);
|
||||
// TODO: move ladder sanitization logic into ISettingsMigrationRule
|
||||
s.ladder.sanitize();
|
||||
// ensure valid named ladder set (w/ distinct, non-zero ids)
|
||||
var ladder_profile_map = new Dictionary<uint, LadderSettingsProfile>();
|
||||
var unidentified_lps = new List<LadderSettingsProfile>();
|
||||
foreach (var lp in s.ladder_profiles) {
|
||||
lp.value.sanitize();
|
||||
if (lp.id == 0) {
|
||||
unidentified_lps.Add(lp);
|
||||
} else ladder_profile_map[lp.id] = lp;
|
||||
}
|
||||
uint id = s.next_ladder_profile_id;
|
||||
foreach (var lp in unidentified_lps) {
|
||||
ladder_profile_map[id] = lp.with(id);
|
||||
id++;
|
||||
}
|
||||
var sorted_lps = ladder_profile_map.Values.ToArray();
|
||||
Array.Sort(sorted_lps, Comparer<uint>.Default.wrap<uint, LadderSettingsProfile>(lp => lp.id));
|
||||
if (!sorted_lps.SequenceEqual(s.ladder_profiles))
|
||||
await _persist.set(NAME_SPACE, DEFAULT_KEY, s = s.with(ladder_profiles: sorted_lps));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.Persist.warn($"Error loading {typeof(UserSettings)} from namespace '{NAME_SPACE}', will restore factory defaults and continue as usual: {ex}");
|
||||
await save_default(s = UserSettings.factory_defaults);
|
||||
}
|
||||
lock (_default_settings_lock) {
|
||||
initialized = true;
|
||||
if (s != null)
|
||||
_default_settings_subject.OnNext(_default_settings = s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// indicates whether or not the settings have been loaded from the server (this happens once, on startup - see the constructor for details)
|
||||
public bool initialized { get; private set; }
|
||||
|
||||
internal UserSettings current {
|
||||
get {
|
||||
lock (_default_settings_lock)
|
||||
return _default_settings ?? UserSettings.factory_defaults;
|
||||
}
|
||||
}
|
||||
|
||||
/// Subscribing via _persist.subscribe may be cleaner in theory, however using a local event allows observers to be notified immediately.
|
||||
/// Assuming there aren't any rogue agents saving UserSettings by themselves, there should never be any discrepancies.
|
||||
public IObservable<UserSettings> subscribe_default_settings() => _default_settings_subject.AsObservable();
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_fills_settings_manager(GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_fills_migration_rules,
|
||||
_fills_propi);
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_mview_settings_manager(GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_mview_migration_rules,
|
||||
_mview_propi);
|
||||
|
||||
public IWindowSettingsManager<PNLSettings> create_pnl_settings_manager(PNLSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_pnl_migration_rules,
|
||||
_pnl_propi);
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_synth_settings_manager(GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_synth_migration_rules,
|
||||
_synth_propi);
|
||||
|
||||
public async Task save_default(UserSettings value) {
|
||||
// notify observers before persisting so the changes are reflected immediately in the ui
|
||||
lock (_default_settings_lock) {
|
||||
_default_settings_subject.OnNext(_default_settings = value);
|
||||
}
|
||||
await _persist_async();
|
||||
}
|
||||
|
||||
IWindowSettingsManager<T> _create_settings_manager<T>(T instance_settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, IImmutableProperty<UserSettings, T> propi) where T : class {
|
||||
if (instance_settings != null && migration_rules.merge().apply(instance_settings, out var new_settings))
|
||||
instance_settings = new_settings;
|
||||
return new WindowSettingsManager<T>(this, instance_settings, propi.get, propi.set);
|
||||
}
|
||||
|
||||
async Task _persist_async() {
|
||||
try {
|
||||
await _persist.set(NAME_SPACE, DEFAULT_KEY, _default_settings);
|
||||
} catch (Exception ex) {
|
||||
Log.Persist.warn($"Error persisting {typeof(UserSettings)} in namespace '{NAME_SPACE}': {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using bts;
|
||||
using bts.settings;
|
||||
using bts.shell.pnl;
|
||||
using bts.utils;
|
||||
|
||||
using mbts.spark;
|
||||
using mbts.ui.settings;
|
||||
|
||||
using Spark.Settings;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.persist {
|
||||
/// <summary>
|
||||
/// Represents the top level of the persisted settings hierarchy.
|
||||
/// One UserSettings object, which contains the defaults for every SparkWindow, can be read/written on the server by UISettingsHub.
|
||||
/// </summary>
|
||||
[Export(typeof(UserSettingsHub))]
|
||||
class UserSettingsHub {
|
||||
// do NOT CHANGE these consts, they represent a file path on the server
|
||||
const string NAME_SPACE = "settings", DEFAULT_KEY = "default";
|
||||
|
||||
static readonly IImmutableProperty<UserSettings, GridSettings>
|
||||
_fills_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.fills_grid, (us, gs) => us.with(fills: gs)),
|
||||
_mview_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.market_view_grid, (us, gs) => us.with(market_view: gs)),
|
||||
_synth_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.synthetic_management_grid, (us, gs) => us.with(synthetic_management: gs)),
|
||||
_reconcile_report_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.reconcile_report_grid, (us, gs) => us.with(reconcile_report: gs));
|
||||
|
||||
static readonly IImmutableProperty<UserSettings, PNLSettings>
|
||||
_pnl_propi = ImmutableProperty.create<UserSettings, PNLSettings>(us => us.pnl, (us, pnl) => us.with(pnl: pnl));
|
||||
|
||||
readonly object _default_settings_lock = new object();
|
||||
readonly BehaviorSubject<UserSettings> _default_settings_subject = new BehaviorSubject<UserSettings>(UserSettings.factory_defaults);
|
||||
// A 'settings theme' is activated by updating the default settings object, and clearing all
|
||||
// 'instance' settings so existing windows adopt the new theme (i.e., the new defaults).
|
||||
readonly IEnumerable<IFillsGridSettingsMigrationRule> _fills_migration_rules;
|
||||
readonly IEnumerable<IMarketViewGridSettingsMigrationRule> _mview_migration_rules;
|
||||
readonly IEnumerable<IPNLSettingsMigrationRule> _pnl_migration_rules;
|
||||
readonly IEnumerable<ISyntheticManagementMigrationRule> _synth_migration_rules;
|
||||
readonly IEnumerable<IReconcileReportMigrationRule> _reconcile_report_migration_rules;
|
||||
readonly IUserPersist _persist;
|
||||
readonly Dictionary<string, UserSettingsTheme> _themes;
|
||||
|
||||
UserSettings _default_settings;
|
||||
IDisposable _theme_op = null;
|
||||
|
||||
[ImportingConstructor]
|
||||
public UserSettingsHub(
|
||||
IUserPersist persist,
|
||||
[ImportMany] IEnumerable<IFillsGridSettingsMigrationRule> fills_migration_rules,
|
||||
[ImportMany] IEnumerable<IMarketViewGridSettingsMigrationRule> mview_migration_rules,
|
||||
[ImportMany] IEnumerable<IPAndLGridSettingsMigrationRule> pandl_migration_rules,
|
||||
[ImportMany] IEnumerable<IPNLSettingsMigrationRule> pnl_migration_rules,
|
||||
[ImportMany] IEnumerable<ISyntheticManagementMigrationRule> synth_migration_rules,
|
||||
[ImportMany] IEnumerable<IReconcileReportMigrationRule> reconcile_report_migration_rules,
|
||||
[ImportMany] IEnumerable<UserSettingsTheme> themes) {
|
||||
//
|
||||
_persist = persist;
|
||||
_fills_migration_rules = fills_migration_rules ?? Enumerable.Empty<IFillsGridSettingsMigrationRule>();
|
||||
_mview_migration_rules = mview_migration_rules ?? Enumerable.Empty<IMarketViewGridSettingsMigrationRule>();
|
||||
_pnl_migration_rules = pnl_migration_rules ?? Enumerable.Empty<IPNLSettingsMigrationRule>();
|
||||
_synth_migration_rules = synth_migration_rules ?? Enumerable.Empty<ISyntheticManagementMigrationRule>();
|
||||
_reconcile_report_migration_rules = reconcile_report_migration_rules.empty_if_null();
|
||||
_themes = themes.ToDictionaryCrushed(t => t.name);
|
||||
init();
|
||||
async void init() {
|
||||
UserSettings s;
|
||||
try {
|
||||
s = await _persist.get<UserSettings>(NAME_SPACE, DEFAULT_KEY);
|
||||
if (s != null) {
|
||||
var wrapped_rules = new[] {
|
||||
_fills_migration_rules.merge().wrap(_fills_propi),
|
||||
_mview_migration_rules.merge().wrap(_mview_propi),
|
||||
_pnl_migration_rules.merge().wrap(_pnl_propi),
|
||||
_synth_migration_rules.merge().wrap(_synth_propi),
|
||||
_reconcile_report_migration_rules.merge().wrap(_reconcile_report_propi)
|
||||
};
|
||||
SettingsMigrationHelper.apply_quiet(ref s, wrapped_rules);
|
||||
// TODO: move ladder sanitization logic into ISettingsMigrationRule
|
||||
s.ladder.sanitize();
|
||||
// ensure valid named ladder set (w/ distinct, non-zero ids)
|
||||
var ladder_profile_map = new Dictionary<uint, LadderSettingsProfile>();
|
||||
var unidentified_lps = new List<LadderSettingsProfile>();
|
||||
foreach (var lp in s.ladder_profiles) {
|
||||
lp.value.sanitize();
|
||||
if (lp.id == 0) {
|
||||
unidentified_lps.Add(lp);
|
||||
} else ladder_profile_map[lp.id] = lp;
|
||||
}
|
||||
uint id = s.next_ladder_profile_id;
|
||||
foreach (var lp in unidentified_lps) {
|
||||
ladder_profile_map[id] = lp.with(id);
|
||||
id++;
|
||||
}
|
||||
var sorted_lps = ladder_profile_map.Values.ToArray();
|
||||
Array.Sort(sorted_lps, Comparer<uint>.Default.wrap<uint, LadderSettingsProfile>(lp => lp.id));
|
||||
if (!sorted_lps.SequenceEqual(s.ladder_profiles))
|
||||
await _persist.set(NAME_SPACE, DEFAULT_KEY, s = s.with(ladder_profiles: sorted_lps));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.Persist.warn($"Error loading {typeof(UserSettings)} from namespace '{NAME_SPACE}', will restore factory defaults and continue as usual: {ex}");
|
||||
await save_default(s = UserSettings.factory_defaults);
|
||||
}
|
||||
lock (_default_settings_lock) {
|
||||
initialized = true;
|
||||
if (s != null)
|
||||
_default_settings_subject.OnNext(_default_settings = s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event Action request_clear_instance_settings {
|
||||
add {
|
||||
lock (_default_settings_lock) {
|
||||
_request_clear_instance_settings += value;
|
||||
}
|
||||
}
|
||||
remove {
|
||||
lock (_default_settings_lock) {
|
||||
_request_clear_instance_settings -= value;
|
||||
}
|
||||
}
|
||||
}
|
||||
event Action _request_clear_instance_settings;
|
||||
|
||||
// indicates whether or not the settings have been loaded from the server (this happens once, on startup - see the constructor for details)
|
||||
public bool initialized { get; private set; }
|
||||
|
||||
public IEnumerable<UserSettingsTheme> themes =>
|
||||
_themes.Values;
|
||||
|
||||
internal UserSettings current {
|
||||
get {
|
||||
lock (_default_settings_lock)
|
||||
return _default_settings ?? UserSettings.factory_defaults;
|
||||
}
|
||||
}
|
||||
|
||||
/// Subscribing via _persist.subscribe may be cleaner in theory, however using a local event allows observers to be notified immediately.
|
||||
/// Assuming there aren't any rogue agents saving UserSettings by themselves, there should never be any discrepancies.
|
||||
public IObservable<UserSettings> subscribe_default_settings() => _default_settings_subject.AsObservable();
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_fills_settings_manager(GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_fills_migration_rules,
|
||||
_fills_propi);
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_mview_settings_manager(GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_mview_migration_rules,
|
||||
_mview_propi);
|
||||
|
||||
public IWindowSettingsManager<PNLSettings> create_pnl_settings_manager(PNLSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_pnl_migration_rules,
|
||||
_pnl_propi);
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_synth_settings_manager(GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_synth_migration_rules,
|
||||
_synth_propi);
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_reconcile_report_settings_manager(
|
||||
GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_reconcile_report_migration_rules,
|
||||
_reconcile_report_propi);
|
||||
|
||||
public async Task save_default(UserSettings value, bool clear_instance_settings = false) {
|
||||
// * Notify observers before persisting so the changes are reflected immediately in the ui.
|
||||
// * If instance settings are to be cleared, don't clear them until the new defaults are
|
||||
// ready. We want to avoid the case where windows w/ instance settings consume cpu
|
||||
// loading the original defaults, just to load the new defaults immediately after that.
|
||||
lock (_default_settings_lock) {
|
||||
Disposable.dispose(ref _theme_op);
|
||||
if (clear_instance_settings) {
|
||||
clear_instance_async().ignore();
|
||||
}
|
||||
_default_settings_subject.OnNext(_default_settings = value);
|
||||
}
|
||||
await _persist_async();
|
||||
|
||||
async Task clear_instance_async() {
|
||||
var cts = new CancellationTokenSource();
|
||||
_theme_op = Disposable.Create(() => cts.Cancel());
|
||||
var next = await _default_settings_subject.Skip(1).FirstOrDefaultAsync();
|
||||
if (!cts.IsCancellationRequested) {
|
||||
_request_clear_instance_settings?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IWindowSettingsManager<T> _create_settings_manager<T>(T instance_settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, IImmutableProperty<UserSettings, T> propi) where T : class {
|
||||
if (instance_settings != null && migration_rules.merge().apply(instance_settings, out var new_settings))
|
||||
instance_settings = new_settings;
|
||||
var wsm = new WindowSettingsManager<T>(this, instance_settings, propi.get, propi.set);
|
||||
lock (_default_settings_lock) {
|
||||
request_clear_instance_settings += clear_instance_settings;
|
||||
}
|
||||
return wsm;
|
||||
void clear_instance_settings() {
|
||||
wsm.instance_settings = null;
|
||||
}
|
||||
}
|
||||
|
||||
async Task _persist_async() {
|
||||
try {
|
||||
await _persist.set(NAME_SPACE, DEFAULT_KEY, _default_settings);
|
||||
} catch (Exception ex) {
|
||||
Log.Persist.warn($"Error persisting {typeof(UserSettings)} in namespace '{NAME_SPACE}': {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#nullable enable
|
||||
namespace Spark.Settings {
|
||||
using mbts.spark;
|
||||
|
||||
using System.Composition;
|
||||
|
||||
class UserSettingsTheme {
|
||||
public UserSettingsTheme() {
|
||||
name = string.Empty;
|
||||
value = new();
|
||||
}
|
||||
|
||||
public UserSettingsTheme(string name, UserSettings value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public string name { get; }
|
||||
public UserSettings value { get; }
|
||||
}
|
||||
|
||||
class ExportedThemes {
|
||||
[Export]
|
||||
public UserSettingsTheme light_theme =>
|
||||
new("Light", UserSettings.factory_defaults);
|
||||
|
||||
[Export]
|
||||
public UserSettingsTheme dark_theme =>
|
||||
new("Dark", UserSettings.factory_defaults.with(
|
||||
market_view: bts.shell.market_view.MarketViewSettings.dark_theme_grid_settings,
|
||||
time_and_sales: bts.shell.time_and_sales.TimeAndSalesSettings.dark_theme_grid_settings,
|
||||
fills: bts.shell.fills.FillsSettings.dark_theme_grid_settings,
|
||||
synthetic_management: bts.shell.SyntheticManagementSettings.dark_theme_grid_settings,
|
||||
ladder: LadderSettings.dark_theme));
|
||||
}
|
||||
}
|
||||
+77
-77
@@ -1,77 +1,77 @@
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.ui.shell {
|
||||
public enum DockState {
|
||||
FLOATING = 1,
|
||||
TOP = 2,
|
||||
BOTTOM = 3,
|
||||
LEFT = 4,
|
||||
RIGHT = 5,
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class WindowPosition {
|
||||
[ProtoMember(3)]
|
||||
public int width;
|
||||
[ProtoMember(4)]
|
||||
public int height;
|
||||
[ProtoMember(5)]
|
||||
public int left;
|
||||
[ProtoMember(6)]
|
||||
public int top;
|
||||
[ProtoMember(7)]
|
||||
public bool is_maximized;
|
||||
[ProtoMember(8)]
|
||||
public string host_id;
|
||||
[ProtoMember(9)]
|
||||
public int tab_index;
|
||||
|
||||
[ProtoMember(100), DefaultValue(false)]
|
||||
public bool horizontal = false;
|
||||
[ProtoMember(101), DefaultValue(true)]
|
||||
public bool show_text = true;
|
||||
[ProtoMember(102), DefaultValue(true)]
|
||||
public bool always_on_top = true;
|
||||
//[ProtoMember(103)] //used
|
||||
|
||||
[ProtoMember(104)]
|
||||
public string custom_title;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class WindowState {
|
||||
[ProtoMember(1)]
|
||||
public string owner;
|
||||
[ProtoMember(2)]
|
||||
public byte[] app_data;
|
||||
|
||||
//optional bool is_open = 3 [default = true];
|
||||
//optional bool is_favorite = 4;
|
||||
//optional string title = 5;
|
||||
|
||||
//optional string id = 6;
|
||||
// optional bool is_loaded = 7 [default = false];
|
||||
[ProtoMember(8)]
|
||||
public WindowPosition position;
|
||||
|
||||
[ProtoMember(9)]
|
||||
public string title; // for debugging
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class ModuleState {
|
||||
[ProtoMember(1)]
|
||||
public string name;
|
||||
[ProtoMember(2)]
|
||||
public byte[] app_data;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
using ProtoBuf;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.ui.shell {
|
||||
public enum DockState {
|
||||
FLOATING = 1,
|
||||
TOP = 2,
|
||||
BOTTOM = 3,
|
||||
LEFT = 4,
|
||||
RIGHT = 5,
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class WindowPosition {
|
||||
[ProtoMember(3)]
|
||||
public int width;
|
||||
[ProtoMember(4)]
|
||||
public int height;
|
||||
[ProtoMember(5)]
|
||||
public int left;
|
||||
[ProtoMember(6)]
|
||||
public int top;
|
||||
[ProtoMember(7)]
|
||||
public bool is_maximized;
|
||||
[ProtoMember(8)]
|
||||
public string host_id;
|
||||
[ProtoMember(9)]
|
||||
public int tab_index;
|
||||
|
||||
[ProtoMember(100), DefaultValue(false)]
|
||||
public bool horizontal = false;
|
||||
[ProtoMember(101), DefaultValue(true)]
|
||||
public bool show_text = true;
|
||||
[ProtoMember(102), DefaultValue(true)]
|
||||
public bool always_on_top = true;
|
||||
//[ProtoMember(103)] //used
|
||||
|
||||
[ProtoMember(104)]
|
||||
public string custom_title;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class WindowState {
|
||||
[ProtoMember(1)]
|
||||
public string owner;
|
||||
[ProtoMember(2)]
|
||||
public byte[] app_data;
|
||||
|
||||
//optional bool is_open = 3 [default = true];
|
||||
//optional bool is_favorite = 4;
|
||||
//optional string title = 5;
|
||||
|
||||
//optional string id = 6;
|
||||
// optional bool is_loaded = 7 [default = false];
|
||||
[ProtoMember(8)]
|
||||
public WindowPosition position;
|
||||
|
||||
[ProtoMember(9)]
|
||||
public string title; // for debugging
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class ModuleState {
|
||||
[ProtoMember(1)]
|
||||
public string name;
|
||||
[ProtoMember(2)]
|
||||
public byte[] app_data;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#nullable enable
|
||||
namespace Spark.Model {
|
||||
using bts.api;
|
||||
using bts.spark;
|
||||
|
||||
using mbts.ui;
|
||||
|
||||
using Spark.ViewModel.Commands;
|
||||
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
class SparkMenuItemToggleModel : SparkCommand, ISelectable {
|
||||
readonly IToggleCommand _cmd;
|
||||
|
||||
public SparkMenuItemToggleModel(
|
||||
string? title,
|
||||
ImageSource? icon = null,
|
||||
bool show_in_toolbar = true,
|
||||
bool show_in_context_menu = true,
|
||||
KeyGesture? key_gesture = null,
|
||||
IToggleCommand? cmd = null)
|
||||
: base(title, icon, show_in_toolbar, show_in_context_menu, key_gesture) =>
|
||||
_cmd = cmd ?? new ToggleCommand();
|
||||
|
||||
public event Action<bool>? is_selected_changed {
|
||||
add => _cmd.is_selected_changed += value;
|
||||
remove => _cmd.is_selected_changed -= value;
|
||||
}
|
||||
|
||||
public bool is_selected {
|
||||
get => _cmd.is_selected;
|
||||
set => _cmd.is_selected = value;
|
||||
}
|
||||
|
||||
public override void execute(IBus bus) =>
|
||||
is_selected = !is_selected;
|
||||
}
|
||||
}
|
||||
+25
-25
@@ -1,25 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace bts.api.orderman {
|
||||
public readonly struct FillSecurity : IEquatable<FillSecurity> {
|
||||
public FillSecurity(SecurityImmutable? sec, FillImmutable fill) {
|
||||
this.sec = sec;
|
||||
this.fill = fill;
|
||||
}
|
||||
public FillSecurity((SecurityImmutable? sec, FillImmutable fill) t) {
|
||||
sec = t.sec;
|
||||
fill = t.fill;
|
||||
}
|
||||
public static implicit operator FillSecurity(ValueTuple<SecurityImmutable?, FillImmutable> x) => new FillSecurity(x);
|
||||
public SecurityImmutable? sec { get; }
|
||||
public FillImmutable fill { get; }
|
||||
|
||||
public override bool Equals(object obj) => (obj is FillSecurity os) && Equals(os);
|
||||
public bool Equals(FillSecurity other) => fill.FillKey == other.fill.FillKey && sec == other.sec;
|
||||
public override int GetHashCode() => (sec, fill).GetHashCode();
|
||||
|
||||
public static bool operator ==(FillSecurity left, FillSecurity right) => left.Equals(right);
|
||||
public static bool operator !=(FillSecurity left, FillSecurity right) => !left.Equals(right);
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace bts.api.orderman {
|
||||
public readonly struct FillSecurity : IEquatable<FillSecurity> {
|
||||
public FillSecurity(SecurityImmutable? sec, FillImmutable fill) {
|
||||
this.sec = sec;
|
||||
this.fill = fill;
|
||||
}
|
||||
public FillSecurity((SecurityImmutable? sec, FillImmutable fill) t) {
|
||||
sec = t.sec;
|
||||
fill = t.fill;
|
||||
}
|
||||
public static implicit operator FillSecurity(ValueTuple<SecurityImmutable?, FillImmutable> x) => new FillSecurity(x);
|
||||
public SecurityImmutable? sec { get; }
|
||||
public FillImmutable fill { get; }
|
||||
|
||||
public override bool Equals(object obj) => (obj is FillSecurity os) && Equals(os);
|
||||
public bool Equals(FillSecurity other) => fill.FillKey == other.fill.FillKey && sec == other.sec;
|
||||
public override int GetHashCode() => (sec, fill).GetHashCode();
|
||||
|
||||
public static bool operator ==(FillSecurity left, FillSecurity right) => left.Equals(right);
|
||||
public static bool operator !=(FillSecurity left, FillSecurity right) => !left.Equals(right);
|
||||
|
||||
}
|
||||
}
|
||||
+101
-92
@@ -1,92 +1,101 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
|
||||
using System;
|
||||
|
||||
using static bts.api.orderman.Fill.Types;
|
||||
|
||||
namespace mbts.spark {
|
||||
class FillTuple : IEquatable<FillTuple> {
|
||||
public static readonly Func<FillTuple, string> key_fn = ft => ft.fill.FillKey;
|
||||
public readonly FillImmutable fill;
|
||||
public readonly SecurityImmutable? security;
|
||||
|
||||
OrderSecurityRootPair? _order_tuple;
|
||||
|
||||
public FillTuple(FillImmutable fill, SecurityImmutable? security, OrderSecurityRootPair? order_tuple) {
|
||||
this.fill = fill;
|
||||
this.security = security;
|
||||
_order_tuple = order_tuple;
|
||||
}
|
||||
|
||||
public FillTuple((SecurityImmutable? sec, FillImmutable val) fill, OrderSecurity? order_tuple)
|
||||
: this(fill.val, fill.sec, order_tuple.HasValue ? OrderSecurityRootPair.from_order_security(order_tuple.Value) : (OrderSecurityRootPair?)null) { }
|
||||
|
||||
public AlgoData? algo =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.order.algo : (AlgoData?)null;
|
||||
|
||||
public AlgoDefinitionMetadata? algo_meta =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.order.meta : null;
|
||||
|
||||
public OrderRecordImmutable? order =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.order.order : (OrderRecordImmutable?)null;
|
||||
|
||||
public AlgoData? root_algo =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.root.algo : (AlgoData?)null;
|
||||
|
||||
public AlgoDefinitionMetadata? root_algo_meta =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.root.meta : null;
|
||||
|
||||
public OrderSecurityAlgo? root_order_security => _order_tuple?.root;
|
||||
|
||||
public string account {
|
||||
get {
|
||||
var account_string = string.Empty;
|
||||
if (fill.FillKind != FillKind.Sod && order.HasValue && order.Value.HasDef) account_string = order.Value.Def.Account;
|
||||
else account_string = fill.Account;
|
||||
return account_string == null ? string.Empty : account_string;
|
||||
}
|
||||
}
|
||||
|
||||
public uint account_id =>
|
||||
fill.FillKind != FillKind.Sod && order.HasValue ?
|
||||
order.Value.account_id() :
|
||||
fill.InternalAccountId;
|
||||
|
||||
public bool Equals(FillTuple other) =>
|
||||
other != null &&
|
||||
_order_tuple.Equals(other._order_tuple) &&
|
||||
security.HasValue == other.security.HasValue &&
|
||||
fill.Equals(other.fill);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as FillTuple);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
fill.FillKey.GetHashCode(),
|
||||
order.HasValue ? order.Value.Key.GetHashCode() : 0,
|
||||
security.HasValue ? security.Value.Id.GetHashCode() : 0);
|
||||
}
|
||||
|
||||
static class FillTupleExtensions {
|
||||
public static DateTime fill_time(this FillTuple fill_tuple) =>
|
||||
fill_tuple.fill.Timestamp.to_local_time();
|
||||
|
||||
public static TimeInForce time_in_force(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order.HasValue ? fill_tuple.order.Value.time_in_force() : TimeInForce.Undefined;
|
||||
|
||||
public static CustomOrderDefinition.CustomOneofCase order_type(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order.HasValue ? fill_tuple.order.Value.order_type() : CustomOrderDefinition.CustomOneofCase.None;
|
||||
|
||||
public static Venue exchange(this FillTuple fill_tuple) =>
|
||||
fill_tuple.security.HasValue ? fill_tuple.security.Value.Exchange : Venue.Unset;
|
||||
|
||||
public static uint order_key(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order.HasValue ? fill_tuple.order.Value.Key : 0u;
|
||||
|
||||
public static string user(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order.HasValue ? fill_tuple.order.Value.user() : string.Empty;
|
||||
}
|
||||
}
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
|
||||
using System;
|
||||
|
||||
using static bts.api.orderman.Fill.Types;
|
||||
|
||||
namespace mbts.spark {
|
||||
class FillTuple : IEquatable<FillTuple> {
|
||||
public static readonly Func<FillTuple, string> key_fn = ft => ft.fill.FillKey;
|
||||
public readonly FillImmutable fill;
|
||||
public readonly SecurityImmutable? security;
|
||||
|
||||
OrderSecurityRootPair? _order_tuple;
|
||||
|
||||
public FillTuple(FillImmutable fill, SecurityImmutable? security, OrderSecurityRootPair? order_tuple) {
|
||||
this.fill = fill;
|
||||
this.security = security;
|
||||
_order_tuple = order_tuple;
|
||||
}
|
||||
|
||||
public FillTuple((SecurityImmutable? sec, FillImmutable val) fill, OrderSecurity? order_tuple)
|
||||
: this(fill.val, fill.sec, order_tuple.HasValue ? OrderSecurityRootPair.from_order_security(order_tuple.Value) : (OrderSecurityRootPair?)null) { }
|
||||
|
||||
public AlgoData? algo =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.order.algo : (AlgoData?)null;
|
||||
|
||||
public AlgoDefinitionMetadata? algo_meta =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.order.meta : null;
|
||||
|
||||
public OrderRecordImmutable? order =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.order.order : (OrderRecordImmutable?)null;
|
||||
|
||||
public AlgoData? root_algo =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.root.algo : (AlgoData?)null;
|
||||
|
||||
public AlgoDefinitionMetadata? root_algo_meta =>
|
||||
_order_tuple.HasValue ? _order_tuple.Value.root.meta : null;
|
||||
|
||||
public OrderSecurityAlgo? root_order_security => _order_tuple?.root;
|
||||
|
||||
public AccountTuple account_tuple => new(account_id, account);
|
||||
|
||||
public string account {
|
||||
get {
|
||||
var account_string = string.Empty;
|
||||
if (fill.FillKind != FillKind.Sod && order.HasValue && order.Value.HasDef) account_string = order.Value.Def.Account;
|
||||
else account_string = fill.Account;
|
||||
return account_string == null ? string.Empty : account_string;
|
||||
}
|
||||
}
|
||||
|
||||
public uint account_id =>
|
||||
fill.FillKind != FillKind.Sod && order.HasValue ?
|
||||
order.Value.account_id() :
|
||||
fill.InternalAccountId;
|
||||
|
||||
public TimeInForce time_in_force =>
|
||||
order?.time_in_force() ?? TimeInForce.Undefined;
|
||||
|
||||
public bool Equals(FillTuple other) =>
|
||||
other != null &&
|
||||
_order_tuple.Equals(other._order_tuple) &&
|
||||
security.HasValue == other.security.HasValue &&
|
||||
fill.Equals(other.fill);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as FillTuple);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
fill.FillKey.GetHashCode(),
|
||||
order.HasValue ? order.Value.Key.GetHashCode() : 0,
|
||||
security.HasValue ? security.Value.Id.GetHashCode() : 0);
|
||||
}
|
||||
|
||||
static class FillTupleExtensions {
|
||||
public static DateTime fill_time(this FillTuple fill_tuple) =>
|
||||
fill_tuple.fill.Timestamp.to_local_time();
|
||||
|
||||
public static TimeInForce time_in_force(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order.HasValue ? fill_tuple.order.Value.time_in_force() : TimeInForce.Undefined;
|
||||
|
||||
public static CustomOrderDefinition.CustomOneofCase order_type(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order.HasValue ? fill_tuple.order.Value.order_type() : CustomOrderDefinition.CustomOneofCase.None;
|
||||
|
||||
public static Venue exchange(this FillTuple fill_tuple) =>
|
||||
fill_tuple.security.HasValue ? fill_tuple.security.Value.Exchange : Venue.Unset;
|
||||
|
||||
public static uint order_key(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order.HasValue ? fill_tuple.order.Value.Key : 0u;
|
||||
|
||||
public static string user(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order.HasValue ? fill_tuple.order.Value.user() : string.Empty;
|
||||
|
||||
public static uint user_id(this FillTuple fill_tuple) =>
|
||||
fill_tuple.order?.user_id() ?? 0;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 895 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user