Compare commits

..
569 Commits
Author SHA1 Message Date
Kevin Darby 704819c3e3 min cost of a tick 2020-02-24 09:48:42 -05:00
Kevin Darby 526a0a0a29 need a better variance 2020-02-21 16:44:52 -05:00
Kevin Darby 759ba55551 build 2020-02-20 21:58:49 -05:00
Kevin Darby 4ef82cc4ab aggressive vs passive 2020-02-20 21:42:57 -05:00
Kevin Darby a9834e6df8 unsaved vwap changes going home because of snow 2020-02-20 16:31:44 -05:00
Kevin Darby f2d592f9c7 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2020-02-19 18:18:00 -05:00
Kevin Darby 26fda7f88f arrival price 2020-02-19 18:17:44 -05:00
Jake Norbut 9864d0b0fc using enums instead of magic numbers; OrderID to NONE on cancel(replace) rejects 2020-02-19 16:36:08 -06:00
Jake Norbut 59692852b5 New, Cancel-Replace, Cancel client sample FIX sequence 2020-02-19 16:33:49 -06:00
Jake Norbut ed3bc9f71c Cancel and Cancel-Replace shoud use OrigClOrdID to lookup bts key 2020-02-19 16:32:50 -06:00
Jake Norbut a2c7481301 populate new ExternalOrderId field with ClOrdID 2020-02-19 16:30:48 -06:00
Jake Norbut f1fdaf1aca dict for external id vs bts key 2020-02-19 16:28:35 -06:00
Kevin Darby bf34963b88 switch fix to use new streaming marketdata (securities only) 2020-02-19 14:46:39 -05:00
Kevin Darby ed1609a89f fixing casing in MarketDataService.proto 2020-02-19 14:28:03 -05:00
Kevin Darby c66202117f Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2020-02-19 14:22:40 -05:00
Kevin Darby 49342e30dd Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2020-02-19 14:18:59 -05:00
Kevin Darby 20b8d095d8 historical data looking ok 2020-02-19 14:18:11 -05:00
Kevin Darby 995b4cbefe added external_order_id to our order def for echo back in order state to facilitate incoming FIX 2020-02-19 10:48:57 -05:00
Kevin Darby fb8c603b18 added cache to the server, seems like somehow do_before_launch is getting called twice 2020-02-18 18:15:35 -05:00
Kevin Darby 743b606030 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2020-02-18 17:07:32 -05:00
Kevin Darby 4950796660 adding logs 2020-02-18 17:06:51 -05:00
Kevin Darby 417fccb03c utc / local errors in sparkcmd 2020-02-18 16:53:07 -05:00
Jake Norbut 3014ceb85f fixing malformed cancel-reject message 2020-02-18 15:02:07 -06:00
Kevin Darby c1efbc00a5 arrival shaping up 2020-02-17 18:18:20 -05:00
Kevin Darby b8851f36bb getting these algos back in my head 2020-02-17 16:48:59 -05:00
Kevin Darby 9de3d80891 getting TradingDistribution data into algo 2020-02-13 18:37:31 -05:00
Kevin Darby f07fd7044f price / volume distribution looking good 2020-02-12 17:30:19 -05:00
Kevin Darby f025eed845 hooking excel to historical_query for testing 2020-02-11 18:10:01 -05:00
Eric Harding cb0dfa50b3 Fix dockerfile 2020-02-11 15:02:41 -05:00
Eric Harding 2429e6019c Merge branch 'master' of bitbucket.org:bluetradesys/spark into prod 2020-02-11 14:16:14 -05:00
Eric Harding e0bb48b0c7 Merged in client_sdk (pull request #397)
Client sdk
2020-02-11 18:31:09 +00:00
Eric Harding a38b0dbc79 Merge branch 'master' of bitbucket.org:bluetradesys/spark into client_sdk 2020-02-11 13:29:43 -05:00
Eric Harding 73065ab48a client sdk sample 2020-02-11 13:29:09 -05:00
Eric Harding bd07c96504 Merged in mdsource_crash (pull request #389)
Fix for assert when algo subs to security w/o MD
2020-02-11 17:11:07 +00:00
Eric Harding dc4033875a Fixes from code review feedback 2020-02-11 11:43:04 -05:00
Eric Harding 130794c538 Merge fixup 2020-02-11 11:22:02 -05:00
Eric Harding b1743f0b1c Uninitialized heap, ffs 2020-02-10 18:58:21 -05:00
Eric Harding 33492c9131 Fixes for _WIN32 2020-02-10 18:27:41 -05:00
Eric Harding d434778a82 MarketDataService 2020-02-10 17:06:13 -06:00
Eric Harding 4c6cc9b3e0 Merge branch 'client_sdk' of bitbucket.org:bluetradesys/spark into client_sdk 2020-02-10 12:32:18 -06:00
Eric Harding 6aaeddb7e9 update vscode for linux on linux development 2020-02-10 12:32:10 -06:00
Eric Harding 1685a13994 Add linux on linux build task (vscode) 2020-02-10 13:24:56 -05:00
Eric Harding 54633dd6db note correct config value for local sparkd 2020-02-10 13:21:03 -05:00
Eric Harding 510f163e5b Add to_string methods for Product for new md api 2020-02-10 12:50:05 -05:00
Eric Harding ee4779da73 MarketDataService 2020-02-09 22:39:01 -05:00
Eric Harding 28e98259f6 Add client sdk example 2020-02-07 22:20:42 -05:00
Eric Harding 82bc402216 Update client examples 2020-02-05 14:58:46 -05:00
Eric Harding ff70e9694b Set spark version from jenkins environment when available 2020-02-05 10:02:17 -05:00
Kevin Darby 6a8930f594 fixed a warning, factored interface 2020-02-04 14:52:58 -05:00
Kevin Darby e49e11feb5 historical data service 2020-02-03 21:45:37 -05:00
Kevin Darby a6d4a05253 filling out volume and price distributions 2020-01-31 18:19:50 -05:00
Kevin Darby db0ff984cb new externals 2020-01-31 10:37:42 -05:00
Kevin Darby 770c54ee88 working on volume distribution 2020-01-30 18:17:46 -05:00
Kevin Darby 7ac87269c2 making my own trades files from mbo data; the exchange supplied stuff doesn't do spreads correctly 2020-01-30 17:14:07 -05:00
Kevin Darby 3a2a52d708 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2020-01-30 09:45:25 -05:00
Jake Norbut 525d0eda30 fix typo in notes 2020-01-30 07:39:22 -06:00
Jake Norbut e9e85b44c9 updating release notes 2020-01-30 07:37:51 -06:00
John Colvin 2654b2e446 SP-283 Ladder does not display 6c spreads correctly 2020-01-29 12:43:01 -06:00
Eric Harding be45938b51 Fix for assert when algo subs to security w/o MD 2020-01-22 14:08:26 -05:00
Kevin Darby 7119e18436 Merge branch 'master' of bitbucket.org:bluetradesys/spark into dotnet 2020-01-21 10:24:48 -05:00
Eric Harding 663f9ecc52 Merge branch 'master' of bitbucket.org:bluetradesys/spark into client_sdk 2020-01-16 17:23:42 -05:00
Kevin Darby a7b8d39787 getting twap back working 2020-01-15 18:41:31 -05:00
Kevin Darby ce88ebcebe som setup screen shots 2020-01-15 12:16:45 -05:00
Eric Harding a6edbbe7b5 Fix spark sdk build 2020-01-14 17:13:07 -05:00
Kevin Darby 1b8cca55e5 fake spreader is probably a little more trouble than worth 2020-01-14 17:10:17 -05:00
Kevin Darby a8f26e998b corn notebook 2020-01-14 14:51:58 -05:00
Kevin Darby 55eb830c7c upstream fix parser changes into historical stuff 2020-01-14 14:30:54 -05:00
Kevin Darby 0230ac06bd Merge branch 'cqg' of bitbucket.org:bluetradesys/spark into vwap 2020-01-14 14:04:50 -05:00
Kevin Darby 83005d7439 algos over fix 2020-01-14 13:28:53 -05:00
Kevin Darby a74da85f18 Merge branch 'cqg' of bitbucket.org:bluetradesys/spark into vwap 2020-01-13 15:54:15 -05:00
Kevin Darby 4872e8ad7f create, update, delete limit orders works 2020-01-13 14:57:11 -05:00
Eric Harding 51276d161c Revert "Add diagnostic logs to SharedMemory open/close"
This reverts commit 3fa699b4c9 because of
log spam
2020-01-13 11:39:51 -05:00
Eric Harding 3fa699b4c9 Add diagnostic logs to SharedMemory open/close 2020-01-13 11:13:15 -05:00
Eric Harding c9396bd6af Stabilize spark_vm_shell.sh ssk keys 2020-01-13 11:13:00 -05:00
Kevin Darby 065f92937c added note about node 2020-01-11 16:39:01 -05:00
Kevin Darby cdfb60f5d9 native orders going through 2020-01-10 18:14:28 -05:00
Kevin Darby b80f96fa5e Merged in ice_excel (pull request #388)
fix RTD crash for ice

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2020-01-10 22:39:27 +00:00
Kevin Darby b7dea6817c fix RTD crash for ice 2020-01-10 14:43:06 -05:00
Kevin Darby f2b407fdac json file for algos 2020-01-10 13:51:12 -05:00
Pedro Pinto f4dc550f1a Fixed slowness in FIX log parsing.
This slowness was actually causing us to take too long to complete the
FIX connection logon process, resulting in a timeout and failure to
establish the session. It took almost 1ms per log line, with
logs commonly having 20K lines. k
2020-01-10 08:35:33 -05:00
Pedro Pinto bc978ed065 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2020-01-10 08:30:17 -05:00
Kevin Darby 4c4fa77d3a algo structure in a file 2020-01-09 22:47:00 -05:00
Kevin Darby 2aa5a02ce2 putting composites in a file 2020-01-09 18:13:20 -05:00
Kevin Darby 5e1f642076 algo types 2020-01-09 17:18:56 -05:00
Kevin Darby 90b4c7e26f starting cqg integration 2020-01-09 15:50:59 -05:00
Kevin Darby eccc3f5f38 fix merge error 2020-01-09 10:48:34 -05:00
Eric Harding 202c5faffd update devcontainer to allow 2g shm 2020-01-09 10:40:47 -05:00
Kevin Darby de20957ae7 change comment 2020-01-09 09:53:58 -05:00
Kevin Darby e410322fcd Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2020-01-08 23:41:57 -05:00
Kevin Darby 85cb481849 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2020-01-08 23:41:23 -05:00
Kevin Darby f7dec1161a build 2020-01-08 18:09:53 -05:00
Kevin Darby f5e826b095 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into cqg 2020-01-08 18:05:25 -05:00
Kevin Darby 4cd3bc40d9 note about not sharing externals 2020-01-08 18:02:00 -05:00
Kevin Darby 3800f3344b netcore 3.1 2020-01-08 17:35:19 -05:00
Kevin Darby 6ad139804f Merge branch 'windows_docker' of bitbucket.org:bluetradesys/spark into dotnet 2020-01-08 17:33:28 -05:00
Kevin Darby 45d5c2d7cd docker / externals 2020-01-08 17:32:19 -05:00
Kevin Darby 0d24062f03 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into windows_docker 2020-01-08 17:25:30 -05:00
Kevin Darby 44e0eae281 Merge branch 'master' of bitbucket.org:bluetradesys/spark into dotnet 2020-01-08 17:24:55 -05:00
Kevin Darby 25805cf675 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2020-01-08 17:22:14 -05:00
Kevin Darby 152ab05b4e remove externals sharing 2020-01-08 17:08:49 -05:00
kevin b7a733a306 fixed_ext 2020-01-08 14:00:16 -06:00
Kevin Darby 11d7291acb Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2020-01-08 14:12:54 -05:00
Kevin Darby d17b643e31 thought better of this assertion 2020-01-08 14:05:20 -05:00
Kevin Darby 8832e97f52 update docker 2020-01-08 12:11:51 -05:00
Kevin Darby c971d07958 MES_ES looking ok 2020-01-07 18:10:30 -05:00
Kevin Darby 55bb13e536 TED spread seems to work... 2020-01-07 17:18:29 -05:00
Kevin Darby f7c37c0a61 more TED 2020-01-07 15:33:37 -05:00
Kevin Darby f742364e46 MES vs ES and TED spread 2020-01-06 18:24:15 -05:00
Kevin Darby 3b6eef465a ted spread 2020-01-06 18:20:28 -05:00
Kevin Darby a565dcbab7 fix tests 2020-01-06 14:30:04 -05:00
Kevin Darby 3d93038bc6 build trouble 2020-01-05 22:01:47 -05:00
Kevin Darby 8d3b4f4957 somehow onix ice external sym links got delete 2020-01-05 21:24:54 -05:00
Kevin Darby d1764997d3 fix parse bug for scientific notation 2020-01-05 21:24:48 -05:00
Kevin Darby 2cf9974b73 for ted need to multiply quote leg 2020-01-03 18:51:46 -05:00
Kevin Darby 7f41cb6ca1 ted notebook 2020-01-03 18:46:58 -05:00
Kevin Darby 8c408dd06e moved screenshots 2020-01-03 18:26:44 -05:00
Kevin Darby 40aad212db screenshots 2020-01-03 18:25:27 -05:00
Kevin Darby 872c42d12c fix dollar pnl 2020-01-03 18:03:56 -05:00
Kevin Darby c80cdd8a52 added dollars to pnl output and lean_factor to FakeSpreader 2020-01-03 17:26:28 -05:00
Kevin Darby ffcefa06aa gc nb 2020-01-02 18:33:13 -05:00
Kevin Darby c038bf0f89 add price factor for gc/mgc spreader 2020-01-02 18:23:20 -05:00
Kevin Darby f93dd9f307 clean up log 2020-01-02 15:36:29 -05:00
Kevin Darby 1939c9c3c0 bug in stacker for 3 length 2020-01-02 15:34:27 -05:00
Kevin Darby 68b37ccfc8 added order stacker, had to change algo creation a bit to facilitate 2020-01-02 14:37:26 -05:00
Pedro Pinto 65d55a7618 Added a parser of historical MD to facilitate analysis. 2020-01-02 13:57:54 -05:00
Pedro Pinto 57c3ec34c2 Fixed clang build. 2020-01-02 13:55:49 -05:00
Kevin Darby 4e85218d02 update dev keycodes to 3/31/2020 2020-01-02 10:12:18 -05:00
Kevin Darby c5d77d4a70 corrected merge issue 2020-01-01 22:50:57 -05:00
Kevin Darby cbec2476be stacker 2020-01-01 18:13:57 -05:00
Kevin Darby 9fd3563ef8 stacker 2020-01-01 18:11:18 -05:00
Kevin Darby b66d7b50f5 changes in roll_query notebook 2020-01-01 16:18:45 -05:00
Kevin Darby fcf6ed5a96 added a get_done() function so we can get pnl for strategies that arent' done executing in our query time 2020-01-01 13:44:48 -05:00
Kevin Darby ae0aa8be2a looks like we could make a little money on roll 2019-12-31 17:31:22 -05:00
Kevin Darby 11ee1357d4 compute algo_pnl 2019-12-31 15:38:26 -05:00
Kevin Darby 937fcf9fb5 added some diagnostics, seems like we can get filled on roll 2019-12-31 15:23:10 -05:00
Kevin Darby 7601fb0a7d Merge branch 'windows_docker' of bitbucket.org:bluetradesys/spark into vwap 2019-12-31 12:40:29 -05:00
Kevin Darby 4257a172c1 fixed shared obj dir on linux build 2019-12-31 12:39:54 -05:00
Kevin Darby e38b173a0e added spark_vm_shell for windows
# Conflicts:
#	src/client/spark_historical/Notebooks/roll_cover.ipynb
2019-12-31 12:39:24 -05:00
Kevin Darby 8afeedc488 fill model logging 2019-12-31 12:14:37 -05:00
Kevin Darby f43bf20a54 Merge branch 'windows_docker' of bitbucket.org:bluetradesys/spark into vwap 2019-12-31 11:16:06 -05:00
Kevin Darby 721a08c4b4 remove these CMakeDeps so gitignore can ignore 2019-12-31 10:44:32 -05:00
Kevin Darby 41a3535e84 made some minor changes to support running a dev environment in our api docker on windows 2019-12-31 10:07:55 -05:00
Kevin Darby 626629bf06 added spark_vm_shell for windows 2019-12-31 10:06:54 -05:00
Kevin Darby 6ec2ee2182 added spark_vm_shell for windows 2019-12-30 12:52:24 -05:00
Kevin Darby 8c89a91164 fix bug in k fill model, change setup_notes to reflect .netcore 3.1 2019-12-30 12:10:08 -05:00
Kevin Darby 58c05b301a KFillModel2, heuristic 2019-12-26 23:07:17 -05:00
Kevin Darby f5e2a3c28f debugging KFillModel, realizing I don't have all the info here 2019-12-24 00:17:40 -05:00
Kevin Darby 0fd45ba92a . 2019-12-23 00:15:10 -05:00
Kevin Darby 4e440fc218 debugging KFillModel 2019-12-22 16:46:06 -05:00
Kevin Darby 41c8351709 ready to test KFillModel 2019-12-22 15:14:38 -05:00
Kevin Darby 1238648c2b leveling applies to those orders that did not receive prorata 2019-12-20 23:43:34 -05:00
Kevin Darby 03ac88683f KFillModel looks to be code complete, needs debug / test 2019-12-20 23:37:39 -05:00
Taha Afzal f6784f1196 Merged in SP-253-spread-trading-needs-future-posit (pull request #386)
pos neutral spreads's fuures are not checked for pos limits
2019-12-20 23:38:00 +00:00
Kevin Darby fe4c057b20 working on KFillModel 2019-12-20 18:18:40 -05:00
Taha Afzal 7f05a2c2af pos neutral spreads's fuures are not checked for pos limits 2019-12-20 16:35:29 -05:00
John Colvin c3d2101dcd infinity ticks be gone 2019-12-20 14:12:50 -06:00
John Colvin 8855ea44b0 remove ugly border around expander buttons in grid settings dialog 2019-12-20 13:19:36 -06:00
Kevin Darby 51f70ad5ed Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2019-12-20 10:12:35 -05:00
Kevin Darby e6c6997e46 prorata start 2019-12-19 23:47:41 -05:00
Kevin Darby ddd8778bab added some comments for taha, some display stuff for notebook 2019-12-19 18:09:02 -05:00
Kevin Darby f95d258210 put real life data with notebooks 2019-12-19 17:13:00 -05:00
Kevin Darby 5aa3f1a684 downgrade log 2019-12-19 16:06:26 -05:00
Kevin Darby a2e1cf8dd5 moving code around 2019-12-19 15:54:37 -05:00
Kevin Darby 0b8b68e6e6 Merge branch 'csv_export' of bitbucket.org:bluetradesys/spark into vwap 2019-12-19 14:48:38 -05:00
Pedro Pinto 5cd3a710bd Skipped lines that do no match a FIX prefix in FIX logs.
This is needed at the ICE where some messages can have new lines. We
use the new line separator to separate FIX messages so the parser was
getting confused when it saw multi-line messages.
2019-12-19 14:04:34 -05:00
John Colvin 6d8b4636ab fix synthetic import qty & side 2019-12-19 11:11:07 -06:00
Jake Norbut 64a75c4b48 updating release notes 2019-12-19 10:22:42 -06:00
Kevin Darby 8658a11d1f 100 ticks in one day...not enough 2019-12-19 11:20:58 -05:00
Pedro Pinto 143b6ce5d8 Merged in multi-line-FIX-messages (pull request #385)
Skipped lines that do no match a FIX prefix in FIX logs.

Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
2019-12-19 15:29:06 +00:00
Kevin Darby d0aa90131a top doesn't do much 2019-12-19 00:35:11 -05:00
Kevin Darby ffb30177b1 trying top 2019-12-19 00:24:57 -05:00
Kevin Darby 297093f9ce honing in 2019-12-18 23:41:31 -05:00
Kevin Darby 5ba8440877 couple small bugs and some diagnostics 2019-12-18 22:35:35 -05:00
Kevin Darby 228e49d152 taking this home 2019-12-18 18:28:39 -05:00
John Colvin aa5c0a5613 added create/clone synthetics test 2019-12-18 16:21:26 -06:00
Pedro Pinto d5fc0c5a56 Skipped lines that do no match a FIX prefix in FIX logs.
This is needed at the ICE where some messages can have new lines. We
use the new line separator to separate FIX messages so the parser was
getting confused when it saw multi-line messages.
2019-12-18 17:16:12 -05:00
Kevin Darby a9e983a717 we rollin 2019-12-18 15:32:19 -05:00
Jake Norbut 25445d58d0 refactoring TaS CSV export 2019-12-18 13:48:14 -06:00
Jake Norbut 0b313a8aa8 refactoring Fills CSV export 2019-12-18 13:47:36 -06:00
Jake Norbut 54f954fd4b adding CSV export to order book 2019-12-18 13:47:03 -06:00
Jake Norbut 980f38ba03 using OrderedDictionary to CSVs are output with a predictable column order 2019-12-18 13:46:22 -06:00
Kevin Darby 198086e639 got roll going 2019-12-18 13:16:49 -05:00
Jake Norbut 9eada2724b Having the Command be on the Window rather than the VM; nice thinking John 2019-12-18 10:11:10 -06:00
Jake Norbut b5f53ee918 adding csv_properties to TimeAndSalesVM.Row 2019-12-18 10:10:04 -06:00
Kevin Darby 7c4daa0f4b we are trying to log in for a deleted user 2019-12-18 10:32:26 -05:00
Taha Afzal ab430e4afb Merged in SP-272_IB_slowness (pull request #384)
Reports transition to end state right away (vs on a timer)
2019-12-18 15:24:14 +00:00
Taha Afzal 29f6ea6d48 Merged in SP-273-fills-highlighting (pull request #383)
SP-273 fills highlighting
2019-12-18 15:20:41 +00:00
Taha Afzal e46372d08e Merged in SP-271-synthetic-cloning (pull request #382)
SP-271 synthetic cloning
2019-12-18 15:18:48 +00:00
Taha Afzal 1d2faeba3e Merged in SP-270-piq-bug (pull request #381)
SP-270 Order Book - PIQ Bug (fix OrderSecurityAlgo.Equals impl)
2019-12-18 15:16:07 +00:00
Taha Afzal 23cffc0630 Merged synthetic-importer into master 2019-12-18 10:03:45 -05:00
Jake Norbut 90b597fc70 Merged in SP-267_ladder_message (pull request #380)
SP-267 ladder status message visibility
2019-12-18 14:57:34 +00:00
Kevin Darby a3a8eb14ad trying another way 2019-12-18 00:07:34 -05:00
Kevin Darby 333acf0822 progress 2019-12-17 18:17:45 -05:00
Jake Norbut efc175f654 adding export to csv command to Fills window 2019-12-17 10:32:58 -06:00
Jake Norbut 6074afd999 adding csv export command to fills vm 2019-12-17 10:32:15 -06:00
Jake Norbut 219dff4055 adding new export_csv IDialogCommand which just creates a new file 2019-12-17 10:28:57 -06:00
Jake Norbut 9a3b07b622 adding ToCsv IEnumerable ext method in a new util namespace 2019-12-17 10:21:38 -06:00
Kevin Darby 1af49376b6 progress in passive fills model 2019-12-16 23:42:58 -05:00
Kevin Darby f90f6cfb33 using sortedlist for our orders in fifo 2019-12-16 23:27:29 -05:00
Kevin Darby 6433f9ba98 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-12-16 21:39:53 -05:00
Kevin Darby cc1882ea1a trying to add iceberg reup to passive fill stream 2019-12-16 18:46:30 -05:00
Kevin Darby 56e83a1e69 fix timestamp bug 2019-12-16 15:17:59 -05:00
Taha Afzal 5c01e6a99a Reports transition to end state right away (vs on a timer) 2019-12-16 13:46:26 -05:00
Eric Harding 487aaef02f update cmake 2019-12-16 13:12:50 -05:00
Eric Harding f22fc03e56 merge master 2019-12-16 12:54:19 -05:00
Kevin Darby 69d429fa4e nb 2019-12-15 23:08:36 -05:00
Eric Harding c6cd5fcf93 services.md 2019-12-15 22:09:20 -05:00
Kevin Darby e8c9e70a4a change name of methods 2019-12-15 21:30:22 -05:00
Kevin Darby 6c78850902 need to deduct from our orders in fifo model 2019-12-15 14:24:14 -05:00
Kevin Darby adab377e44 got some passive fills, need to verify 2019-12-13 23:09:31 -05:00
Kevin Darby 7bdaa35768 passive fifo fill model 2019-12-13 22:14:02 -05:00
Kevin Darby 1c7f79f293 icomparer 2019-12-13 18:18:47 -05:00
Kevin Darby 3a7246c5b0 time to leave 2019-12-13 18:16:59 -05:00
Kevin Darby 3bd95de839 bugs 2019-12-13 18:14:23 -05:00
Kevin Darby 576d3836b3 fills by query 2019-12-13 18:05:41 -05:00
Eric Harding 177b96b453 manual updates 2019-12-13 17:46:22 -05:00
Eric Harding 292f66a774 Add package id for nuget 2019-12-13 17:46:03 -05:00
Kevin Darby f1788b172c trying to layer these as queries 2019-12-13 16:53:56 -05:00
Kevin Darby ae5912d41d now we're cookin 2019-12-13 14:36:15 -05:00
Kevin Darby adbab4057f renames 2019-12-13 14:15:29 -05:00
Kevin Darby ca4a639b86 changed IFillModel to IMatchingModel, because that's really what it is, now I need fill model 2019-12-13 14:12:55 -05:00
Kevin Darby a539a06f45 interim push to go more stateless 2019-12-13 13:33:18 -05:00
Kevin Darby 1a87544ccb orders 2019-12-12 23:51:18 -05:00
Kevin Darby b97450297e update notes 2019-12-12 23:10:07 -05:00
Kevin Darby 6a1040a339 regen for trade counter parties 2019-12-12 23:07:49 -05:00
Kevin Darby 7fe20129d2 seems like we really can't count on the order of trade vs delete, so we have to use the price book to approximate crosses 2019-12-12 22:46:12 -05:00
Kevin Darby 4f59c1bb47 1 based arrays, so good 2019-12-12 18:26:10 -05:00
Kevin Darby 3ba17f1c87 allow disk in query 2019-12-12 18:19:08 -05:00
Kevin Darby ef874dda7e bigblue is slow, but I think we're on the right track now 2019-12-12 18:10:03 -05:00
Kevin Darby 6e966a6229 write the data with orders in same mongo document 2019-12-12 16:56:43 -05:00
Kevin Darby ed3b890c67 might have to store the data a little differently 2019-12-12 15:16:02 -05:00
Kevin Darby 2a46fd6227 push for pete 2019-12-12 14:27:28 -05:00
Kevin Darby 23e13846c6 botched order priority when only reducing size 2019-12-12 10:13:40 -05:00
Kevin Darby 39e4549972 only got one fill 2019-12-11 22:55:12 -05:00
Kevin Darby d924962546 kinda starting to work 2019-12-11 18:11:13 -05:00
Kevin Darby 0f1c81eb48 roundtrip 2019-12-11 18:07:33 -05:00
Kevin Darby 5e6fe7282d moved code around verified roll_snipe still works 2019-12-11 17:02:47 -05:00
Kevin Darby a47dad1ce4 changes in algo backtester to facilitate jupyter, will probably remove backwards compatibility in a bit 2019-12-11 13:28:48 -05:00
Kevin Darby 1809c778e2 ialgo 2019-12-10 23:40:55 -05:00
Kevin Darby 2178e16d3c restructure for jupyter 2019-12-10 22:50:49 -05:00
Kevin Darby deadbc6cf2 we actually do want one row per order for better query perf 2019-12-10 21:14:57 -05:00
John Colvin 7ccd9134c4 SyntheticSecurityParser trims whitespace from leg type text 2019-12-10 18:11:26 -06:00
John Colvin 54d82e1773 DeleteSyntheticDaemon - improve logging, support batch delete 2019-12-10 18:01:44 -06:00
Kevin Darby 445d77223f move some stuff around 2019-12-10 18:18:05 -05:00
Kevin Darby 09b24e33fc Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-12-09 14:05:56 -06:00
Kevin Darby 84553d11c4 new_externals 2019-12-09 14:05:04 -06:00
Kevin Darby eb1bab5878 linux build 2019-12-09 14:18:05 -05:00
Kevin Darby a7a58038b3 release build 2019-12-09 14:15:04 -05:00
Kevin Darby 1b9af8f010 new externals 2019-12-09 11:45:10 -05:00
Kevin Darby 113c4fe539 changes in notebooks, I am matching Taha 2019-12-09 11:12:06 -05:00
Kevin Darby c33662dc2c py 2019-12-09 00:30:36 -05:00
Kevin Darby 8817b22376 write book with trades 2019-12-08 14:37:48 -05:00
Kevin Darby b48da514e5 I think we got it 2019-12-08 00:18:25 -05:00
Kevin Darby 59cba2456b honing in on some timing issue 2019-12-07 22:02:45 -05:00
Kevin Darby 839c3d430b fixed build 2019-12-07 16:27:30 -05:00
Kevin Darby 5b1f2522e1 found bug in synth processor, won't build until I fix 2019-12-06 18:29:58 -05:00
Kevin Darby 6c3ccdf856 that's 400 micros, not nanos 2019-12-06 14:30:15 -05:00
Kevin Darby 157d834cbb getting somewhere with the roll 2019-12-06 13:42:40 -05:00
Kevin Darby 4f2e9b19cc trying to merge some collections 2019-12-05 23:49:11 -05:00
Kevin Darby fed7ca1ff3 honing in 2019-12-05 18:20:28 -05:00
Kevin Darby 73cfa17791 notes 2019-12-05 10:50:54 -05:00
Kevin Darby 474a53ba92 change notebook destination to big blue 2019-12-05 10:50:10 -05:00
Kevin Darby 69612eaee7 thought better of two tables, this makes simulation way easier 2019-12-04 23:33:48 -05:00
Kevin Darby b7ad777de4 have to go back to orders stream 2019-12-04 22:44:22 -05:00
Kevin Darby d6a70d852e added orders to the mongo 2019-12-04 18:10:47 -05:00
John Colvin 9483962e7b DiscretePropertyChangeDetector uses generic/strong types rather than boxing to object 2019-12-04 13:27:44 -06:00
John Colvin f94c5bc542 SP-273 Fills are unselected when order tree changes 2019-12-04 13:24:30 -06:00
Kevin Darby 33f12edce5 finally figured out timestamps 2019-12-03 23:47:48 -05:00
Kevin Darby ab2e9fe6bd I might have fixed the dates 2019-12-03 22:34:04 -05:00
Kevin Darby 8bcffea686 jupyter 2019-12-03 18:17:31 -05:00
John Colvin ce985f4b44 FillsVM state doesn't need to be observable (every usage is equiv to Observable.Return) 2019-12-03 14:02:26 -06:00
John Colvin 032f2253a7 ExecutionStrategyEditor 'Remove Leg' tooltip 2019-12-03 12:49:42 -06:00
John Colvin 4b4eb66ef0 SP-271 Synthetic cloning 2019-12-03 12:49:42 -06:00
Eric Harding 8c837f9f64 Expound on service fundamentals / ServiceContext 2019-12-03 13:16:41 -05:00
Kevin Darby 941472c5c0 bug in spread processing, small things in simulation 2019-12-02 23:35:42 -05:00
Kevin Darby d2a5e03976 notes 2019-12-02 18:14:54 -05:00
John Colvin a71914d566 TickFactorsPicker - disable auto complete behavior 2019-12-02 16:16:41 -06:00
John Colvin bd00183f7a SP-270 Order Book - PIQ Bug (fix OrderSecurityAlgo.Equals impl) 2019-12-02 15:24:10 -06:00
Kevin Darby 97e3bce327 moving historical analysis stuff to spark_historical 2019-12-02 16:06:05 -05:00
Kevin Darby e766667584 <= vs < 2019-12-02 14:48:54 -05:00
Kevin Darby a93c110c29 better serialization for pbuf Timestamps 2019-12-02 14:43:29 -05:00
Jake Norbut 2674e526be formatting 2019-12-02 13:11:44 -06:00
Jake Norbut 4ea2c08cc3 hooking up message Visibility to new vm property 2019-12-02 13:06:30 -06:00
Jake Norbut 99432ad7ac adding status message visibility property to ladder vm 2019-12-02 13:05:43 -06:00
Jake Norbut 8c210bff34 adding status message visibility to setting vm 2019-12-02 13:04:57 -06:00
Jake Norbut e566a94380 adding status_message_visible to settings 2019-12-02 13:04:15 -06:00
Eric Harding 85f97ae552 Combine rpc/streams into services.md 2019-12-01 21:50:17 -05:00
Kevin Darby e1c7b89408 mongo 2019-12-01 21:38:55 -05:00
Kevin Darby ad3ce72a39 added a mongo generator, much nicer 2019-11-29 16:35:46 -05:00
Kevin Darby 57f48472d3 trying out timescaledb, 2019-11-29 15:47:20 -05:00
Kevin Darby 45ae6ac585 parsing this data needs to get faster 2019-11-29 13:33:06 -05:00
Eric Harding 7ca0d10eb0 client/overview.md 2019-11-29 11:49:24 -05:00
Kevin Darby 5a90676a25 need a finally 2019-11-27 21:57:03 -05:00
Kevin Darby 8536f782ce Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 21:47:03 -05:00
Kevin Darby 5f45ddf210 wack vs backwack 2019-11-28 02:46:27 +00:00
Kevin Darby 67c83c7d68 remove postgres tag from spark_vm_shell 2019-11-27 21:10:22 -05:00
Kevin Darby 6f6e1a9e55 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 21:01:43 -05:00
Kevin Darby c80451bc5e added async 2019-11-27 18:20:14 -05:00
Kevin Darby d6a533ca5c PR feedback 2019-11-27 17:21:24 -05:00
Kevin Darby e0112eff89 stuff 2019-11-27 17:20:55 -05:00
Kevin Darby 38b29f0afb more pg 2019-11-27 17:14:29 -05:00
Kevin Darby 8b9d689a5f able to preprocess trades and books into a postgres 2019-11-27 16:18:56 -05:00
Kevin Darby 097edb2726 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 15:09:58 -05:00
Kevin Darby 12e3e9a222 gave up and decided to run postgres in docker on my host 2019-11-27 15:08:51 -05:00
Taha Afzal ce19ba1419 added MarketQueriesHelper 2019-11-27 14:04:32 -05:00
Taha Afzal c81a22e55d Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 11:50:45 -05:00
Taha Afzal bd04f49ff5 Reorg 2019-11-27 11:50:38 -05:00
Eric Harding 3a9e8970d6 merge temp_synthetic_compute 2019-11-27 10:28:22 -05:00
Taha Afzal 9837645d16 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-27 09:34:43 -05:00
Taha Afzal 1789c02295 skeleton in place 2019-11-27 09:34:28 -05:00
Kevin Darby 3280376df3 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-26 21:31:42 -05:00
Taha Afzal d251593f04 parsing sniper args 2019-11-26 19:49:51 -05:00
Kevin Darby 9afeb29c63 pg 2019-11-26 18:21:06 -05:00
Kevin Darby e4dc07ce3f Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-26 17:40:01 -05:00
Kevin Darby 2bbf22a153 new docker for postges some db code 2019-11-26 16:03:43 -05:00
Taha Afzal a598ae8bd2 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-26 15:40:19 -05:00
Taha Afzal 4d72b9d2e7 filling in 2019-11-26 15:40:10 -05:00
Kevin Darby e000b1d0c0 hmm 2019-11-26 00:07:36 -05:00
Kevin Darby ef4261fe7f small parser tweaks for taha / me 2019-11-25 23:17:30 -05:00
Kevin Darby c3b75aa742 rename packet variable 2019-11-25 15:13:30 -05:00
Taha Afzal 2518be52fb book post processing path formalized 2019-11-25 15:12:29 -05:00
Kevin Darby f3ee6c0e30 TWAP completes 2019-11-25 14:54:15 -05:00
Kevin Darby 94363767ab small bug in slicer 2019-11-25 14:10:54 -05:00
John Colvin 07da9fd296 Import Synthetics manual test 2019-11-25 12:55:13 -06:00
John Colvin 436dbd217c SP-266 synthetic csv parser/import dialog 2019-11-25 12:55:13 -06:00
John Colvin 0937381a3f INotifyDataErrorInfo 'subscribe_has_errors' extension only notifies observers when HasErrors changes 2019-11-25 12:55:13 -06:00
John Colvin d42f6b6fff EnumHelper - generic add/remove_flags methods 2019-11-25 12:55:13 -06:00
John Colvin 03940b291b ArrayHelper.trim was not actually reassigning ref with trimmed result 2019-11-25 12:55:13 -06:00
John Colvin 2a72b03afc TickFactorsPicker - disable auto complete behavior 2019-11-25 12:55:13 -06:00
John Colvin 98caeb5410 MDDaemon - check that synthetic metadata.HasStrategy before adding to map 2019-11-25 12:55:13 -06:00
John Colvin 73cb56a661 DIYGridRowPresenter - don't perform drag selection when SelectionMode=Single 2019-11-25 12:55:13 -06:00
John Colvin 88932081b7 add WindowMaxSizeConverter to implement 'SizeToContent' max dimensions 2019-11-25 12:55:13 -06:00
Taha Afzal 9dd6e39665 post processing of books + helper functions to allign boks 2019-11-25 09:34:38 -05:00
Eric Harding ca8aed1f2b client pages outline 2019-11-22 18:49:58 -05:00
Eric Harding b32d63fcda Update api site template 2019-11-20 12:39:59 -05:00
Eric Harding 54d9d5e2f3 Add google analytics 2019-11-19 16:38:30 -05:00
Kevin Darby 722ae68cf1 more 2019-11-18 15:08:09 -05:00
Eric Harding e39313ecf6 Add merge_synthetic_book sample test code 2019-11-15 17:42:49 -05:00
Eric Harding 8095100bee p/invoke for synthetic book computation 2019-11-15 16:11:02 -05:00
Kevin Darby a76415a52f native algo state 2019-11-14 17:04:42 -05:00
Kevin Darby 06305ee4ac having trouble routing fills down to c++ 2019-11-14 16:21:51 -05:00
Kevin Darby 70e87151bc upstream changes for PR 2019-11-13 16:49:17 -05:00
Kevin Darby d1299f8a78 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-13 16:47:18 -05:00
Kevin Darby 20e58dc80b address PR concerns 2019-11-13 16:46:36 -05:00
Kevin Darby 9f7b0e5898 running twap from c#, need some stats 2019-11-12 18:12:06 -05:00
Kevin Darby 5e073c2330 interop with c++ algos kinda works 2019-11-12 17:56:25 -05:00
Kevin Darby 1b425d9a7c Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-12 11:31:31 -05:00
Kevin Darby 3546b65c91 don't finalize 2019-11-12 11:18:24 -05:00
Kevin Darby 758da29e25 added assert I think works 2019-11-11 23:19:03 -05:00
Kevin Darby 60e246036b back to state where I can repl dan 2019-11-11 23:14:49 -05:00
Kevin Darby 4b34f959e6 skip default 2019-11-11 18:17:29 -05:00
Kevin Darby 8abbd24cbe bug in orderbook refactor 2019-11-11 17:57:19 -05:00
Kevin Darby 932f852d6a Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-11 17:14:16 -05:00
Kevin Darby 5773945791 add data dir for .net in linux
make sparkcmd build in the right place on windows again
2019-11-11 17:12:55 -05:00
Kevin Darby bda1bd8f96 round trip algo runs on linux 2019-11-11 17:11:27 -05:00
Kevin Darby c3632e02d3 Added NativeOrderWrapper for algo backtesting 2019-11-08 18:04:34 -05:00
Kevin Darby 1b959b09c8 hooking c++ algos to algo backtest framework 2019-11-07 18:01:21 -05:00
Kevin Darby f55637ea08 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-06 21:36:57 -05:00
Kevin Darby 0272df9974 Merge branch 'vwap' of bitbucket.org:bluetradesys/spark into vwap 2019-11-06 21:34:45 -05:00
Kevin Darby 0e02428b79 change algoanalyzer startup for taha 2019-11-06 21:34:35 -05:00
Kevin Darby 721c889235 release mode uses published sparkcmd 2019-11-06 18:22:06 -05:00
Kevin Darby 9957af655f hooked sparkcmd linux build to the main server build stuff 2019-11-06 17:47:21 -05:00
Kevin Darby eab92fb486 damn windows casing 2019-11-06 13:55:29 -05:00
Kevin Darby 9f1cd47cfa hooking build to cmake 2019-11-06 13:55:07 -05:00
Kevin Darby 90c3edbbe7 adding core 2 back to docker per eric 2019-11-06 13:24:57 -05:00
Kevin Darby adee8b584c capitalized first letter of proto to work around a protoc bug 2019-11-06 12:43:27 -05:00
Kevin Darby 329ad323e0 delete protos to change case on windows 2019-11-06 12:41:37 -05:00
Kevin Darby a8473f9340 Capitalizing first letter of some protos because of a protoc bug 2019-11-06 12:31:58 -05:00
Kevin Darby 1c0115f8cf had to delete these protos to change case 2019-11-06 12:31:12 -05:00
Kevin Darby a6ef802d3f fix proj files 2019-11-05 18:38:17 -05:00
Kevin Darby 37b5c37015 merging dotnet branch 2019-11-05 18:24:24 -05:00
Kevin Darby 1e28e214a4 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-05 18:23:47 -05:00
Kevin Darby c713b97441 cleanup csproj 2019-11-05 18:23:09 -05:00
Kevin Darby 4e8525f015 Merge branch 'dotnet' of bitbucket.org:bluetradesys/spark into vwap 2019-11-05 18:21:40 -05:00
Kevin Darby eb79264b9b netcore builds on linux through dotnet build 2019-11-05 18:19:17 -05:00
Kevin Darby 9c359ddb33 for_taha 2019-11-05 16:38:01 -05:00
Kevin Darby 67a16d5085 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-11-03 23:20:44 -05:00
Kevin Darby 6049cc4b43 oil spreads seem to work 2019-10-26 22:38:13 -04:00
Kevin Darby d916180132 back to oil 2019-10-25 23:50:20 -04:00
Kevin Darby ad9f28f782 round trip treasuries...not great 2019-10-25 23:32:02 -04:00
Kevin Darby 2890e4ebcc rt spreader 2019-10-25 18:56:52 -04:00
Kevin Darby 6638e4c765 fixed spreads / outright arbitration 2019-10-25 18:50:04 -04:00
Kevin Darby a251a8f411 clone 2019-10-25 00:50:02 -04:00
Kevin Darby e7790679ca this isn't as fast as I thought it would be 2019-10-25 00:15:34 -04:00
Kevin Darby 2a5e777c15 this is better 2019-10-24 18:30:24 -04:00
Kevin Darby fdbbce9ff3 remove concept of pre_book, instead separate the book arrangement and the trade / cross processing and layer in our orders by timestamp 2019-10-24 18:25:06 -04:00
Kevin Darby d47ac768c4 trying something 2019-10-24 16:30:21 -04:00
Kevin Darby 4867f4fa53 match event 2019-10-24 15:53:16 -04:00
Kevin Darby 902a99e7d0 new files 2019-10-24 14:30:13 -04:00
Kevin Darby faf5f664dc share one order book 2019-10-24 14:29:35 -04:00
Kevin Darby 4af94aaaa8 made some trades 2019-10-23 23:52:23 -04:00
Kevin Darby 2bea0a0a11 replicated some CL spread arbs 2019-10-23 18:34:40 -04:00
Kevin Darby b55067be91 trying 10/21 oil spreads 2019-10-23 00:24:21 -04:00
Kevin Darby 7ea20dbbff had to introduce concept of can_aggress-- if we're first in the queue we can't tell the difference between that and a cross 2019-10-22 23:50:07 -04:00
Kevin Darby 2dcf1424ea . 2019-10-22 18:21:30 -04:00
Kevin Darby caedb1ed64 round trip spreader taking shape 2019-10-22 18:15:13 -04:00
Kevin Darby d39856a93a refactor for multiple securities, implemented round tripper 2019-10-22 16:27:01 -04:00
Kevin Darby f8c7cc87b9 complicated hierarchies 2019-10-22 00:07:35 -04:00
Kevin Darby ee9a9bd721 moving stuff around for spreader case 2019-10-21 18:21:21 -04:00
Kevin Darby 7520d3516a move pnl computation to orderbook 2019-10-21 12:37:12 -04:00
Kevin Darby a2a9df86fc now we need prorata 2019-10-20 22:18:36 -04:00
Kevin Darby aa31aa7a3e testing 2019-10-20 21:27:19 -04:00
Kevin Darby 950b8686f2 small bug here in change / cancel 2019-10-20 21:22:50 -04:00
Kevin Darby b6ab217a11 assert that we are moving forward in time 2019-10-20 21:21:59 -04:00
Kevin Darby 3b8176d326 status event 2019-10-20 21:19:49 -04:00
Kevin Darby e9ec07cb2b have to queue the hedge with latency 2019-10-20 01:13:12 -04:00
Kevin Darby 78659b5d20 if these treasury results are real we are getting into a new business 2019-10-20 00:25:07 -04:00
Kevin Darby 9e26a2c651 adding top 2019-10-19 21:42:08 -04:00
Kevin Darby 3243ce1a45 treasury roll 2019-10-18 23:58:11 -04:00
Kevin Darby 475b6ae25b peg, cover, lean 2019-10-18 22:48:17 -04:00
Kevin Darby b853af59d6 usings 2019-10-18 22:24:10 -04:00
Kevin Darby ded1ad42e7 arbitraged spread and futures streams, separated replay from sending 2019-10-18 22:13:30 -04:00
Kevin Darby f3541b6c7f added latency to the catalyst loop; my clock isn't great though because it relies on the tape 2019-10-18 17:21:55 -04:00
Kevin Darby 3c40c84c02 got the mbo fixed 2019-10-18 16:47:02 -04:00
Kevin Darby d50420edb9 cme can send order updates in MDEntry or OrderIdEntry.... 2019-10-18 15:06:37 -04:00
Kevin Darby e0794f0725 added some assertions, cleaning up, now have to look at q 2019-10-18 13:23:40 -04:00
Kevin Darby fb6282ae9e optimizations 2019-10-18 11:15:08 -04:00
Kevin Darby 23995e5cc6 shaping up 2019-10-17 23:52:20 -04:00
Kevin Darby 161545b864 jcl is small loss always joining at end of q 2019-10-17 22:10:30 -04:00
Kevin Darby 285250dded bugs 2019-10-17 18:22:23 -04:00
Kevin Darby bbc3e03e48 now lets look at this queue 2019-10-17 18:04:03 -04:00
Kevin Darby 6b8d0d203c partials work, the queue seems to have broken 2019-10-17 17:42:03 -04:00
Kevin Darby c0bec8cfa5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-10-17 12:18:01 -04:00
Kevin Darby 19b0d8bf56 typo that I need to fix in master 2019-10-17 11:47:46 -04:00
Kevin Darby ede14ea7b3 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-10-17 11:40:24 -04:00
Kevin Darby 48ae7c4c33 this looks a lot like our c++ interface; I do not know if that's good 2019-10-17 11:36:35 -04:00
Kevin Darby fc217fd904 sweet replication 2019-10-16 23:23:16 -04:00
Kevin Darby b692a3e9d2 partial fills kinda kill everything 2019-10-16 18:37:13 -04:00
Kevin Darby 48f5e47335 now partial fills 2019-10-16 15:38:43 -04:00
Kevin Darby a080a0ccaa using sendingTime instead of transactTime 2019-10-16 14:45:13 -04:00
Kevin Darby 90321045f6 need to filter orders separately 2019-10-15 23:55:16 -04:00
Kevin Darby 8ad9e24415 interesting results 2019-10-15 23:26:10 -04:00
Kevin Darby 2008786c4e some semblance of a simulation going 2019-10-15 18:28:49 -04:00
Kevin Darby 57f8036796 jcl 2019-10-14 18:10:21 -04:00
Kevin Darby 21a304261e merge 2019-10-14 00:14:29 -04:00
Kevin Darby cefb2b2524 Merge branch 'fix' of bitbucket.org:bluetradesys/spark into vwap 2019-10-13 14:12:50 -04:00
Kevin Darby e26bdb228b Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-10-13 14:10:12 -04:00
Kevin Darby 1621ad03bf Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-10-13 14:09:45 -04:00
Kevin Darby 174acf1597 getting join fills 2019-10-11 18:22:53 -04:00
Kevin Darby bf32c6133a mbo parsing in c# 2019-10-11 18:18:22 -04:00
Kevin Darby fe73874b19 more analysis 2019-10-11 17:28:12 -04:00
Kevin Darby 0eb82a0d5c inching towards a simulated pnl of an arbitrary strategy 2019-10-10 18:05:14 -04:00
Kevin Darby fc2165d85d CL, ZN, ZC exhibit spookily similar histograms, ES is a little different 2019-10-10 11:04:34 -04:00
Kevin Darby a368b2ea16 factored out analyzers so we can test Join/Cover/Lean and Spreader 2019-10-09 17:46:10 -04:00
Kevin Darby 6294ef6db2 made some headway in analysis 2019-10-09 16:25:44 -04:00
Kevin Darby a9cca80c6d some corn stats 2019-10-08 17:36:16 -04:00
Kevin Darby aee9066ccf cmake 2019-10-08 15:14:44 -04:00
Kevin Darby ea3c130738 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-10-08 13:13:56 -04:00
Kevin Darby 193f248f06 merge deleted some of my files 2019-10-07 17:40:01 -04:00
Kevin Darby 5212fb272e Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-10-07 17:27:12 -04:00
Kevin Darby 6772c77e01 rudimentary fix orders seem to work 2019-10-07 17:18:20 -04:00
Kevin Darby 95c589f594 hooking up to run this fix server 2019-09-30 15:00:30 -04:00
Kevin Darby 699c7c5bfe Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-09-30 12:02:13 -04:00
Kevin Darby 7de82b5685 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-09-30 11:45:44 -04:00
Kevin Darby a8e9d0d7d8 ordersession and dropcopy session code complete for native orders, time to debug 2019-09-27 18:21:35 -04:00
Kevin Darby d078e73179 order session 2019-09-27 17:58:36 -04:00
Kevin Darby 4351d6e66a drop copy session code complete 2019-09-27 17:04:42 -04:00
Kevin Darby 9850d6bde0 rx-cpp requires concurrent use with prescription medicine 2019-09-27 14:29:22 -04:00
Kevin Darby d23a072ad6 rx 2019-09-26 18:27:41 -04:00
Kevin Darby 6ef0808f49 never thought I'd use rxcpp...it's truly terrible; I feel physical pain 2019-09-26 18:16:48 -04:00
Kevin Darby e1c2d8aaaf it builds, ship it 2019-09-26 12:33:42 -04:00
Kevin Darby 719fd03063 nothing builds 2019-09-26 11:45:08 -04:00
Kevin Darby d436dae7cd more fix 2019-09-24 18:22:09 -04:00
Kevin Darby f66004b465 fix port 2019-09-24 14:37:30 -04:00
Kevin Darby aa8a56787b fix fix fix 2019-09-23 18:16:05 -04:00
Kevin Darby 35a69396f1 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-09-23 17:08:56 -04:00
Kevin Darby ddfb6d1105 update externals 2019-09-23 17:03:49 -04:00
Kevin Darby 005c0f9a5d c++ fix 2019-09-23 16:59:39 -04:00
Kevin Darby cf62eae51b replay book building 2019-09-06 18:13:16 -04:00
Kevin Darby c7e49e8d21 data replay 2019-09-05 18:47:04 -04:00
Kevin Darby 8bc420190b Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-05 18:46:53 -04:00
Kevin Darby 62711a144b got some replay 2019-09-05 14:14:41 -04:00
Kevin Darby 333273b48a testing 2019-09-04 17:41:48 -04:00
Kevin Darby dd194f8ac0 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-04 17:11:44 -04:00
Kevin Darby 5992f4814d testing replay 2019-09-04 17:02:06 -04:00
Kevin Darby d67f15b3fb Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-04 15:12:02 -04:00
Kevin Darby be8635788a Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-04 14:03:41 -04:00
Kevin Darby d96355e6c4 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-03 14:36:29 -04:00
Kevin Darby 4ef2c8a2da conf changes 2019-09-03 14:35:23 -04:00
Kevin Darby 058fbe1aee utils is not in mbts any longer 2019-09-02 17:14:41 -04:00
Kevin Darby bc0f9f574a Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-09-02 17:05:21 -04:00
Kevin Darby f575b82ce0 almost got the replay going 2019-08-30 17:54:58 -04:00
Kevin Darby 050ea18a02 generation 2019-08-30 00:17:00 -04:00
Kevin Darby 18829279d2 working on replay from parser 2019-08-29 17:44:55 -04:00
Kevin Darby 75703d54ea getting mbo from s3 2019-08-29 12:21:01 -04:00
Kevin Darby 0a3fc1bca0 s3 2019-08-28 18:52:02 -04:00
Kevin Darby bc688ef44d parsing mbo files 2019-08-28 17:47:29 -04:00
Kevin Darby 57af89b628 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-08-28 16:47:20 -04:00
Kevin Darby 4fcc70bcf0 mbo fix parser 2019-08-27 18:06:03 -04:00
Kevin Darby cad6f2c1eb Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-26 17:57:16 -04:00
Kevin Darby c16b898d86 scaffolding for historical data 2019-08-26 17:55:26 -04:00
Kevin Darby 4f98244c3a arrival 2019-08-23 22:23:59 -04:00
Kevin Darby 8770f98fb1 more algo 2019-08-19 11:54:23 -04:00
Kevin Darby bbcf78517f self adjust 2019-08-16 11:30:36 -04:00
Kevin Darby b9a940b4e3 adjust for my size 2019-08-15 18:00:55 -04:00
Kevin Darby d8a4c1aaf7 added VolumeSlicer hierarchy, VolumeSliceAlgo and POVAlgo 2019-08-15 17:41:23 -04:00
Kevin Darby 9803cf31c8 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-15 09:36:00 -04:00
Kevin Darby 4a36177992 externals with master 2019-08-14 16:17:19 -04:00
Kevin Darby 421614198a fixed external 2019-08-14 15:45:31 -04:00
Kevin Darby 51e227e921 timeslicer 2019-08-12 22:33:26 -04:00
Kevin Darby 74948d11c5 build 2019-08-12 18:33:00 -04:00
Kevin Darby f88e1b5903 build 2019-08-12 18:30:27 -04:00
Kevin Darby 59115ce886 volume estimator 2019-08-12 18:24:29 -04:00
Kevin Darby 4b18d15421 refactor into TimeSlicer and VolumeSlicer 2019-08-12 18:07:07 -04:00
Kevin Darby 9be591c634 need a slightly different hierarchy for pov 2019-08-12 16:08:46 -04:00
Kevin Darby 462af2f1f3 make offset algo support a negative offset which means cross 2019-08-12 14:20:45 -04:00
Kevin Darby ab35c943fd working on pov 2019-08-12 13:52:57 -04:00
Kevin Darby aa36b24165 slicer params 2019-08-12 11:11:27 -04:00
Kevin Darby 547cdda3db adding aggression params to base class 2019-08-11 16:06:01 -04:00
Kevin Darby 3b77fd1a6c peg price updates 2019-08-09 18:09:31 -04:00
Kevin Darby a8110c9a8d added peg algo to support vwap/twap 2019-08-09 17:58:09 -04:00
Kevin Darby b6f80d6a4a twap working ok 2019-08-09 14:52:25 -04:00
Kevin Darby 18489e64a2 twapping 2019-08-08 18:16:00 -04:00
Kevin Darby a3d2df9ba2 . 2019-08-08 16:44:35 -04:00
Kevin Darby 314700e2e7 residual quantity 2019-08-08 16:09:06 -04:00
Kevin Darby 461b6c15d6 twap bugs 2019-08-08 15:25:00 -04:00
Kevin Darby d23ac7e8ef allow generic order vm to reflect one level deep into protos to generate a gui; will need john's help on a datetimepicker 2019-08-08 14:54:34 -04:00
Kevin Darby 68c0179465 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-08 12:20:15 -04:00
Kevin Darby 8bc1455813 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-08 11:07:40 -04:00
Kevin Darby ecaa04ecf8 factor 2019-08-08 09:59:14 -04:00
Kevin Darby 44e3070544 add TimeSlicer 2019-08-08 00:05:41 -04:00
Kevin Darby 1815950835 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-07 17:31:50 -04:00
Kevin Darby 8762fbf7ce added twap to algo types 2019-08-06 18:20:10 -04:00
Kevin Darby 0459171d6e moved algo types to config 2019-08-06 18:15:47 -04:00
Kevin Darby fcdf72c5d9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-06 16:57:36 -04:00
Kevin Darby f5caf0b7ba twap bones 2019-08-06 16:57:02 -04:00
Kevin Darby a0f2673b3a basic timing structure down; maybe all these derive from twap 2019-08-05 23:51:28 -04:00
Kevin Darby ba3439b049 twap 2019-08-05 18:21:03 -04:00
Kevin Darby ae4d7f719b Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-02 17:57:21 -04:00
Kevin Darby 64916f6bfc Merge branch 'master' of bitbucket.org:bluetradesys/spark into vwap 2019-08-02 17:38:30 -04:00
Kevin Darby 1a9dd1c37a cleaning up this proto 2019-08-01 17:40:21 -04:00
Kevin Darby d77925c568 Merge branch 'prorata' of bitbucket.org:bluetradesys/spark into vwap 2019-08-01 17:11:38 -04:00
Kevin Darby f1f1b68ffb arrival 2019-07-03 22:14:39 -04:00
Kevin Darby 835f453f03 Merge branch 'prorata' of bitbucket.org:bluetradesys/spark into vwap 2019-07-03 15:17:09 -04:00
Kevin Darby 0eb9518485 didn't get far 2019-06-13 23:41:30 -04:00
Kevin Darby b5c70fc34e added price to slice 2019-06-11 23:56:11 -04:00
Kevin Darby a1dba1d7d0 historicaldataservice 2019-06-11 23:12:20 -04:00
Kevin Darby efdea5256f algoflow will probably not be in python 2019-06-11 21:07:55 -04:00
Kevin Darby 5df1e5f383 py notes 2019-06-09 22:50:46 -04:00
Kevin Darby 99750da31d beginnings of python driver for algo development. goal is to allow visual / interactive development environment for
more complex algos.  Will also offer backtesting of bts algos over MBO data.
2019-06-09 22:47:51 -04:00
Kevin Darby 49247dba3f externals 2019-06-09 21:58:11 -04:00
Kevin Darby a25257cb33 update externals for pybind 2019-06-08 23:33:43 -04:00
Kevin Darby b1c180d663 Merge branch 'prorata' of bitbucket.org:bluetradesys/spark into vwap 2019-06-07 16:01:49 -04:00
Kevin Darby 6afb16abcb bones for bluex orders 2019-06-07 15:53:25 -04:00
Kevin Darby 995fa3df35 . 2019-06-06 23:34:25 -04:00
Kevin Darby 18eb28dfd2 waps 2019-06-06 22:08:03 -04:00
Kevin Darby 99cdfadf8d bones for vwap, twap, arrivalprice, pov 2019-06-05 23:30:52 -04:00
Kevin Darby feab3cbb89 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2019-01-22 16:10:16 -05:00
Kevin Darby 1f7c1be194 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2018-12-28 13:51:59 -05:00
Kevin Darby f78f18b3b4 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix 2018-12-20 16:20:19 -05:00
Kevin Darby 6e7fa91718 ready for testing 2018-12-14 10:39:34 -05:00
Kevin Darby 8cac406547 fix error handling 2018-12-12 17:33:25 -05:00
Kevin Darby 0d83f28cbc fix lives 2018-12-12 14:26:03 -05:00
Kevin Darby 941f4bf837 Merge branch 'cleanup_mdclient' of bitbucket.org:bluetradesys/spark into fix 2018-12-11 23:05:35 -05:00
Kevin Darby 8bbe9cfd98 Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-06 12:56:02 -05:00
Kevin Darby ba633532eb hit a wall here with MDClient, going to refactor 2018-12-06 12:55:21 -05:00
Kevin Darby 7bf50415ca nearly ready for testing 2018-12-05 17:55:06 -05:00
Kevin Darby edbaa9c435 looks like we have dropcopy 2018-12-05 15:35:49 -05:00
Kevin Darby 7789f39c9d handle some messages 2018-12-04 18:20:23 -05:00
Kevin Darby 6e188b4f73 shaping up 2018-12-04 16:37:41 -05:00
Kevin Darby 33e8ff58c5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-04 09:42:30 -05:00
Kevin Darby 24ae03a9f8 access permissions api so fix session can report fills and orders 2018-12-03 18:19:20 -05:00
Kevin Darby 5fe0335e1a Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 17:02:34 -05:00
Kevin Darby 6cbf707094 Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 16:06:58 -05:00
Kevin Darby 7e4ad55d9d established a session 2018-12-03 15:32:45 -05:00
Kevin Darby 35a0074014 bts fix44 adapter 2018-12-03 14:49:35 -05:00
Kevin Darby b8dd5c119d Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-12-03 10:41:34 -05:00
Kevin Darby 8ada73aa64 Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc2 2018-11-30 18:37:39 -05:00
Kevin Darby a16ff30873 lets see how hard this is 2018-11-30 18:32:51 -05:00
500 changed files with 123176 additions and 1066 deletions

No files matched your search

+1
View File
@@ -34,3 +34,4 @@ trace_data/
/doc/user/public/*
.ionide/*
/.ignore
CMakeDeps.cmake
+8 -3
View File
@@ -5,7 +5,9 @@
"includePath": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
"${workspaceRoot}",
"${workspaceFolder}/src",
"${workspaceRoot}/externals/linux/include"
],
"defines": [],
"intelliSenseMode": "clang-x64",
@@ -13,13 +15,16 @@
"path": [
"/usr/include",
"/usr/local/include",
"${workspaceRoot}"
"${workspaceRoot}",
"${workspaceFolder}/src",
"${workspaceRoot}/externals/linux/include"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"cStandard": "c11",
"cppStandard": "c++17"
"cppStandard": "c++17",
"compileCommands": "${workspaceFolder}/build/ninja_debug/compile_commands.json"
},
{
"name": "Windows",
+145 -12
View File
@@ -44,9 +44,10 @@
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
"/home/bts/spark/": "${workspaceRoot}",
"/home/bts/spark_host/": "${workspaceRoot}",
}
},
},
{
"name": "(gdb) Launch",
"type": "cppdbg",
@@ -98,11 +99,52 @@
"externalConsole": true,
"requireExactSource": false
},
{
"name": "sparkcmd Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": [
"historical",
"--server",
//"--rstart", "11/22/2019",
//"--rend", "11/25/2019",
//"--rsymbol", "ES",
//"--rsecuritytype", "Synthetic",
//"--post_process_file_type", "Trade",
//"--upload_to_aws",
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "sparkcmd simulation",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": [
"historical",
"--rstart", "11/26/2019",
"--rend", "11/26/2019",
"--rsymbol", "ZN",
"--rsecuritytype", "Synthetic",
"--algo_security", "ZNH0;ZNZ9-ZNH0;ZNZ9",
"--algo_type", "RoundTrip",
"--algo_params", "size=10;tick_size=0.015625,0.0078125,0.015625;hedge_factors=1,1,-1",
"--skip_preanalysis", "true",
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Excel Launch",
"type": "clr",
"request": "launch",
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
"program": "C:/Program Files/Microsoft Office/Office16/EXCEL.EXE",
"args": [
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"
],
@@ -111,6 +153,20 @@
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Fix Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": [
"fix",
"-order_session",
],
"cwd": "${workspaceRoot}/build/Debug/bin/net471/",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Tests",
"type": "clr",
@@ -126,11 +182,18 @@
"requireExactSource": false
},
{
"name": "Client Attach",
"name": ".NET Framework Attach",
"type": "clr",
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}",
"requireExactSource": false
},
{
"name": "Spreader Tests",
"type": "cppdbg",
@@ -337,20 +400,24 @@
"quoteArgs": true
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
"/home/bts/spark_host/": "${workspaceRoot}"
},
"justMyCode": true
},
{
"name": ".NET Core Remote Launch",
"name": "sparkcmd vsdbg (c#)",
"type": "coreclr",
"request": "launch",
"program": "/usr/bin/dotnet",
"program": "sparkcmd",
"args": [
"sparkcmd.dll",
"test",
"test_join_create_and_takeout_market"
"historical",
"--rstart", "11/20/2019",
"--rend", "11/20/2019",
"--rsymbol", "CL",
"--rsecuritytype", "Spread",
"--post_process_db", "mbo_history",
],
"env": {"LD_LIBRARY_PATH" : "."},
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/server",
"pipeTransport": {
@@ -361,11 +428,77 @@
"debuggerPath": "/home/bts/vsdbg/vsdbg",
"pipeCwd": "${workspaceRoot}",
"quoteArgs": true
},
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
},
"justMyCode": false
},
},
{
"name": "sparkcmd gdb (c++)",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/server/sparkcmd",
"args": [
"historical",
"--rstart", "10/21/2019",
"--rend", "10/21/2019",
"--rsymbol", "CL",
"--rsecuritytype", "Future",
"--algo_security", "CLX9",
"--algo_type", "TWAP",
"--algo_params", "'size=100;profit_ticks=1;tick_size=1,1,1;;hedge_factors=1,1,-1'",
"--skip_preanalysis", "true",
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/server/",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"preLaunchTask": "",
"logging": {
"engineLogging": false
},
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "enable pretty-printer",
"ignoreFailures": true
},
{
"description": "Skip STL",
"text": "skip -gfi /usr/include/c++/7/bits/*.h",
"ignoreFailures": true
},
{
"description": "LD LIBRARY",
"text": "set env LD_LIBRARY_PATH=.",
"ignoreFailures": false
}
],
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
}
},
{
"name": "Jupyter notebook",
"type": "coreclr",
"request": "launch",
"program": "jupyter",
"args": ["notebook", "--notebook-dir=c:\\code\\bts\\spark\\src\\client\\spark_historical\\Notebooks"],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "internalConsole",
},
]
}
+19 -10
View File
@@ -14,24 +14,21 @@
"focus": false,
"panel": "shared"
},
"group": {
"kind": "build",
"isDefault": true
}
"group": "build"
},
{
"label": "server_build",
"label": "server_build",
"type": "shell",
"command": ".\\tools\\buildsparkd.cmd",
"problemMatcher": {
"base": "$gcc",
"problemMatcher": {
"base": "$gcc",
"owner": "cpp",
"fileLocation": [
"relative",
"${workspaceFolder}"
"${workspaceFolder}"
],
"pattern": {
"regexp": "(.?)(home)(.?)(bts)(.?)(spark)(.?)(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)",
"regexp": "(.?)(home)(.?)(bts)(.?)(spark)(.?)(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)",
"file": 8,
"line": 9,
"column": 10,
@@ -49,6 +46,18 @@
"kind": "build",
"isDefault": true
}
}
},
{
"label": "linux build (Debug)",
"type": "shell",
"command": "cd build/ninja_debug; time ninja",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$gcc"
]
}
]
}
+11
View File
@@ -48,6 +48,7 @@ else ()
add_subdirectory(doc/user)
add_subdirectory(src/sdk)
add_subdirectory(src/data_files)
add_subdirectory(src/client)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/server
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/server
@@ -59,7 +60,17 @@ else ()
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libACE-5.7.5.so ${CMAKE_BINARY_DIR}/server
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
COMMENT "Creating server runtime layout"
DEPENDS sparkd data_files_dir spark_client_dir
DEPENDS sparkd data_files_dir)
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
add_custom_command(TARGET server_layout POST_BUILD
COMMAND ln -fs ${CMAKE_BINARY_DIR}/../${CMAKE_BUILD_TYPE}/bin/netcoreapp3.1/linux-x64/sparkcmd ${CMAKE_BINARY_DIR}/server/ )
else()
add_custom_command(TARGET server_layout POST_BUILD
COMMAND ln -fs ${CMAKE_BINARY_DIR}/../${CMAKE_BUILD_TYPE}/bin/netcoreapp3.1/linux-x64/publish/sparkcmd ${CMAKE_BINARY_DIR}/server/ )
endif()
endif()
+1
View File
@@ -0,0 +1 @@
[0108/231015.447:ERROR:file_io.cc(89)] ReadExactly: expected 36, observed 0
+1 -1
View File
@@ -1,6 +1,6 @@
+++
title="Client API"
template="client.html"
redirect_to = "client/getting-started/overview/"
sort_by="weight"
+++
@@ -1,6 +1,5 @@
+++
title="Getting Started"
template="client.html"
sort_by="weight"
weight = 1
+++
@@ -1,9 +1,25 @@
+++
title = "Install Prerequisites"
template="client.html"
weight = 1
weight = 10
+++
## Coming Soon
# C#/.NET
We target [.NET Standard 2.0](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) so you can use .NET Framework 4.6.1+ or .NET Core 2.0+. Simply add a reference to the [Spark Nuget package](https://nuget.org) to get started.
```
dotnet new console -lang c#
dotnet add package spark_sdk
dotnet build
```
# C++
The easiest way to get started with C++ is to use the Spark Server SDK to develop your application. Follow the instructions to [download and install the Spark Server SDK](/server/getting-started/install) and open the client_sdk sample project. If you want to run your C++ Client SDK application on Windows [contact us](mailto://api@bluetradesys.com) to get the required headers and binaries.
# JavaScript
We use JavaScript internally with the Spark Client API over a websocket connection but the documentation and packaging isn't ready yet. If this is something you're interested in get in touch with us at [api@bluetradesys.com](mailto:api@bluetradesys.com).
* Install .net core
@@ -0,0 +1,31 @@
+++
title = "Overview"
weight = 1
+++
The Spark Client SDK is a high level command and control API which can be used to build scripts or integrate Spark into your existing ecosystem. The API is divided into several independent **"services"** responsible for different aspects of the system. A Service can define both [remote procedure call (RPC)](/client/getting-started/services#rpc) and incremental [streaming](/client/getting-started/services#streams) data.
The [Spark Desktop](https://www.bluetradingsystems.com/bts-spark/) is built on top of this API so you can be assured that it is complete and well tested.
Spark uses [Google Protocol Buffers](https://developers.google.com/protocol-buffers/) as an interface definition language similar to [GRPC](https://grpc.io) but backed by our custom high-performance TCP protocol. We currently support C#/.NET and C++ <!--and Javascript--> but the architecture is designed for easy extension so contact us about your specific business needs.
## Orders
Use the [OrderService](/client/services/orderservice) to send and manage orders including native exchange orders, spreader orders, and custom Algo orders created using the [Spark Server SDK](https://api.bluetradesys.com/server/).
## Spreader
Use the [SyntheticSecurityService](/client/services/syntheticsecurityservice) to create and manage synthetic spreads. Once created these spreads show up in the security and market data feeds just like exchange listed spreads.
## Position
Use the [PositionService](/client/services/positionservice) to get your position and P&L reports or add fills manually to keep the Spark system up to date with an external system.
## Administration
Use the [AdminService](/client/services/adminservice) to manage users and control limits.
## Market Data
For .NET subscribe to market data though the high level MarketDataClient interface. From C++ or JavaScript use the MarketDataRelay service directly.
@@ -0,0 +1,127 @@
+++
title = "Services"
weight = 20
+++
The Spark Client SDK uses [Google Protocol Buffers](https://developers.google.com/protocol-buffers/) to define the interface for each service in a language independent manner. Services have both functions which return a single result and streams of data which return values continuously while subscribed. This page describes the mapping from the Protocol Buffer definition to the concrete definition in each supported language.
# ServiceContext
To connect your client to the Spark Server you need to get or create a `ServiceContext` instance. A `ServiceContext` represents a connection to the Spark Server and a background thread for handling messages on that connection. Generally there should be a single `ServiceContext` per process. All of the methods on the `ServiceContext` are safe thread safe.
Construct a ServiceContext by calling the default constructor. The ServiceContext will find the Spark Server by loading the server configuration over an HTTP connection. To determine the URL it will search the following locations and use the first one that is present.
* A command line argument `-configserver <url>`
* An environment variable `BTSCONFIG`
* `%APPDATA%/bts/conf/spark.conf` (Windows)
* /sparkdata/conf/spark.conf (Linux)
* $HOME/sparkdata/conf/spark.conf (Linux)
If your application is running on a system with the BTS Client installed this will probably work without any further configuration. If you plan to run your client application on a separate machine we recommend setting the `BTSCONFIG` environment variable.
<div class="info">
<i class="fa fa-lightbulb-o fa-fw" aria-hidden="true"></i>
If you are using the Spark Server SDK to run a local copy of the Spark server set <p><strong>BTSCONFIG = http://localhost:9000/config/</strong></p>
</div>
To get a proxy for a given service call ServiceContext.locate&lt;T>("qualifier") where T is the type of the service and "qualifier" is an optional qualifier used to disambiguate between 2 instances of the same service. The locate call cannot fail but it may return a disconnected proxy. When you make a call on the proxy you should check the call result to see if the method call succeeded.
# RPC
Here is the OrderService reduced to a single method definition. A RPC method always takes exactly 1 argument and returns 1 value. In this example the `create_order` call takes a `Create` instance and returns a `Response`.
```C
service OrderService {
rpc create_order(Create) returns (Response);
...
}
```
The `Create` and `Response` types are also defined as protocol buffer messages. Each field can either be a primitive type another message or a list or map.
```C
message Create {
OrderDefinition new_order = 1;
double price = 2;
uint32 quantity = 3;
}
message Response {
uint32 order_key = 1;
OrderError error_code = 2;
string msg = 3;
}
```
Each RPC call generates both synchronous (blocking) and asynchronous variants. In C# the asynchronous calls have the `_async` suffix and return a `Task`. In C++ the call is overloaded to either return a value synchronously or accept a callback function to be called on the ServiceContext thread when the call completes. <!-- In Javascript only async methods returning a Promise are generated because it never makes sense to make a blocking call in the browser. -->
To call the resulting service locate a proxy using the [ServiceContext](#servicecontext) and then call `cancel_order` or `cancel_order_async`. If you are calling the Spark API from a GUI application you should always prefer the `_async` version of the calls.
### C# example
```c#
var ctx = new ServiceContext();
var orders = ctx.locate<OrderService>();
var response =
await orders.create_order_async(
new Create { price = 2.25, quantity = 42 });
if (response.success) {
Console.WriteLine($"success: {response.value}");
}
else {
Console.WriteLine($"Error: {response.error_message}");
}
```
### C++ example
```cpp
auto ctx = ServiceContext{};
auto orders = ctx.locate<OrderService>();
Create c;
c.set_price(2.25);
c.set_quantity(42);
// Instead of providing an _async method C++ uses overloading.
// To make a syncronous call simply omit the callback
orders.create_order(c, [=](const Result<Response>& response) {
if(response.success()) {
std::cout << "success.";
}
else {
std::cout << "error: " << response.error_message();
}
});
```
# Streams
Streams allow you to subscribe to live updating data. Each stream represents a changing collection of protocol buffer objects. Similar to a database row each item has an `id` which uniquely identifies it within the collection. Updates from the Spark server can add, remove or replace entries in the collection. Since only the changed items are sent over the wire the stream can remain responsive even when it represents a large collection.
Streams are implemented using the language bindings for [ReativeX](http://reactivex.io/) which provide utility functions to drill down to the specific data your application needs. In C# we provide both mutable and immutable versions of the generated data types (C++ users can rely on `const`).
When defined in a proto file streams use the `stream` keyword on the result type.
```C
service OrderService {
...
rpc fills (FillsSub) returns (stream Fill);
}
```
### C# example
```c#
var sc = new ServiceContext();
var orders = sc.locate<OrderService>();
var fills_observable = orders.subscribei_fills();
var unsub = fills_observable.Subscribe(update => {
Console.WriteLine(
"Added {0} items, Removed {1} items, Updated {2} items. Total items {3}",
update.added.Count(),
update.removed.Count(),
update.changes.Count(),
update.all.Count());
});
Thread.Sleep(TimeSpan.FromMinutes(5));
unsub.Dispose();
```
### C++ Example
```cpp
```
+6
View File
@@ -0,0 +1,6 @@
+++
title="Samples"
sort_by="weight"
weight = 30
+++
@@ -0,0 +1,3 @@
+++
title = "AdminService"
+++
@@ -0,0 +1,3 @@
+++
title = "OrderService"
+++
@@ -0,0 +1,3 @@
+++
title = "PositionService"
+++
@@ -0,0 +1,3 @@
+++
title = "SyntheticService"
+++
@@ -0,0 +1,6 @@
+++
title="Services"
#sort_by="weight"
weight = 10
+++
@@ -0,0 +1,3 @@
+++
title = "Market Data"
+++
-1
View File
@@ -1,6 +1,5 @@
+++
title="Algo API"
template="documentation.html"
redirect_to = "server/getting-started/overview/"
sort_by="weight"
+++
@@ -1,6 +1,5 @@
+++
title="Getting Started"
template="documentation.html"
sort_by="weight"
weight = 1
+++
@@ -71,12 +71,12 @@ The first step is to start the spark_sdk Docker container with the examples dire
Windows Command Prompt:
{% clipable() %}
docker run --rm -it -v %CD%:/examples -u bts -w /examples --shm-size 1024m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
docker run --rm -it -v %CD%:/examples -u bts -w /examples --shm-size 2048m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
{% end %}
**Powershell** / Linux / Mac:
{% clipable() %}
docker run --rm -it -v ${PWD}:/examples -u bts -w /examples --shm-size 1024m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
docker run --rm -it -v ${PWD}:/examples -u bts -w /examples --shm-size 2048m --name spark_sdk -h spark_sdk --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 9000:9000 -p 9001:9001 spark_sdk bash
{% end %}
### Attach
@@ -5,7 +5,7 @@ weight = 2
## Request Access 🔑
To start using the Server SDK you will need to request access though our sales department. Please email <a href="mailto:info@bluetradesys.com">info@bluetradesys.com</a> or call <a href="tel:919-913-0850">(919) 913-0850</a> and we will provide download credentials.
To start using the Server SDK you will need to request access though our sales department. Please email <a href="mailto:api@bluetradesys.com">api@bluetradesys.com</a> or call <a href="tel:919-913-0850">(919) 913-0850</a> and we will provide download credentials.
## Download
-1
View File
@@ -1,6 +1,5 @@
+++
title="Reference"
template="documentation.html"
sort_by="weight"
weight = 2
+++
+6
View File
@@ -6,3 +6,9 @@ weight = 40
## I need to make a call that might take a few seconds. What is the best way to do this?
Create a background thread in [do_before_launch](/server/ref/initialization#before-launch) or [do_launch](/server/ref/initialization#launch-launch-attached) to do the work. Use [on_timer](/server/ref/subscriptions#timer) to poll until the result is available.
## I don't see the header files in the .zip file. Where are they?
The SDK headers are baked into the docker image along with the build tools. Once you've loaded the image with `docker load < spark_sdk.img` you can run this command to copy them out to your local file system `docker run --rm -it -v $PWD:/out spark_sdk /bin/tar -cvf /out/headers.tar /sdk/include` This will copy the headers to the current directory in headers.tar. We still recommend doing the actual build inside the docker environment but having a copy of the headers can be useful for setting up your development environment.
File renamed without changes.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

-1
View File
@@ -1,6 +1,5 @@
+++
title="Samples"
template="documentation.html"
sort_by="weight"
weight = 3
+++
-31
View File
@@ -1,31 +0,0 @@
{% extends "index.html" %}
{% block extra_nav_class %}{% endblock extra_nav_class %}
{% block extra_content_class %}content--reversed{% endblock extra_content_class %}
{% block content %}
{% set section = get_section(path="client/_index.md") %}
<div class="documentation">
<aside class="documentation__sidebar">
<ul>
{% for p in section.subsections %}
{% set subsection = get_section(path=p) %}
<li>
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
<ul>
{% for page in subsection.pages %}
<li class="{% if current_path == page.path %}active{% endif %}">
<a href="{{page.permalink}}">{{page.title}}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</aside>
<div class="documentation__content">
{% block doc_content %}
{% endblock doc_content %}
</div>
</div>
{% endblock content %}
-36
View File
@@ -1,36 +0,0 @@
{% extends "index.html" %}
{% block extra_nav_class %}{% endblock extra_nav_class %}
{% block extra_content_class %}content--reversed{% endblock extra_content_class %}
{% block content %}
{%if page.extra.section %}
{% set section = get_section(path="{{page.extra.section}}/_index.md") %}
{% else %}
{% set section = get_section(path="server/_index.md") %}
{% endif %}
<div class="documentation">
<aside class="documentation__sidebar">
<ul>
{% for p in section.subsections %}
{% set subsection = get_section(path=p) %}
<li>
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
<ul>
{% for page in subsection.pages %}
<li class="{% if current_path == page.path %}active{% endif %}">
<a href="{{page.permalink}}">{{page.title}}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</aside>
<div class="documentation__content">
{% block doc_content %}
{% endblock doc_content %}
{% block next_link %} {% endblock next_link %}
</div>
</div>
{% endblock content %}
+15 -15
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en-gb">
<html lang="en-us">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -14,6 +14,7 @@
<link rel="stylesheet" href="{{ get_url(path="site.css") }}"/>
<link rel="icon" href="{{ get_url(path="favicon.ico") }}">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script> dataLayer = [{ 'visitorType': 'geek' }]; </script>
</head>
<body>
@@ -36,13 +37,13 @@
</nav>
</header>
<div class="content {% block extra_content_class %}{% endblock extra_content_class %}">
<div class="content content--reversed">
{% block content %}
<div class="hero">
<h1>Spark SDK</h1>
<p class="hero__tagline">
Spark consists of a client UI
(<a class="white" href="https://www.bluetradingsystems.com/bts-spark#ladder-algo-screen">Spark Client</a>),
(<a href="https://www.bluetradingsystems.com/bts-spark#ladder-algo-screen">Spark Client</a>),
a high-performance professional futures front end, connected via our proprietary protocol over TCP to our Spark Server, which in turn connects to execution venues via their native protocols.
Use the Spark SDK to extend Spark Server with your custom Algos, then use the Client SDK to launch and control them.
</p>
@@ -64,7 +65,7 @@
<a href="{{ get_url(path="@/server/_index.md") }}" class="button">Get started</a>
</div>
<div class="selling-point disabled">
<div class="selling-point">
<h2>
Client SDK
<i class="fa fa-gamepad" aria-hidden="true"></i>
@@ -72,19 +73,9 @@
<p>
The Spark client SDK allows you to integrate spark with your existing ecosystem. The Spark Client is built entirely on this simple, powerful API.
</p>
<a href="" class="button" disabled>Get started</a>
<a href="" class="button">Get started</a>
</div>
<!-- <div class="selling-point disabled">
<h2>
Microsoft Excel
<i class="fa fa-table" aria-hidden="true"></i>
</h2>
<p>
More of your business is built on excel than probably should be. Continue this worrying trend with the Spark Excel API.
</p>
<a href="" class="button" disabled>Get started</a>
</div> -->
</div>
</div>
@@ -94,6 +85,15 @@
© Blue Trading Systems
</footer>
<script>
if (location.hostname == "api.bluetradesys.com") {
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-56FHG8R');
}
</script>
<script type="text/javascript" src="{{ get_url(path="elasticlunr.min.js") }}"></script>
<script type="text/javascript" src="{{ get_url(path="search_index.en.js") }}"></script>
<script type="text/javascript" src="{{ get_url(path="search.js") }}"></script>
+36 -8
View File
@@ -1,13 +1,39 @@
{% extends "documentation.html" %}
{% extends "index.html" %}
{# this has access to the "page" variable but documentation.html does not #}
{% block title %}{{ page.title }} | {{ super() }} {% endblock title %}
{% block doc_content %}
<h1>{{page.title}}</h1>
{{page.content | safe}}
{% endblock doc_content %}
{% block next_link %}
<p>
{% endblock next_link %}
{% block content %}
{% set section = get_section(path=page.ancestors[1]) %}
{#
{#<div>{{ section.title }} </div>#}
<div class="documentation">
<aside class="documentation__sidebar">
<ul>
{% for p in section.subsections %}
{% set subsection = get_section(path=p) %}
<li>
<span class="documentation__sidebar__title">{{ subsection.title }}</span>
<ul>
{% for page in subsection.pages %}
<li class="{% if current_path == page.path %}active{% endif %}">
<a href="{{page.permalink}}">{{page.title}}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</aside>
<div class="documentation__content">
<h1>{{page.title}}</h1>
{{page.content | safe}}
{# next/pre links #}
<p>
{% if page.lighter %}
<a href="{{ page.lighter.permalink }}">
<i class="fa fa-chevron-left fa-fw" aria-hidden="true"></i>
@@ -21,5 +47,7 @@
<i class="fa fa-chevron-right fa-fw" aria-hidden="true"></i>
</a>
{% endif %}
</p>
{% endblock next_link %}
</p>
</div>
</div>
{% endblock content %}
+1 -1
View File
@@ -1 +1 @@
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/_index.md;content/bts_spark/Help/_index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Launcher/_index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Logging-In-to-the-System/_index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Market/_index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/_index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/Settings/_index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Spreader/_index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Trading/OrderTypes/index.md;content/bts_spark/Trading/_index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
set(sources /home/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/_index.md;content/bts_spark/Help/_index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Launcher/_index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Logging-In-to-the-System/_index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Market/_index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/_index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/Settings/_index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Spreader/_index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Trading/OrderTypes/index.md;content/bts_spark/Trading/_index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
+31
View File
@@ -4,6 +4,37 @@ weight = 3
sort_by = "weight"
insert_anchor_links = "right"
+++
## 2.15.3
### Bug Fixes
- Fixed a bug where some CME currency spreads exhibited incorrect tick values
## 2.15.2
### User Visible Changes
- Excel RTD plugin can now receive ICE market data
## 2.15.1
### Bug Fixes
- Fixed a FIX log parsing issue that resulted in exchange gateway logon problems
## 2.15.0
### User Visible Changes
- Added ability to hide Ladder status messages via Ladder Settings -> Left Panel tab -> Show/Hide groupbox -> Status Message
- Synthetic cloning now allows for any parameter to be changed, including tick size and leg contracts
- Added ability to import synthetics from a CSV file; see our manual for further details on this process
- Reduced Iceberg reloading latency
### Bug Fixes
- Fixed a bug where highlighted fills could lose their highlight status
- Fixed a bug where the (Mini) OrderBook PIQ could be displaying an old value
## 2.14.3
### Bug Fixes
- Fixed a bug regarding ICE logon messaging
## 2.14.2
### Bug Fixes
- Fixed a bug regarding ICE messaging
## 2.14.1
### Bug Fixes
- Fixed a bug regarding ICE logon authentication
## 2.14.0
### User Visible Changes
- All ladders can now be recentered by (shift or alt) + double clicking over the Price column in a ladder
+51
View File
@@ -23,7 +23,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\sp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_excel", "src\client\spark_excel\spark_excel.csproj", "{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_fix", "src\client\spark_fix\spark_fix.csproj", "{D8031C13-7BEA-430A-BB60-40927D15CFC3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_historical", "src\client\spark_historical\spark_historical.csproj", "{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}"
EndProject
Global
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
@@ -219,6 +226,50 @@ Global
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x64.Build.0 = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x86.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|Any CPU.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x64.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x64.Build.0 = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Debug|x86.ActiveCfg = Debug|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|Any CPU.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.MinSizeRel|x86.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.Release|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x64.Build.0 = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{D8031C13-7BEA-430A-BB60-40927D15CFC3}.RelWithDebInfo|x86.Build.0 = Release|x64
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|x64.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|x64.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|x86.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Debug|x86.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.MinSizeRel|x86.Build.0 = Debug|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|Any CPU.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|x64.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|x64.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|x86.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.Release|x86.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{5F64A360-43F4-4AD8-8B04-3C3F4424BCBC}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+19
View File
@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.5)
project(spark_client)
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
file(GLOB_RECURSE spark_client_sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cs)
file(GLOB_RECURSE spark_client_csproj RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.csproj)
file(GLOB_RECURSE spark_ui_sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} spark/*.cs)
file(GLOB_RECURSE spark_ui_csproj RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} spark/*.csproj)
list(REMOVE_ITEM spark_ui_sources ${spark_client_sources} )
list(REMOVE_ITEM spark_ui_csproj ${spark_client_csproj} )
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/../
COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/build_spark_dotnetcore.sh
ARGS ${CMAKE_BUILD_TYPE} ${CMAKE_BINARY_DIR}
COMMENT "Building sparkcmd in netcore3"
DEPENDS libspark ${spark_client_sources} ${spark_client_csproj} ${CMAKE_CURRENT_SOURCE_DIR}/build_spark_dotnetcore.sh)
add_custom_target(spark_client_dir ALL DEPENDS ${CMAKE_BINARY_DIR}/../)
@@ -5,6 +5,7 @@
<SparkRoot>$([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\..))</SparkRoot>
<SourceRoot>$([System.IO.Path]::GetFullPath($(SparkRoot)\src))</SourceRoot>
<Externals>$(SparkRoot)\externals\windows</Externals>
<Externals Condition=" '$(OS)' != 'Windows_NT' ">$(SparkRoot)/externals/linux</Externals>
<DepotName>$([System.IO.Path]::GetFileName($([System.IO.Path]::GetFullPath($(SparkRoot)))))</DepotName>
<ErrorReport>None</ErrorReport>
<Prefer32Bit>False</Prefer32Bit>
@@ -19,20 +20,25 @@
<OutputPath>$(BaseOutputPath)\bin</OutputPath>
<BaseIntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseOutputPath)\obj\$(MSBuildProjectName)</IntermediateOutputPath>
<BaseIntermediateOutputPath Condition=" '$(OS)' != 'Windows_NT' ">$(BaseOutputPath)\linuxobj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<IntermediateOutputPath Condition=" '$(OS)' != 'Windows_NT' ">$(BaseOutputPath)\linuxobj\$(MSBuildProjectName)</IntermediateOutputPath>
<GenDir>$(BaseOutputPath)\gen\$(MSBuildProjectName)</GenDir>
</PropertyGroup>
<Import Project="$(ComputerSpecificConfig)" Condition="Exists($(ComputerSpecificConfig))" />
<ItemGroup>
<AvailableItemName Include="Proto2">
<Targets>CompileProto3</Targets>
<Targets Condition=" '$(OS)' == 'Windows_NT' " >CompileProto2</Targets>
<Targets Condition=" '$(OS)' != 'Windows_NT' " >CompileProto2Linux</Targets>
</AvailableItemName>
<AvailableItemName Include="Proto3">
<Targets>CompileProto3</Targets>
<Targets Condition=" '$(OS)' == 'Windows_NT' " >CompileProto3</Targets>
<Targets Condition=" '$(OS)' != 'Windows_NT' " >CompileProto3Linux</Targets>
</AvailableItemName>
<AvailableItemName Include="Proto3Excel">
<Targets>CompileProto3Excel</Targets>
<Targets Condition=" '$(OS)' == 'Windows_NT' ">CompileProto3Excel</Targets>
</AvailableItemName>
</ItemGroup>
@@ -43,6 +49,14 @@
<protogen>$(Externals)\bin\protogen.exe</protogen>
</PropertyGroup>
<PropertyGroup Label="ProtoBuf" Condition=" '$(OS)' != 'Windows_NT' ">
<ProtocPluginBase>$(SparkRoot)/src/lib/api/service_compiler</ProtocPluginBase>
<ProtoInclude>$(Externals)/include</ProtoInclude>
<protoc>$(Externals)/bin/protoc</protoc>
<protoc_rename>$(Externals)/bin/lowercase_files.sh</protoc_rename>
<protogen>$(Externals)/bin/protogen</protogen>
</PropertyGroup>
<ItemDefinitionGroup>
<Proto2>
</Proto2>
@@ -3,7 +3,7 @@
<Target Name="CompileProto2"
Condition="'@(Proto2)'!=''"
Condition="'@(Proto2)'!='' AND '$(OS)' == 'Windows_NT' "
Inputs="$(MSBuildProjectFile);$(protogen);%(Proto2.FullPath)"
Outputs="$(GenDir)\proto2\%(Proto2.filename).cs"
AfterTargets="PreBuildEvent">
@@ -28,7 +28,7 @@
passes wipes out the additional compile includes that we add.
-->
<Target Name="CompileProto3" AfterTargets="GetAssemblyVersion"
Condition="'@(Proto3)'!=''"
Condition="'@(Proto3)'!='' AND '$(OS)' == 'Windows_NT' "
Inputs="$(protoc);
%(Proto3.Identity);$(ProtocPluginBase)\plugin.py;
$(ProtocPluginBase)\templates\c#\client.pb.cs;
@@ -58,7 +58,7 @@
</Target>
<Target Name="CompileProto3Excel" AfterTargets="GetAssemblyVersion"
Condition="'@(Proto3Excel)'!=''"
Condition="'@(Proto3Excel)'!='' AND '$(OS)' == 'Windows_NT' "
Inputs="$(protoc);
%(Proto3Excel.Identity);$(ProtocPluginBase)\plugin.py;
$(ProtocPluginBase)\templates\excel\excel.pb.cs;"
@@ -82,12 +82,74 @@
</ItemGroup>
</Target>
<Target Name="CompileProto2Linux"
Condition="'@(Proto2)'!='' AND '$(OS)' != 'Windows_NT' "
Inputs="$(MSBuildProjectFile);$(protogen);%(Proto2.FullPath)"
Outputs="$(GenDir)/proto2/%(Proto2.filename).cs"
AfterTargets="PreBuildEvent">
<PropertyGroup>
<SourceRelativeFile>$([System.String]::Copy('%(proto2.fullpath)').Replace($(SourceRoot), ''))</SourceRelativeFile>
<SourceRelativeDir>$([System.IO.Path]::GetDirectoryName($(SourceRelativeFile)))</SourceRelativeDir>
<WorkingDirectory>$(GenDir)/proto2</WorkingDirectory>
</PropertyGroup>
<MakeDir Directories="$(WorkingDirectory)" />
<Copy SourceFiles="%(Proto2.FullPath)" DestinationFolder="$(WorkingDirectory)" SkipUnchangedFiles="True" UseHardLinksIfPossible="True" />
<Error Text="protogen.exe does not exist. If you forgot to clone with externals try: git submodule update --init --recursive" Condition="!Exists($(protogen))" />
<Message Importance="High" Text="Doing Proto2 %(proto2.filename)" />
<Exec Command="PYTHONPATH=$(Externals)/python $(Protogen) %(Proto2.filename)%(proto2.extension) --csharp_out=. -I. -I$(SourceRoot) -I$(Externals)/include"
WorkingDirectory="$(WorkingDirectory)" />
<ItemGroup>
<Compile Include="$(WorkingDirectory)/%(Proto2.filename).cs" />
</ItemGroup>
</Target>
<Target Name="CompileProto3Linux" AfterTargets="GetAssemblyVersion"
Condition="'@(Proto3)'!='' AND '$(OS)' != 'Windows_NT' "
Inputs="$(protoc);
%(Proto3.Identity);$(ProtocPluginBase)/plugin.py;
$(ProtocPluginBase)/templates/c#/client.pb.cs;
$(ProtocPluginBase)/templates/c#/server.pb.cs;
$(ProtocPluginBase)/templates/c#/immutable.pb.cs;"
Outputs="%(Proto3.OutCS);%(Proto3.OutCSServer);%(Proto3.OutCSClient);%(Proto3.OutCSImmutable)">
<MakeDir Directories="%(Proto3.OutBase)" />
<MakeDir Directories="$(IntermediateOutputPath)/gen" />
<Message Importance="High" Text="Generating service %(Proto3.FileName)" />
<Exec Command="
PYTHONPATH=$(Externals)/python $(protoc) \
%(Proto3.FullPath) \
--error_format=gcc \
--proto_path=$(protoinc):$(SourceRoot) \
--plugin=&apos;protoc-gen-service=$(ProtocPluginBase)/plugin.py&apos; \
--service_out=lang=c#,strip_out_path=1:%(Proto3.OutBase) \
--csharp_out=%(CompileOptions)%(Proto3.OutBase)"
WorkingDirectory="%(relativedir)" />
<!-- <Exec Command="$(protoc_rename) %(Proto3.OutBase)" /> -->
<ItemGroup>
<Compile Include="%(Proto3.OutCS)" />
<Compile Include="%(Proto3.OutCSClient)" />
<Compile Include="%(Proto3.OutCSServer)" />
<Compile Include="%(Proto3.OutCSImmutable)" />
</ItemGroup>
</Target>
<!-- <Target Name="RenameProto3Linux" AfterTargets="CompileProto3Linux"
Condition="'@(Proto3)'!='' AND '$(OS)' != 'Windows_NT' ">
<Move SourceFiles="$(OutBase)\Apicore.cs" DestinationFiles="$(OutBase)\apicore.cs" />
<Move SourceFiles="$(OutBase)\Apioptions.cs" DestinationFiles="$(OutBase)\apioptions.cs" />
<Move SourceFiles="$(OutBase)\Co2.cs" DestinationFiles="$(OutBase)\co2.cs" />
</Target> -->
<Target Name="AfterClean" AfterTargets="Clean">
<Delete Files="%(Proto3.OutCS)" />
<Delete Files="%(Proto3.OutCSClient)" />
<Delete Files="%(Proto3.OutCSServer)" />
<Delete Files="%(Proto3.OutCSImmutable)" />
<Delete Files="$(GenDir)\proto2\*" />
<Delete Files="$(GenDir)/proto2/*" />
<Delete Files="%(Proto3Excel.OutCSExcel)" />
</Target>
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
#$1 = Debug / Release (CMAKE_BUILD_TYPE), $2 = CMAKE_BINARY_DIR
cd "$(dirname "$0")"
echo $PWD dotnet build -c $1 ./sparkcmd/
dotnet build -c $1 -o $2/../$1/bin/netcoreapp3.1/linux-x64 ./sparkcmd/
if [ "$1" == "Release" ]
then
dotnet publish -c $1 -o $2/../$1/bin/netcoreapp3.1/linux-x64 ./sparkcmd/
ln -fs $2/src/lib/libspark.so $2/../$1/bin/netcoreapp3.1/linux-x64/publish
fi
ln -fs $2/src/lib/libspark.so $2/../$1/bin/netcoreapp3.1/linux-x64
@@ -4,43 +4,112 @@ using mbts;
using mbts.spark;
using mbts.ui;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Windows;
namespace bts {
[Export, Export(typeof(IShellDaemon))]
class DeleteSyntheticDaemon : IShellDaemon, IDisposable {
// request_id is noted in the series of log messages that occur during a particular delete op.
static int _request_id = 1;
readonly OrdersModel _orders_model;
readonly ErrorReporter _error_reporter;
IReadOnlyDictionary<uint, ExecutionStrategyImmutable> _strategy_map =
DictionaryHelper.empty<uint, ExecutionStrategyImmutable>();
IDisposable _dispose;
[ImportingConstructor]
public DeleteSyntheticDaemon(IBus bus, OrdersModel orders_model, ErrorReporter error_reporter) {
_orders_model = orders_model;
_error_reporter = error_reporter;
_dispose = bus.subscribe(SparkTags.SyntheticDeleteTag)
.OnDispatcher()
.Subscribe(_delete_synthetic);
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SyntheticDeleteTag).Subscribe(_delete_synthetic),
orders_model.spreader.strategy.Subscribe(map => _strategy_map = map));
}
public void Dispose() =>
Disposable.dispose(ref _dispose);
async void _delete_synthetic(uint id) {
var (orders, orders_success) = await _orders_model.on_orders.FirstAsyncTask(SparkUtils.DefaultTimeOut);
async void _delete_synthetic(IImmutableSet<uint> keys) {
var log = new LoggerContext(Log.Ui, $"Synthetic Delete Req {unchecked(Interlocked.Increment(ref _request_id))} - ", null);
if (keys.is_null_or_empty()) {
log.warn("received empty delete request (no securities specified).");
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);
if (!orders_success) {
_error_reporter.report_error($"Unable to delete synthetic security ({id}): unable to determine whether or not security ({id}) has working orders (deletion is not permitted while there are working orders).");
Log.Ui.warn($"Ignoring request to delete synthetic security ({id}) because the orders lookup timed out (must confirm no working orders).");
} else if (orders.state.Values.Any(o => o.order.Def.SecurityKey == id && o.order.is_non_terminal())) {
Log.Ui.warn($"Ignoring request to delete synthetic security ({id}) because non-terminal-orders were detected.");
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).";
_error_reporter.report_error(msg);
log.warn(msg);
} else {
ExecutionStrategyImmutable? strategy = await _orders_model.spreader.lookup_strategy_async(id);
if (strategy.HasValue) {
ExecutionStrategyImmutable deleted_strategy = strategy.Value.with(deleted: true);
await _orders_model.spreader.save_strategy(id, deleted_strategy);
var working_orders = new List<uint>();
var missing_strategies = new List<uint>();
var items = new List<(uint key, ExecutionStrategyImmutable strategy)>();
var temp_strats = _strategy_map;
foreach (var k in keys) {
if (orders.state.TryGetValue(k, out var sec_group) && sec_group.table.state.Values.Any(tup => tup.order.is_active())) {
working_orders.Add(k);
} else if (!temp_strats.TryGetValue(k, out var strat)) {
missing_strategies.Add(k);
} else {
items.Add((k, strat.with(deleted: true)));
}
}
var lines = new List<string>();
if (working_orders.Count > 0) {
lines.Add($"skipping {working_orders.Count} securities because they have working orders.");
}
if (missing_strategies.Count > 0) {
lines.Add($"skipping {missing_strategies.Count} securities because their execution strategies were not discovered.");
}
if (lines.Count > 0)
log.warn(string.Join("; ", lines));
var errors = await _orders_model.spreader.save_strategies(items);
int error_count = errors.Count(e => !string.IsNullOrEmpty(e));
log.info($"{items.Count - error_count}/{items.Count} attempted deletes were successful (skipped {working_orders.Count + missing_strategies.Count}).");
// display results to user
const string dialog_title = "Synthetic Deletion";
string display_msg;
MessageBoxImage icon = MessageBoxImage.None;
if (keys.Count > 1) {
if (error_count == 0 && working_orders.Count == 0 && missing_strategies.Count == 0) {
display_msg = $"Synthetic securities have been deleted.";
icon = MessageBoxImage.Information;
} else {
display_msg = $"{keys.Count - error_count - working_orders.Count - missing_strategies.Count}/{keys.Count} securities were successfully deleted.";
icon = MessageBoxImage.Error;
}
} else {
if (error_count == 0 && working_orders.Count == 0 && missing_strategies.Count == 0) {
display_msg = "Synthetic security has been deleted.";
icon = MessageBoxImage.Information;
} else {
string reason;
if (error_count > 0) {
reason = errors[0];
} else if (missing_strategies.Count > 0) {
reason = "did not find execution strategy.";
} else if (working_orders.Count > 0) {
reason = "working orders detected.";
} else {
// unexpected code path
reason = "unknown error.";
}
icon = MessageBoxImage.Error;
display_msg = $"Unable to delete synthetic security: " + reason;
}
}
BTSMessageBox.Show(display_msg, dialog_title, BTSMessageBoxButtons.OK, icon);
}
}
}
}
}
+44 -1
View File
@@ -1,12 +1,15 @@
using bts.api;
using bts.api.core;
using bts.support;
using bts.utils;
using mbts;
using mbts.spark;
using mbts.ui;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
@@ -164,5 +167,45 @@ namespace bts.spark {
void _report_error(string msg) =>
error_reporter.report_error($"Error {desc}: {msg}", AlgoStudioWindow.NAME, MessageBoxImage.Warning);
}
public static async Task<(TResult result, string error_msg)[]> safe_execute_batch<T, TResult>(
this ErrorReporter error_reporter,
IEnumerable<Task<T>> execute,
Func<T, (TResult result, string error_msg)> result_selector,
string desc,
string title) {
//
try {
var results = await Task.WhenAll(execute as Task<T>[] ?? ArrayHelper.clone_optimized(execute));
var result_values = new (TResult, string)[results.Length];
StringBuilder error_bd = null;
for (int i = 0; i < results.Length; i++) {
var r = results[i];
var (result, error) = result_selector(r);
if (!string.IsNullOrEmpty(error)) {
if (error_bd == null) {
error_bd = new StringBuilder();
} else {
error_bd.AppendLine();
}
error_bd.Append(error);
}
result_values[i] = (result, error);
}
if (error_bd != null) {
var error_msg = error_bd.ToString();
if (!string.IsNullOrEmpty(error_msg))
_report_error(error_msg);
}
return result_values;
}
catch (Exception ex) {
_report_error(ex.ToString());
}
return null;
//
void _report_error(string msg) =>
error_reporter.report_error($"Error {desc}: {msg}", title, MessageBoxImage.Warning);
}
}
}
}
+2 -2
View File
@@ -39,7 +39,7 @@ namespace bts {
_relay.set_token(_token.Value);
_dispose.Add(
_ss.Value.subscribei_metadata()
.Select(upd => upd.all.ToDictionary(i => i.SecurityKey, i => i.Strategy))
.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
@@ -64,4 +64,4 @@ namespace bts {
Task.Run(run);
}
}
}
}
+6 -1
View File
@@ -1,4 +1,5 @@
using bts;
using mbts.ui.dpi;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -55,7 +56,9 @@ namespace mbts.ui {
StarGeometryKey = nameof(StarGeometryKey),
ToolsGeometryKey = nameof(ToolsGeometryKey),
TrashGeometryKey = nameof(TrashGeometryKey),
UndoCircularGeometryKey = nameof(UndoCircularGeometryKey);
UndoCircularGeometryKey = nameof(UndoCircularGeometryKey),
WarningExclamationGeometryKey = nameof(WarningExclamationGeometryKey),
WarningGeometryKey = nameof(WarningGeometryKey);
public static Geometry PlayGeometry => Geometry.Parse(get_geometric_path(PlayGeometryKey).FirstOrDefault() ?? string.Empty);
@@ -190,6 +193,8 @@ namespace mbts.ui {
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 Wrench { get { return load_resource_and_freeze("Wrench"); } }
+12
View File
@@ -363,6 +363,18 @@
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>
<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
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.WarningGeometryKey}">
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
M15.999277,2.3337629L2.3830507,22.996989 29.615442,22.996989z
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>
<!--
'y' for equilateral triangle of side length x:
(x/2)^2 + Y^2 = x^2
@@ -1,6 +1,7 @@
using bts.api;
using bts.co2;
using bts.collections;
using bts.utils.csv;
using mbts.persist;
using mbts.spark.filters;
using mbts.ui;
@@ -52,7 +53,7 @@ namespace mbts.spark {
columns.Add(new RenderTextColumn<Row, TimeAndSalesView.Columns>(col_key, r => r[col_key], filter, filter_template, r => _get_aggressor_brush(r))
{ id = col_id(col_key), Header = header, Width = width, sort_member_path = sort_member_path, horizontal_alignment = h_alignment }
);
// be sure to load GridSettings AFTER the columns collection has been populated, the grid settings manager looks columns by id to apply the appropriate settings,
// if the collection is not populated, the column settings will not be applied
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, row_vms);
@@ -173,6 +174,19 @@ namespace mbts.spark {
}
}
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(
typeof(TimeAndSalesVM.Row),
new string[] {
nameof(security),
nameof(time),
nameof(price),
nameof(size),
nameof(aggressor_side),
nameof(security_key),
});
public void raise_invalidated() => invalidated?.Invoke();
}
}
@@ -3,6 +3,7 @@ using bts.api;
using bts.shell;
using bts.shell.fills;
using bts.spark;
using bts.utils.csv;
using mbts.co2;
using mbts.persist;
using mbts.ui;
@@ -63,6 +64,7 @@ namespace mbts.spark {
var current_smd_source = connectable_builder.create(vm => vm.current_security_md_observable(_bus)).OnDispatcher();
var selected_secs_source = connectable_builder.create(vm => vm.selected_securities_observable()).OnDispatcher();
_commands = new SparkCommand[] {
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.value.row_vms.ToCsv(TimeAndSalesVM.Row.csv_ordered_properties), "TaS")),
SparkCommands.ShowLadder(SparkCommands.KeyGestures.ShowLadder).complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity(SparkCommands.KeyGestures.ShowOrderTicket).complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills(SparkCommands.KeyGestures.ShowFills).complete(selected_secs_source, always_true, get_fills_startup_data),
@@ -140,7 +140,7 @@
<DataTemplate>
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:ConditionalQuoting.Types.PriceType}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
</usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
<usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
<DataTemplate>
<Grid IsEnabled="{Binding enabled}">
@@ -333,13 +333,61 @@
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ContractPickerButton" TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="ToolTip" Value="Change Security"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
x:Name="Bd"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Padding="{TemplateBinding Padding}"
>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="MouseStates">
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd" Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="#24FFFFFF"/>
</Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bd" Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="#40FFFFFF"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border.Background>
<SolidColorBrush/>
</Border.Background>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataGridTextColumn x:Key="{x:Static usp:ExecutionStrategyEditor.AddColumnPlaceholderKey}" x:Shared="False" IsReadOnly="True">
<DataGridTextColumn.Header>
<c:PopupControl
ShowExpanderArrow="False" PopupPlacement="Right" Header="Add Leg" Padding="6,0"
ShowExpanderArrow="False"
PopupPlacement="Right"
Header="Add Leg"
Padding="6,0"
Background="{StaticResource Button.Static.BackgroundBrush}"
IsOpen="{Binding IsNewLegPopupOpen, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}, Mode=TwoWay}"
IsEnabled="{Binding CanUserAddNewLeg, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}}"
VerticalAlignment="Center"
>
<msp:ContractPickerView
CloseOnOK="False" CloseOnCancel="False"
@@ -348,7 +396,7 @@
</c:PopupControl>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.RemovableColumnHeaderTemplateKey}">
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.RemovableColumnHeaderTemplateKey}" DataType="{x:Type usp:LegSpreaderParamsTuple}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@@ -356,13 +404,40 @@
</Grid.ColumnDefinitions>
<Button
Command="{Binding RemoveLegCommand, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}}"
CommandParameter="{Binding}" Style="{StaticResource RoundCloseButton}"
CommandParameter="{Binding}"
Style="{StaticResource RoundCloseButton}"
ToolTip="Remove Leg"
/>
<TextBlock Grid.Column="1" Margin="4,0,0,0" Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
<c:ContractPickerPopup
Grid.Column="1"
BorderThickness="0"
ButtonStyle="{StaticResource ContractPickerButton}"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding security}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding ContractPickerModel.all_securities, RelativeSource={RelativeSource AncestorType=usp:ExecutionStrategyEditor}, Mode=OneWay}"
SelectedSecurity="{Binding security}"
ShowDialogSkirt="True"
>
<c:ContractPickerPopup.Background>
<SolidColorBrush/>
</c:ContractPickerPopup.Background>
<c:ContractPickerPopup.HeaderTemplate>
<DataTemplate>
<TextBlock Foreground="{x:Static u:BTSColors.header_text_brush}" Text="{Binding}"/>
</DataTemplate>
</c:ContractPickerPopup.HeaderTemplate>
</c:ContractPickerPopup>
</Grid>
</DataTemplate>
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.NonRemovableColumnHeaderTemplateKey}">
<TextBlock Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
<DataTemplate x:Key="ReadOnlyColumnHeaderTemplate" DataType="{x:Type usp:LegSpreaderParamsTuple}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding security}"
/>
</DataTemplate>
<ControlTemplate x:Key="ExecutionStrategyEditorTemplate" TargetType="usp:ExecutionStrategyEditor">
<Border
@@ -560,6 +635,14 @@
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="5,0"/>
<Setter Property="MinHeight" Value="26"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupItemStyle}"/>
</DataGrid.GroupStyle>
@@ -227,8 +227,8 @@ namespace mbts.ui.spark {
}
DataGridColumn _column_factory(LegSpreaderParamsTuple l) {
var col = new DataGridColumnInternal(l.leg.Security, ParameterCellTemplateConverter, ParameterCellEditingTemplateConverter);
col.HeaderTemplate = (DataTemplate)FindResource(IsNew ? RemovableColumnHeaderTemplateKey : NonRemovableColumnHeaderTemplateKey);
var col = new DataGridColumnInternal(l, ParameterCellTemplateConverter, ParameterCellEditingTemplateConverter);
col.HeaderTemplate = TryFindResource(IsNew ? RemovableColumnHeaderTemplateKey : NonRemovableColumnHeaderTemplateKey) as DataTemplate;
DataGridEx.SetRightBorder(col, new DataGridExVerticalLine(false, true, Colors.DarkGray, 1));
return col;
}
@@ -237,10 +237,12 @@ namespace mbts.ui.spark {
private class DataGridColumnInternal : DataGridDynamicColumn {
readonly IValueConverter _cell_template_converter, _cell_editing_template_converter;
readonly LegSpreaderParamsTuple _leg_tup;
public DataGridColumnInternal(Security security, IValueConverter cell_template_converter, IValueConverter cell_editing_template_converter) {
Header = security;
SetValue(DataGridEx.CellKeyProperty, security);
public DataGridColumnInternal(LegSpreaderParamsTuple leg_tup, IValueConverter cell_template_converter, IValueConverter cell_editing_template_converter) {
_leg_tup = leg_tup;
Header = leg_tup;
SetValue(DataGridEx.CellKeyProperty, leg_tup);
_cell_template_converter = cell_template_converter;
_cell_editing_template_converter = cell_editing_template_converter;
}
@@ -248,8 +250,8 @@ namespace mbts.ui.spark {
// I decided this was an easy and simple enough solution to justify breaking the mvvm paradigm
// The alternative - within strict bounds of the paradigm - is to change the DataGridParameterCellTemplateConverter into an IMultiValueConverter, bound to data_type AND read_only
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) {
if (dataItem is SyntheticEditorVM.RowVM row_vm) {
if (row_vm.read_only || row_vm.get_cell(Header) is SyntheticEditorVM.ICell cell_vm && !cell_vm.enabled)
if (dataItem is RowVM row_vm) {
if (row_vm.read_only || row_vm.get_cell(_leg_tup) is ICell cell_vm && !cell_vm.enabled)
return base.GenerateElement(cell, dataItem);
}
return base.GenerateEditingElement(cell, dataItem);
@@ -352,4 +354,4 @@ namespace mbts.ui.spark {
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,25 @@
<UserControl
x:Class="bts.shell.spreader.ContractLegTypeView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:spr="clr-namespace:bts.shell.spreader"
>
<ItemsControl ItemsSource="{Binding Controller.flags, RelativeSource={RelativeSource AncestorType=spr:ContractLegTypeView}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox
Margin="2"
IsChecked="{Binding is_selected}"
Content="{Binding value, Converter={x:Static spr:ContractLegTypeView.LegTypeShortDescConverter}}"
ToolTip="{Binding value, Converter={x:Static spr:ContractLegTypeView.LegTypeLongDescConverter}}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
@@ -0,0 +1,104 @@
using bts.collections;
using mbts;
using mbts.ui;
using mbts.ui.converters;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace bts.shell.spreader {
static class EnumFlagItemsVM {
public static EnumFlagItemsVM<T> create<T>(IEnumerable<T> flags) where T : struct =>
new EnumFlagItemsVM<T>(flags);
}
class EnumFlagItemsVM<T> : ViewModelBase where T : struct {
readonly ObservableCollection<SelectableItemVM<T>> _flags = new ObservableCollection<SelectableItemVM<T>>();
bool _is_busy;
T _result;
public EnumFlagItemsVM()
: this(EnumHelper.get_values<T>()) { }
public EnumFlagItemsVM(IEnumerable<T> flags) {
this.flags = ObservableAutoWrapper.create_read_only_wrapper(_flags);
_flags.AddRange(flags.Select(f => new SelectableItemVM<T>(f)));
foreach (var vm in _flags) {
vm.is_selected_changed += on_is_selected_changed;
void on_is_selected_changed(bool value) {
if (!_is_busy) {
_is_busy = true;
result = value ?
EnumHelper.add_flags(result, vm.value) :
EnumHelper.remove_flags(result, vm.value);
_is_busy = false;
}
}
}
}
public event Action result_changed;
public IReadOnlyObservableCollection<SelectableItemVM<T>> flags { get; }
public T result {
get => _result;
set {
if (!EqualityComparer<T>.Default.Equals(_result, value)) {
_result = value;
if (!_is_busy) {
_is_busy = true;
for (int i = 0; i < flags.Count; i++)
flags[i].is_selected = EnumHelper.has_flags(value, flags[i].value);
_is_busy = false;
}
result_changed?.Invoke();
RaisePropertyChanged();
}
}
}
}
/// <summary>
/// Interaction logic for ContractLegTypeView.xaml
/// </summary>
partial class ContractLegTypeView : UserControl {
public static IValueConverter LegTypeShortDescConverter =>
ValueConverter.create<ContractLegType, string>(SyntheticSecurityParser.display_name);
public static IValueConverter LegTypeLongDescConverter =>
ValueConverter.create<ContractLegType, string>(lt =>
string.Join(" & ", EnumHelper.split_flags(lt).Select(f => EnumHelper.get_display_name(f))));
public ContractLegTypeView() {
InitializeComponent();
Controller.result = Value;
Controller.result_changed += _on_controller_result_changed;
}
public static readonly DependencyProperty ValueProperty =
WPFHelper.create_dp<ContractLegType, ContractLegTypeView>(
nameof(Value),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, v) => {
o.Controller.result_changed -= o._on_controller_result_changed;
o.Controller.result = v;
o.Controller.result_changed += o._on_controller_result_changed;
});
public ContractLegType Value {
get => (ContractLegType)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public EnumFlagItemsVM<ContractLegType> Controller { get; } = EnumFlagItemsVM.create(EnumHelper.get_values_except(ContractLegType.None));
void _on_controller_result_changed() =>
SetCurrentValue(ValueProperty, Controller.result);
}
}
@@ -0,0 +1,47 @@
<Window
x:Class="bts.shell.spreader.SyntheticImportConflictsDialog"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:spr="clr-namespace:bts.shell.spreader"
u:WindowStyle.AllowResize="True"
u:WindowStyle.SizeToContent="WidthAndHeight"
u:WindowStyle.TitleContent="Import Synthetics Conflicts"
Style="{StaticResource BTSDialogTitleTemplate}"
>
<Window.MaxHeight>
<Binding Path="SizeToContent" RelativeSource="{RelativeSource Self}">
<Binding.Converter>
<cv:WindowMaxHeightConverter SizeToContentThreshold="{u:DPIDouble 700}"/>
</Binding.Converter>
</Binding>
</Window.MaxHeight>
<u:WindowStyle.TitleContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Image Height="12" Margin="5,2" Source="{x:Static u:Icons.Import}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</u:WindowStyle.TitleContentTemplate>
<Window.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<spr:SyntheticImportConflictsView Margin="12,10,12,8"/>
<c:DialogSkirt Grid.Row="1">
<c:DialogSkirt.RightItems>
<x:Array Type="{x:Type Button}">
<Button Command="{Binding ok_cmd}">Continue</Button>
<Button Command="{Binding cancel_cmd}" IsCancel="True">Cancel</Button>
</x:Array>
</c:DialogSkirt.RightItems>
</c:DialogSkirt>
</Grid>
</Window.Content>
</Window>
@@ -0,0 +1,11 @@
using System.Windows;
namespace bts.shell.spreader {
/// <summary>
/// Interaction logic for SyntheticImportConflictsDialog.xaml
/// </summary>
partial class SyntheticImportConflictsDialog : Window {
public SyntheticImportConflictsDialog() =>
InitializeComponent();
}
}
@@ -0,0 +1,101 @@
using bts.api;
using bts.collections;
using bts.utils;
using mbts;
using mbts.spark;
using mbts.ui;
using mbts.user.settings;
using System.Collections.Generic;
using System.Linq;
namespace bts.shell.spreader {
readonly struct SyntheticComponent {
public readonly SyntheticContractComponent type;
public readonly int leg_index;
public SyntheticComponent(SyntheticContractComponent type, int leg_index) {
this.type = type;
this.leg_index = leg_index;
}
}
class SyntheticImportConflictsVM : ImportConflictsVM<(SyntheticImportRowVM synth, ContractLegVM leg), SyntheticImportRowVM, SyntheticContractComponent>, IRequestViewClose {
IReadOnlyDictionary<uint, SecurityMD> _security_map = DictionaryHelper.empty<uint, SecurityMD>();
public SyntheticImportConflictsVM(int num_synthetics)
: base(num_synthetics, tup => tup.synth) { }
// misc items sources
public IReadOnlyDictionary<uint, SecurityMD> security_map {
get => _security_map;
set {
if (!ReferenceEquals(_security_map, value)) {
_security_map = value;
_resolve_securities();
RaisePropertyChanged();
}
}
}
void _resolve_securities() {
if (_security_map == null)
return;
var reverse_lookup = SyntheticSecurityParser.reverse_lookup(MappedDictionary.create(_security_map, v => v.security.ToString()));
foreach (var r in conflicts) {
if (r.component == SyntheticContractComponent.Security && r.has_error) {
var id = reverse_lookup(r.text);
if (id != 0 && _security_map.TryGetValue(id, out var smd))
r.value = smd.security;
}
}
}
}
class SyntheticImportConflictRowVM : ImportConflictRowVM<(SyntheticImportRowVM synth, ContractLegVM leg), SyntheticContractComponent>, ITemplateSelectorKey {
public SyntheticImportConflictRowVM(
SyntheticContractComponent component,
string text,
IReadOnlyCollection<(SyntheticImportRowVM synth, ContractLegVM leg)> affected_parts,
object error_value,
object value)
: base(component, text, affected_parts, error_value, value, affected_parts.DistinctOn(tup => tup.synth).Count()) { }
public override void on_conflicts_resolved() {
if (has_error)
return;
foreach (var (synth, leg) in affected_parts) {
switch (component) {
case SyntheticContractComponent.Name:
if (value is string str)
synth.name = str;
break;
case SyntheticContractComponent.TickSize:
if (value is double ts && ts.IsNonZero()) {
synth.tick_factors = TickUtils.get_regional_ticks(new ContractSpecificationImmutable(new ContractSpecification {
LowTick = ts,
HighTick = ts
})).low_tick;
} else if (value is TickFactors tf && tf.is_valid())
synth.tick_factors = tf;
break;
case SyntheticContractComponent.Security:
if (value is SecurityImmutable sec && sec.is_exchange_security())
leg.security = sec;
break;
case SyntheticContractComponent.PriceMultiplier:
if (value is double pm && pm.IsNonZero())
leg.price_multiplier = pm;
break;
case SyntheticContractComponent.Quantity:
if (value is double q && q.IsNonZero())
leg.quantity = q;
break;
case SyntheticContractComponent.LegType:
if (value is ContractLegType lt)
leg.leg_type = lt;
break;
}
}
}
}
}
@@ -0,0 +1,199 @@
<UserControl
x:Class="bts.shell.spreader.SyntheticImportConflictsView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:spr="clr-namespace:bts.shell.spreader"
MinWidth="400"
>
<UserControl.Resources>
<Style x:Key="VerticallyCenteredText" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!--Value Cell Template Selector-->
<u:KeyedTemplateSelector x:Key="ValueCellTemplateSelector">
<u:KeyedTemplateSelector.Default>
<DataTemplate>
<TextBlock Text="{Binding value}" VerticalAlignment="Center"/>
</DataTemplate>
</u:KeyedTemplateSelector.Default>
<u:TemplateSelectorKeyValue Key="PriceMultiplier">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={StaticResource DoubleTextConverter}}" VerticalAlignment="Center"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="Quantity">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={StaticResource DoubleTextConverter}}" VerticalAlignment="Center"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="Security">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={x:Static cv:ValueConverters.SecuritySelectorText}}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="LegType">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={x:Static spr:ContractLegTypeView.LegTypeShortDescConverter}}">
<TextBlock.ToolTip>
<TextBlock>
<Run Text="{Binding value, Converter={x:Static spr:ContractLegTypeView.LegTypeLongDescConverter}}"/>
<LineBreak/>
<Run Text="Q = Quote"/>
<LineBreak/>
<Run Text="H = Hedge"/>
<LineBreak/>
<Run Text="N = None"/>
</TextBlock>
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</u:KeyedTemplateSelector>
<!--Value Cell Editing Template Selector-->
<u:KeyedTemplateSelector x:Key="ValueCellEditingTemplateSelector">
<u:TemplateSelectorKeyValue Key="Security">
<DataTemplate>
<c:ContractPickerPopup
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding value, Converter={x:Static cv:ValueConverters.SecuritySelectorText}}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=DataGrid}, Mode=OneWay}"
SelectedSecurity="{Binding value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ShowDialogSkirt="True"
/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="Name">
<DataTemplate>
<TextBox u:Behavior.FocusOnLoaded="True" Text="{Binding value}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="TickSize">
<DataTemplate>
<c:TickUpDownDouble Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="PriceMultiplier">
<DataTemplate>
<c:TickUpDownDouble Value="{Binding value}" DisplayConverter="{StaticResource DoubleTextConverter}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="Quantity">
<DataTemplate>
<c:TickUpDownDouble Value="{Binding value}" DisplayConverter="{StaticResource DoubleTextConverter}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="LegType">
<DataTemplate>
<spr:ContractLegTypeView Value="{Binding value}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</u:KeyedTemplateSelector>
</UserControl.Resources>
<!--Content-->
<Grid x:Name="content_root">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image VerticalAlignment="Center" Margin="0,0,8,0">
<Image.Source>
<Binding Source="{x:Static MessageBoxImage.Exclamation}">
<Binding.Converter>
<cv:SystemIconConverter/>
</Binding.Converter>
</Binding>
</Image.Source>
</Image>
<Canvas x:Name="text_canvas" Grid.Column="1" HorizontalAlignment="Stretch"/>
<TextBlock Grid.Column="1" TextWrapping="Wrap" Width="{Binding ActualWidth, ElementName=text_canvas}">
<Run Text="{Binding num_successfully_parsed_rows, Mode=OneWay}"/>
<Run Text="/"/>
<Run Text="{Binding num_rows, Mode=OneWay}"/>
<Run Text=" synthetics were successfully converted."/>
<Run Text="Resolve conflicts below by selecting a value to assign each affected synthetic,"/>
<Run Text="or modify individual synthetics in the main window."/>
<LineBreak/>
<LineBreak/>
<Run Text="Click 'Continue' to proceed to the main window."/>
</TextBlock>
<DataGrid
x:Name="data_grid"
u:DataGridBehavior.SingleClickEdit="True"
Grid.Row="1"
Grid.ColumnSpan="2"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserSortColumns="True"
HeadersVisibility="All"
ItemsSource="{Binding conflicts_view}"
Margin="0,8,0,0"
MinRowHeight="17"
RowHeaderWidth="22"
SelectionMode="Single"
>
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
<Grid Background="Green">
<Border x:Name="exclamation" HorizontalAlignment="Center">
<Image Height="12" VerticalAlignment="Center" Source="{x:Static u:Icons.WarningExclamation}"/>
</Border>
<ContentControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" ToolTip="{Binding (Validation.Errors), RelativeSource={RelativeSource AncestorType=DataGridRow}}">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Grid Background="Transparent">
<Grid.ToolTip>
<ToolTip Content="{Binding ToolTip, RelativeSource={RelativeSource TemplatedParent}}">
<ToolTip.ContentTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}" DisplayMemberPath="ErrorContent"/>
</DataTemplate>
</ToolTip.ContentTemplate>
</ToolTip>
</Grid.ToolTip>
</Grid>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Grid>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ValidationErrorTemplate" Value="{Binding RowValidationErrorTemplate, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource SelectedRowBackground}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Component" Binding="{Binding component, Converter={StaticResource EnumDescriptionConverter}}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTextColumn Header="Text" Binding="{Binding text}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTextColumn Header="Affected Synthetics" Binding="{Binding num_affected_rows}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTemplateColumn Header="Value" CellTemplateSelector="{StaticResource ValueCellTemplateSelector}" CellEditingTemplateSelector="{StaticResource ValueCellEditingTemplateSelector}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
@@ -0,0 +1,20 @@
using System.Windows.Controls;
namespace bts.shell.spreader {
enum SyntheticContractComponent {
Name,
TickSize,
Security,
PriceMultiplier,
Quantity,
LegType
}
/// <summary>
/// Interaction logic for SyntheticImportConflictsView.xaml
/// </summary>
partial class SyntheticImportConflictsView : UserControl {
public SyntheticImportConflictsView() =>
InitializeComponent();
}
}
@@ -0,0 +1,45 @@
<Window
x:Class="bts.shell.spreader.SyntheticImportDialog"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:spr="clr-namespace:bts.shell.spreader"
u:WindowStyle.AllowResize="True"
u:WindowStyle.SizeToContent="WidthAndHeight"
u:WindowStyle.TitleContent="Import Synthetics"
Style="{StaticResource BTSDialogTitleTemplate}"
Content="{Binding}"
>
<Window.MaxHeight>
<Binding Path="SizeToContent" RelativeSource="{RelativeSource Self}">
<Binding.Converter>
<cv:WindowMaxHeightConverter SizeToContentThreshold="{u:DPIDouble 700}"/>
</Binding.Converter>
</Binding>
</Window.MaxHeight>
<u:WindowStyle.TitleContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Image Height="12" Margin="5,2" Source="{x:Static u:Icons.Import}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</u:WindowStyle.TitleContentTemplate>
<Window.ContentTemplate>
<DataTemplate DataType="{x:Type spr:SyntheticImportVM}">
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<spr:SyntheticImportView/>
<c:DialogSkirtOKCancel Grid.Row="1" OKCommand="{Binding ok_cmd}" CancelCommand="{Binding cancel_cmd}"/>
</Grid>
</AdornerDecorator>
</DataTemplate>
</Window.ContentTemplate>
</Window>
@@ -0,0 +1,11 @@
using System.Windows;
namespace bts.shell.spreader {
/// <summary>
/// Interaction logic for SyntheticImportDialog.xaml
/// </summary>
partial class SyntheticImportDialog : Window {
public SyntheticImportDialog() =>
InitializeComponent();
}
}
@@ -0,0 +1,236 @@
using mbts;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Data;
namespace bts.shell.spreader {
class SyntheticImportRowCollectionView : ListCollectionView {
ReadOnlyObservableCollection<object> _base_groups, _groups_wrapper;
ObservableAutoWrapper<object, object> _groups;
public SyntheticImportRowCollectionView(IList list)
: base(list) { }
public override bool CanGroup => true;
// Must return null if there are no GroupDescriptions (i.e., instead of an empty collection).
// If an empty collection is returned, the view assumes grouping is active and there are no items.
public override ReadOnlyObservableCollection<object> Groups {
get {
if ((GroupDescriptions?.Count ?? 0) == 0)
return null;
if (_groups_wrapper == null) {
_base_groups = base.Groups;
if (_base_groups == null)
return null;
_groups = new ObservableAutoWrapper<object, object>(_base_groups, obj => new SyntheticImportRowCollectionViewGroup((CollectionViewGroup)obj, this));
_groups_wrapper = new ReadOnlyObservableCollection<object>(_groups);
}
return _groups_wrapper;
}
}
public void set_all_expanded(bool is_expanded) {
foreach (var g in Groups.OfTypeSafe<SyntheticImportRowCollectionViewGroup>())
g.set_all_expanded(is_expanded);
}
}
class SyntheticImportRowCollectionViewGroup : CollectionViewGroup, INotifyDataErrorInfo, IRaiseNotifyPropertyChanged, IEquatable<SyntheticImportRowCollectionViewGroup> {
static readonly string _desc_delim = ", ";
readonly SyntheticImportRowCollectionView _root;
readonly CollectionViewGroup _from;
readonly Dictionary<object, InnerGroupSubscription> _inner_groups = new Dictionary<object, InnerGroupSubscription>();
readonly Dictionary<int, IDisposable> _rows = new Dictionary<int, IDisposable>();
string
_leg_type_desc,
_security_desc,
_quantity_desc,
_price_multiplier_desc;
IDisposable _dispose;
bool _is_expanded;
public SyntheticImportRowCollectionViewGroup(CollectionViewGroup from, SyntheticImportRowCollectionView root) : base(from.Name) {
_root = root;
_from = from;
((INotifyCollectionChanged)_from.Items).CollectionChanged += _from_items_CollectionChanged;
_from_items_CollectionChanged(_from.Items, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, _from.Items, 0));
}
public event Action ProtectedItemCountChanged;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public override bool IsBottomLevel => _from.IsBottomLevel;
public bool is_expanded { get => _is_expanded; set => _set(ref _is_expanded, value); }
public int level => parent == null ? 1 : parent.level + 1;
public SyntheticImportRowCollectionViewGroup parent { get; private set; }
// leg type desc
public string leg_type_desc =>
_leg_type_desc ?? (_leg_type_desc = string.Join(_desc_delim, ProtectedItems.OfType<ContractLegVM>().Select(l => l.leg_type.display_name())));
// price multiplier desc
public string price_multiplier_desc =>
_price_multiplier_desc ?? (_price_multiplier_desc = string.Join(_desc_delim, ProtectedItems.OfType<ContractLegVM>().Select(l => l.price_multiplier)));
// quantity desc
public string quantity_desc =>
_quantity_desc ?? (_quantity_desc = string.Join(_desc_delim, ProtectedItems.OfType<ContractLegVM>().Select(l => l.quantity)));
// security desc
public string security_desc =>
_security_desc ?? (_security_desc = string.Join(_desc_delim, ProtectedItems.OfType<ContractLegVM>().Select(l => l.security?.ToString() ?? "?").ExceptNull()).if_null_or_empty("(No Legs)"));
new int ProtectedItemCount {
get => base.ProtectedItemCount;
set {
if (base.ProtectedItemCount != value) {
base.ProtectedItemCount = value;
ProtectedItemCountChanged?.Invoke();
}
}
}
public bool HasErrors => Items.OfType<ContractLegVM>().Any(l => l.HasErrors);
public IEnumerable GetErrors(string propertyName) =>
propertyName == nameof(Items) ?
Items.OfType<ContractLegVM>().SelectMany(l => l.AllErrors) :
Enumerable.Empty<string>();
public void set_all_expanded(bool is_expanded) {
this.is_expanded = is_expanded;
foreach (var g in Items.OfTypeSafe<SyntheticImportRowCollectionViewGroup>())
g.set_all_expanded(is_expanded);
}
public bool Equals(SyntheticImportRowCollectionViewGroup other) =>
other != null &&
level == other.level &&
Equals(Name, other.Name);
void IRaiseNotifyPropertyChanged.raise_notify_property_changed(string prop) =>
OnPropertyChanged(new PropertyChangedEventArgs(prop));
void _add_item(int index, object item) {
Log.assert(index >= 0, "Index is less than 0.");
if (item is CollectionViewGroup group) {
var inner_group = new SyntheticImportRowCollectionViewGroup(group, _root);
_inner_groups.Add(group.Name, new InnerGroupSubscription(this, inner_group));
ProtectedItems.Insert(index, inner_group);
} else {
var row = (ContractLegVM)item;
if (_rows.TryPopValue(row.id, out var sub))
sub.Dispose();
_rows[row.id] = _connect_row(row);
ProtectedItems.Insert(index, item);
}
}
void _remove_item(int index, object item) {
if (item is SyntheticImportRowCollectionViewGroup inner_group) {
if (!_inner_groups.TryPopValue(inner_group.Name, out var ig_sub))
throw new InvalidOperationException($"Inner groups cache does not have an entry for the specified name '{inner_group.Name}' (entire tree structure may be compromised).");
ig_sub.Dispose();
Disposable.dispose(ref inner_group._dispose);
}
ProtectedItems.RemoveAt(index);
}
void _from_items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
for (int i = 0; i < e.NewItems.Count; i++)
_add_item(e.NewStartingIndex + i, e.NewItems[i]);
break;
case NotifyCollectionChangedAction.Remove:
for (int i = 0; i < e.OldItems.Count; i++)
_remove_item(e.OldStartingIndex, ProtectedItems[e.OldStartingIndex]);
break;
case NotifyCollectionChangedAction.Replace:
for (int i = 0; i < e.OldItems.Count; i++)
_remove_item(e.OldStartingIndex, ProtectedItems[e.OldStartingIndex]);
for (int i = 0; i < e.NewItems.Count; i++)
_add_item(e.NewStartingIndex + i, e.NewItems[i]);
break;
case NotifyCollectionChangedAction.Reset:
while (ProtectedItems.Count > 0)
_remove_item(0, ProtectedItems[0]);
for (int i = 0; i < _from.Items.Count; i++)
_add_item(i, _from.Items[i]);
break;
}
}
void _invalidate_props(params string[] props) {
for (int i = 0; i < props.Length; i++)
OnPropertyChanged(new PropertyChangedEventArgs(props[i]));
}
void _set<T>(ref T backing, T new_value, [CallerMemberName]string prop = "") =>
_set(ref backing, new_value, EqualityComparer<T>.Default, prop);
void _set<T>(ref T backing, T new_value, IEqualityComparer<T> comparer, [CallerMemberName]string prop = "") {
if (!comparer.Equals(backing, new_value)) {
backing = new_value;
OnPropertyChanged(new PropertyChangedEventArgs(prop));
}
}
IDisposable _connect_row(ContractLegVM row) {
row.result_changed += on_result_changed;
row.ErrorsChanged += on_errors_changed;
return Disposable.Create(() => {
row.result_changed -= on_result_changed;
row.ErrorsChanged -= on_errors_changed;
});
void on_result_changed() {
_security_desc = null;
_price_multiplier_desc = null;
_quantity_desc = null;
_leg_type_desc = null;
_invalidate_props(nameof(security_desc), nameof(price_multiplier_desc), nameof(quantity_desc), nameof(leg_type_desc));
}
void on_errors_changed(object sender, DataErrorsChangedEventArgs e) =>
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Items)));
}
class InnerGroupSubscription : IDisposable {
public readonly SyntheticImportRowCollectionViewGroup _inner_group;
readonly SyntheticImportRowCollectionViewGroup _parent;
int _last_count;
public InnerGroupSubscription(SyntheticImportRowCollectionViewGroup parent, SyntheticImportRowCollectionViewGroup inner_group) {
_parent = parent;
_inner_group = inner_group;
_inner_group.parent = parent;
// init item count
_on_item_count_changed();
// listen to item count changes
_inner_group.ProtectedItemCountChanged += _on_item_count_changed;
}
public void Dispose() {
_inner_group.ProtectedItemCountChanged -= _on_item_count_changed;
_inner_group.parent = null;
// make sure the state is valid (if a bad state goes by undetected here, nefarious/difficult-to-track bugs could arise)
if (_last_count != _inner_group.ProtectedItemCount) {
Log.Ui.warn("Cached inner count is not equal to the inner group's ItemCount (entire tree structure may be compromised).");
}
_parent.ProtectedItemCount -= _last_count;
}
void _on_item_count_changed() {
// add the delta
_parent.ProtectedItemCount += (_inner_group.ProtectedItemCount - _last_count);
// reset the local cache
_last_count = _inner_group.ProtectedItemCount;
}
}
}
}
@@ -0,0 +1,253 @@
using bts.api;
using bts.collections;
using bts.spark;
using mbts;
using mbts.collections;
using mbts.spark;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.utility;
using mbts.user.settings;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Data;
using System.Windows.Input;
using Disposable = mbts.Disposable;
namespace bts.shell.spreader {
class SyntheticImportVM : DisposableViewModelBase, IRequestViewClose {
IReadOnlyDictionary<uint, SecurityMD> _security_map = DictionaryHelper.empty<uint, SecurityMD>();
public SyntheticImportVM() {
var rows_view = new SyntheticImportRowCollectionView(flat_legs);
rows_view.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ContractLegVM.owner)));
this.rows_view = rows_view;
ok_cmd = new DelegateCommand(() => import_request?.Invoke());
cancel_cmd = new DelegateCommand(() => RequestClose?.Invoke(this, new EventArgs()));
_dispose = ObservableAutoWrapper.connect_item_hooks(
rows,
r => {
flat_legs.AddRange(r.legs);
return Disposable.Create(() => flat_legs.removeRange(r.legs));
});
}
public event Action import_request;
public event EventHandler RequestClose;
public ObservableCollection<SyntheticImportRowVM> rows { get; } = new ObservableCollection<SyntheticImportRowVM>();
public ObservableCollection<ContractLegVM> flat_legs { get; } = new ObservableCollection<ContractLegVM>();
public IReadOnlyDictionary<uint, SecurityMD> security_map { get => _security_map; set => Set(ref _security_map, value); }
public ICollectionView rows_view { get; }
// commands
public ICommand ok_cmd { get; }
public ICommand cancel_cmd { get; }
}
class SyntheticImportRowVM : DisposableViewModelBase, IEquatable<SyntheticImportRowVM> {
readonly ReactiveThresholdMonitor<ContractLegVM> _leg_errors_mon = new ReactiveThresholdMonitor<ContractLegVM>(l => l.subscribe_has_errors(true));
readonly Func<string, bool> _get_name_exists;
SecurityImmutable? _security;
ExecutionStrategyImmutable? _strategy;
TickFactors _tick_factors;
string _name;
bool _is_selected;
public SyntheticImportRowVM(uint user_id, Func<string, bool> get_name_exists) {
this.user_id = user_id;
_get_name_exists = get_name_exists;
_validate();
add_leg_cmd = new DelegateCommand<SyntheticImportRowVM>(r => {
if (r != null) {
r.legs.Add(new ContractLegVM {
price_multiplier = 1,
quantity = 1,
owner = r
});
}
});
remove_leg_cmd = new DelegateCommand<ContractLegVM>(l => l?.owner?.legs?.Remove(l));
PropertyChanged += on_property_changed;
var disp = new DisposableCombo(
_leg_errors_mon.OnDispatcher().Subscribe(v => _validate()),
ObservableAutoWrapper.connect_item_hooks(legs,
l => {
l.result_changed += invalidate;
return new DisposableCombo(_leg_errors_mon.join(l), l) +
new Action(() => l.result_changed -= invalidate);
})) +
new Action(() => PropertyChanged -= on_property_changed);
void invalidate() {
_security = null;
_strategy = null;
_validate();
result_changed?.Invoke();
}
void on_property_changed(object sender, PropertyChangedEventArgs e) =>
invalidate();
}
public event Action result_changed;
public SecurityImmutable result =>
_security ?? (_security = new SecurityImmutable(to_security())).Value;
public ExecutionStrategyImmutable strategy =>
_strategy ?? (_strategy = new ExecutionStrategyImmutable(to_strategy())).Value;
public (SecurityImmutable security, ExecutionStrategyImmutable strategy) result_tuple =>
(result, strategy);
public ParsedSyntheticContract parsed_synth { get; set; }
public string name { get => _name; set => Set(ref _name, value); }
public uint user_id { get; }
public TickFactors tick_factors { get => _tick_factors; set => Set(ref _tick_factors, value); }
public ObservableCollection<ContractLegVM> legs { get; } = new ObservableCollection<ContractLegVM>();
public bool is_selected { get => _is_selected; set => Set(ref _is_selected, value); }
public ICommand add_leg_cmd { get; }
public ICommand remove_leg_cmd { get; }
public Security to_security() {
var sc = new SyntheticContract {
Name = name,
UserId = user_id
};
sc.Legs.AddRange(legs.Select(l => l.to_contract_leg()));
return new Security {
Synthetic = sc,
Symbol = sc.Legs.FirstOrDefault()?.Security?.Symbol ?? string.Empty,
Exchange = Venue.Bts,
ContractSpec = tick_factors.to_spec()
};
}
public ExecutionStrategy to_strategy() {
var strat = new ExecutionStrategy();
foreach (var l in legs) {
if (l.security.HasValue)
strat.LegParamsMap[l.security.Value.Id] = l.to_leg_params();
}
return strat;
}
void _validate() {
using (validation()) {
if (string.IsNullOrEmpty(name)) {
validation_assert(false, "Name is not specified.", nameof(name));
} else if (_get_name_exists(name)) {
validation_assert(false, "Name already exists.", nameof(name));
}
validation_assert(tick_factors != null && tick_factors.is_valid(), "Invalid tick", nameof(tick_factors));
validation_assert(legs.Count > 0 && !_leg_errors_mon.is_above_threshold, "Invalid legs.", nameof(legs));
}
}
public bool Equals(SyntheticImportRowVM other) =>
other != null &&
parsed_synth.unpersisted_id == other.parsed_synth.unpersisted_id;
public override bool Equals(object obj) =>
Equals(obj as SyntheticImportRowVM);
public override int GetHashCode() =>
parsed_synth.unpersisted_id;
}
class ContractLegVM : DisposableViewModelBase, IEquatable<ContractLegVM> {
ContractLegImmutable? _leg;
LegSpreaderParamsImmutable? _leg_params;
SecurityImmutable? _security;
double _price_multiplier, _quantity;
ContractLegType _leg_type;
bool _is_selected;
public ContractLegVM() {
PropertyChanged += on_property_changed;
_dispose = Disposable.Create(() => PropertyChanged -= on_property_changed);
_validate();
void on_property_changed(object sender, PropertyChangedEventArgs e) {
switch (e.PropertyName) {
case nameof(security):
case nameof(price_multiplier):
case nameof(quantity):
_leg = null;
break;
case nameof(leg_type):
_leg_params = null;
break;
default: return;
}
_validate();
result_changed?.Invoke();
}
}
public event Action result_changed;
public ParsedContractLeg parsed_leg { get; set; }
public SyntheticImportRowVM owner { get; set; }
public int owner_id => owner.parsed_synth.unpersisted_id;
public int id => parsed_leg.id;
public ContractLegImmutable leg =>
_leg ?? (_leg = new ContractLegImmutable(to_contract_leg())).Value;
public LegSpreaderParamsImmutable leg_params =>
_leg_params ?? (_leg_params = new LegSpreaderParamsImmutable(to_leg_params())).Value;
public (ContractLegImmutable leg, LegSpreaderParamsImmutable leg_params) result_tuple =>
(leg, leg_params);
public SecurityImmutable? security { get => _security; set => Set(ref _security, value); }
public double price_multiplier { get => _price_multiplier; set => Set(ref _price_multiplier, value); }
public double quantity { get => _quantity; set => Set(ref _quantity, value); }
public ContractLegType leg_type { get => _leg_type; set => Set(ref _leg_type, value); }
public bool is_selected { get => _is_selected; set => Set(ref _is_selected, value); }
public void load(ContractLegImmutable leg) {
security = leg.Security;
price_multiplier = leg.PriceMultiplier;
quantity = leg.Quantity;
}
public ContractLeg to_contract_leg() =>
new ContractLeg {
Security = security?.CloneToMutable(),
PriceMultiplier = price_multiplier,
Side = quantity < 0 ? Side.Sell : Side.Buy,
Quantity = Math.Abs(quantity)
};
public LegSpreaderParams to_leg_params() {
var lps = SyntheticDataHelper.Defaults.LegSpreaderParams;
lps.EnableHedging = (leg_type & ContractLegType.Hedge) != 0;
lps.IsActiveQuoting = (leg_type & ContractLegType.Quote) != 0;
return lps;
}
void _validate() {
using (validation()) {
validation_assert(security.is_exchange_security(), "Invalid security", nameof(security));
validation_assert(price_multiplier.IsNonZero(), "Invalid price multiplier", nameof(price_multiplier));
validation_assert(quantity.IsNonZero(), "Invalid quantity", nameof(quantity));
}
}
public bool Equals(ContractLegVM other) =>
other != null && id == other.id;
public override bool Equals(object obj) =>
Equals(obj as ContractLegVM);
public override int GetHashCode() =>
id;
}
}
@@ -0,0 +1,270 @@
<UserControl
x:Class="bts.shell.spreader.SyntheticImportView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:spr="clr-namespace:bts.shell.spreader"
xmlns:pnl="clr-namespace:bts.shell.pnl"
MinWidth="400"
Content="{Binding}"
>
<UserControl.Resources>
<cv:GridLengthConverter x:Key="GridLengthPixelConverter" UnitType="Pixel"/>
<Style x:Key="GroupDesc" TargetType="TextBlock">
<Setter Property="Margin" Value="{x:Static spr:SyntheticImportView.CellPadding}"/>
<Setter Property="ToolTip" Value="{Binding Text, RelativeSource={RelativeSource Self}}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="#80000000"/>
<Setter Property="FontStyle" Value="Italic"/>
</Style>
<DataTemplate DataType="{x:Type spr:ContractLegVM}">
<c:ContractPickerPopup
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding security}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=DataGrid}, Mode=OneWay}"
SelectedSecurity="{Binding security, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ShowDialogSkirt="True"
ToolTip="{Binding SelectedCriteria, RelativeSource={RelativeSource Self}, Converter={x:Static c:ContractPickerPopup.SecurityCriteriaTextConverter}, Mode=OneWay}"
/>
</DataTemplate>
<DataTemplate DataType="{x:Type spr:SyntheticImportVM}">
<c:DIYGrid
Grid.IsSharedSizeScope="True"
Rows="{Binding rows_view}"
RowHeight="{x:Static spr:SyntheticImportView.RowHeight}"
RowsHorizontalScrollBarVisibility="Collapsed"
SelectionHighlight="#30000000"
SelectionMode="Single"
SupportGroupItems="True"
>
<c:DIYGrid.Columns>
<c:DIYGridColumnCollection>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.Name}" Width="106"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.Ticks}" Width="128"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.Security}" Header="Securities" Width="120"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.Quantity}" Header="Quantities" Width="70"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.PriceMultiplier}" Header="Price Multipliers" Width="80"/>
<spr:SyntheticImportColumn key="{x:Static spr:SyntheticImportColumns.LegType}" Header="Leg Types" Width="83"/>
</c:DIYGridColumnCollection>
</c:DIYGrid.Columns>
<c:DIYGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="HorizontalAlignment" Value="Left"/>
<EventSetter Event="MouseDown" Handler="_on_group_item_mouse_down"/>
<!--Note: Tag is bound to 'Items' so errors on the 'Items' prop will be included in Validation.Errors.-->
<Setter Property="Tag">
<Setter.Value>
<MultiBinding>
<MultiBinding.Converter>
<cv:MultiValueConverterPipe/>
</MultiBinding.Converter>
<Binding Path="Name.legs"/>
<Binding Path="Name.name"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Grid
c:DIYGrid.SyncWithColumnGeometry="True"
u:StateManager.VisualState="{Binding IsChecked, ElementName=expander, Converter={x:Static pnl:PNLView.ExpandedVisualStateConverter}}"
Background="{TemplateBinding Background}"
>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ExpansionStates">
<VisualState Name="Expanded"/>
<VisualState Name="Collapsed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="presenter" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding RowHeight, RelativeSource={RelativeSource AncestorType=c:DIYGrid}, Converter={StaticResource GridLengthPixelConverter}}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.ColumnSpan="99" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Fill="Gray" UseLayoutRounding="True"/>
<!--Expander Toggle-->
<c:ExpanderToggleButton
x:Name="expander"
Grid.ColumnSpan="99"
Validation.ValidationAdornerSiteFor="{Binding RelativeSource={RelativeSource TemplatedParent}}"
ArrowPadding="14,2,0,0"
Background="{u:Brush Transparent}"
BorderThickness="0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Height="{Binding RowHeight, RelativeSource={RelativeSource AncestorType=c:DIYGrid}}"
IsChecked="{Binding is_expanded}"
>
<u:StateManager.VisualState>
<Binding Path="TemplatedParent.(Validation.Errors).Count" RelativeSource="{RelativeSource Self}">
<Binding.Converter>
<cv:ValueConverterPipe>
<cv:IntGreaterThanConverter Value="0"/>
<cv:BooleanToObjectConverter ValueForFalse="NoErrors" ValueForTrue="Errors"/>
</cv:ValueConverterPipe>
</Binding.Converter>
</Binding>
</u:StateManager.VisualState>
<u:StateManager.Groups>
<u:StateGroupsManager AttachToMembers="True">
<u:StateDefinitionCollection>
<u:MouseStateDefinition MouseSource="{Binding Group.Target, RelativeSource={RelativeSource Self}}"/>
</u:StateDefinitionCollection>
</u:StateGroupsManager>
</u:StateManager.Groups>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="MouseStates">
<VisualState Name="Normal"/>
<VisualState Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="expander" Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="#10000000"/>
</Storyboard>
</VisualState>
<VisualState Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="expander" Storyboard.TargetProperty="Background.Color" BeginTime="0" Duration="0" To="#10000000"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Validation.ErrorTemplate>
<ControlTemplate TargetType="Control">
<Grid>
<AdornedElementPlaceholder x:Name="adorned_element_pl"/>
<Border x:Name="exclamation" HorizontalAlignment="Left">
<Image Height="12" Margin="6,0,0,0" VerticalAlignment="Center" Source="{x:Static u:Icons.WarningExclamation}"/>
</Border>
<ContentControl u:Behavior.MirrorLayoutFor="{Binding ElementName=exclamation}" ToolTip="{Binding AdornedElement.(Validation.ValidationAdornerSiteFor).(Validation.Errors), ElementName=adorned_element_pl}">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Grid Background="Transparent">
<Grid.ToolTip>
<ToolTip Content="{Binding ToolTip, RelativeSource={RelativeSource TemplatedParent}}">
<ToolTip.ContentTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}" DisplayMemberPath="ErrorContent"/>
</DataTemplate>
</ToolTip.ContentTemplate>
</ToolTip>
</Grid.ToolTip>
</Grid>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Grid>
</ControlTemplate>
</Validation.ErrorTemplate>
<Grid c:DIYGrid.SyncWithColumnGeometry="True" Margin="-22,0,0,0">
<c:EditableTextBlock
x:Name="PART_Name_EditableTextBlock"
Grid.Column="0"
Margin="22,0,0,0"
EndEditOnLostKeyboardFocus="True"
Foreground="{u:Brush Black}"
Text="{Binding Name.name}"
ToolTip="Click to Edit Name"
UseLayoutRounding="True"
VerticalAlignment="Center"
>
<c:EditableTextBlock.CommandBindings>
<CommandBinding Command="{x:Static c:EditableTextBlock.BeginEditCommand}" Executed="{x:Static c:EditableTextBlock.BeginEditExecuted}"/>
<CommandBinding Command="{x:Static c:EditableTextBlock.EndEditCommand}" Executed="{x:Static c:EditableTextBlock.EndEditExecuted}"/>
<CommandBinding Command="{x:Static c:EditableTextBlock.CancelEditCommand}" Executed="{x:Static c:EditableTextBlock.CancelEditExecuted}"/>
</c:EditableTextBlock.CommandBindings>
<c:EditableTextBlock.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{x:Static c:EditableTextBlock.BeginEditCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:EditableTextBlock.EndEditCommand}"/>
<KeyBinding Key="Esc" Command="{x:Static c:EditableTextBlock.CancelEditCommand}"/>
</c:EditableTextBlock.InputBindings>
</c:EditableTextBlock>
<c:TickFactorsPicker
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Ticks}"
Margin="{x:Static spr:SyntheticImportView.CellPadding}"
SelectedTickFactors="{Binding Name.tick_factors}"
UseLayoutRounding="True"
/>
<TextBlock c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Security}" Text="{Binding security_desc}" Style="{StaticResource GroupDesc}"/>
<TextBlock c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Quantity}" Text="{Binding quantity_desc}" Style="{StaticResource GroupDesc}"/>
<TextBlock c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.PriceMultiplier}" Text="{Binding price_multiplier_desc}" Style="{StaticResource GroupDesc}"/>
<TextBlock c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.LegType}" Text="{Binding leg_type_desc}" Style="{StaticResource GroupDesc}">
<TextBlock.ToolTip>
<TextBlock>
<Run Text="{Binding leg_type_desc, Mode=OneWay}"/>
<LineBreak/>
<Run Text="Q = Quote"/>
<LineBreak/>
<Run Text="H = Hedge"/>
<LineBreak/>
<Run Text="N = None"/>
</TextBlock>
</TextBlock.ToolTip>
</TextBlock>
</Grid>
</c:ExpanderToggleButton>
<ItemsPresenter x:Name="presenter" Grid.Row="1" Grid.ColumnSpan="99"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</c:DIYGrid.GroupStyle>
<c:DIYGrid.RowTemplate>
<DataTemplate DataType="{x:Type spr:ContractLegVM}">
<Grid c:DIYGrid.SyncWithColumnGeometry="True">
<c:ContractPickerPopup
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Security}"
Background="Transparent"
BorderThickness="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="{x:Static spr:SyntheticImportView.CellPadding}"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding security, Converter={x:Static cv:ValueConverters.SecuritySelectorText}}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=c:DIYGrid}, Mode=OneWay}"
SelectedSecurity="{Binding security, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ShowDialogSkirt="True"
ToolTip="{Binding SelectedSecurity, RelativeSource={RelativeSource Self}}"
>
</c:ContractPickerPopup>
<c:TickUpDownDouble
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.Quantity}"
Margin="{x:Static spr:SyntheticImportView.CellPadding}"
Value="{Binding quantity}"
/>
<c:TickUpDownDouble
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.PriceMultiplier}"
Margin="{x:Static spr:SyntheticImportView.CellPadding}"
Value="{Binding price_multiplier}"
/>
<spr:ContractLegTypeView
c:DIYGrid.ColumnKey="{x:Static spr:SyntheticImportColumns.LegType}"
Value="{Binding leg_type}"
/>
</Grid>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
</DataTemplate>
</UserControl.Resources>
</UserControl>
@@ -0,0 +1,50 @@
using mbts.ui.controls;
using mbts.ui.dpi;
using mbts.ui.utility;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace bts.shell.spreader {
enum SyntheticImportColumns {
Name = 1,
Security,
Ticks,
PriceMultiplier,
Quantity,
LegType
}
/// <summary>
/// Interaction logic for SyntheticImportView.xaml
/// </summary>
partial class SyntheticImportView : UserControl {
public static double RowHeight =>
27.0.RoundLayoutValue();
public static Thickness CellPadding =>
new Thickness(6, 3, 6, 3).RoundToLayout();
public SyntheticImportView() =>
InitializeComponent();
void _on_group_item_mouse_down(object sender, MouseButtonEventArgs e) {
if (!e.Handled && e.ClickCount == 2 && sender is FrameworkElement el && el.DataContext is SyntheticImportRowCollectionViewGroup group) {
group.is_expanded = !group.is_expanded;
e.Handled = true;
}
}
}
class SyntheticImportColumn : DIYGrid.ColumnBase<SyntheticImportColumns> {
public SyntheticImportColumn() =>
is_visible = true;
protected override void on_key_initialized(SyntheticImportColumns key) {
if (Header == null)
Header = EnumHelper.get_display_name(key);
if (id == null)
id = key.ToString();
}
}
}
@@ -0,0 +1,194 @@
using bts;
using bts.api;
using bts.collections;
using bts.shell.spreader;
using mbts.ui;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace mbts.spark {
static class SyntheticImporter {
const string _import_title = "Import Synthetics";
static readonly TimeSpan _import_op_sec_map_timeout = TimeSpan.FromSeconds(30);
public static IDisposable import_from_file(string path, IBus bus, OrdersModel orders_model) {
bool is_disposed = false;
IDisposable dispose_op = null;
dispose_op = Disposable.Create(() => is_disposed = true);
impl();
return Disposable.Create(() => Disposable.dispose(ref dispose_op));
async void impl() {
IReadOnlyDictionary<uint, SecurityMD> sec_md_map = null;
while (true) {
await Task.WhenAny(init_sec_md_map(), Task.Delay(_import_op_sec_map_timeout));
if (is_disposed)
return;
if (sec_md_map != null)
break;
if (!retry_on_error($"Synthetic import op timed out waiting for security map after {_import_op_sec_map_timeout.TotalSeconds:0.00} seconds."))
return;
}
// The following conditions should break the while loop:
// 1) the import succeeds
// 2) the import fails, and the user does not click 'Yes' in the 'try again...' confirmation dialog
while (true) {
string error_msg = null;
try {
var raw_lines = File.ReadAllLines(path);
var used_names = new HashSet<string>(sec_md_map.Values.Where(smd => smd.security.ContractCase == Security.ContractOneofCase.Synthetic)
.Select(smd => smd.security.Synthetic.Name));
var parsed_synths = SyntheticSecurityParser.parse_rows(raw_lines, MappedDictionary.create(sec_md_map, smd => smd.security), used_names).ToList();
var main_vm = new SyntheticImportVM() {
security_map = sec_md_map
};
// add rows/conflicts
main_vm.rows.AddRange(parsed_synths.Select(ps => {
var sec = ps.result;
var r = new SyntheticImportRowVM(orders_model.admin_model.user.id, used_names.Contains) {
tick_factors = TickUtils.get_regional_ticks(sec.ContractSpec).low_tick,
name = sec.Synthetic.Name,
parsed_synth = ps
};
r.legs.AddRange(ps.legs.Zip(sec.Synthetic.Legs).Select(tup => {
var (pl, l) = tup;
var leg_vm = new ContractLegVM {
price_multiplier = l.PriceMultiplier,
quantity = l.Quantity,
parsed_leg = pl,
owner = r,
leg_type =
(pl.result_tuple.leg_params.EnableHedging ? ContractLegType.Hedge : ContractLegType.None) |
(pl.result_tuple.leg_params.IsActiveQuoting ? ContractLegType.Quote : ContractLegType.None)
};
if (l.Security != null)
leg_vm.security = new SecurityImmutable(l.Security);
return leg_vm;
}));
return r;
}));
var main_win = new SyntheticImportDialog { DataContext = main_vm };
ui.WindowStyle.SetSizeToContent(main_win, SizeToContent.WidthAndHeight);
main_vm.import_request += on_main_import_request;
main_win.move_to_mouse(-200, -75);
dispose_op = WindowUtilities.connect_vm(main_win, main_vm, main_vm);
main_win.Show();
// conflicts
var conflicts = SyntheticSecurityParser.get_conflicts(main_vm.rows);
if (conflicts.Values.Any(c => c.Count > 0)) {
var conflicts_vm = new SyntheticImportConflictsVM(parsed_synths.Count) {
security_map = sec_md_map
};
foreach (var kv in conflicts) {
var assign_del = SyntheticSecurityParser.assign_component(kv.Key);
foreach (var text_groups in kv.Value) {
var eval = get_error_value(kv.Key);
var row = new SyntheticImportConflictRowVM(kv.Key, text_groups.Key, text_groups.Value, eval, eval);
conflicts_vm.conflicts.Add(row);
conflicts_vm.committed += () => {
foreach (var (synth, leg_vm) in text_groups.Value) {
var leg_tup = leg_vm?.parsed_leg?.result_tuple;
assign_del(synth.parsed_synth.result, leg_tup?.leg, leg_tup?.leg_params, row.value);
}
row.on_conflicts_resolved();
};
}
}
var conflicts_win = new SyntheticImportConflictsDialog {
DataContext = conflicts_vm,
Owner = main_win,
Title = $"{_import_title} Conflicts",
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
dispose_op = new DisposableCombo(WindowUtilities.connect_vm(conflicts_win, conflicts_vm, conflicts_vm)) + dispose_op;
conflicts_win.ShowDialog();
}
object get_error_value(SyntheticContractComponent component) {
switch (component) {
case SyntheticContractComponent.PriceMultiplier:
case SyntheticContractComponent.Quantity: return double.NaN;
default: return null;
}
}
void on_main_import_request() {
if (!is_disposed)
import_synths(parsed_synths, main_vm.rows);
Disposable.dispose(ref dispose_op);
}
}
catch (Exception ex) {
error_msg = $"Error parsing synthetics from file '{path}': {ex}.";
}
if (error_msg == null) {
// Exit loop: the import succeeded.
break;
}
if (!retry_on_error(error_msg)) {
// Exit loop: the import failed, and the user did not click 'Yes' in the 'try again...' confirmation dialog.
break;
}
// Continue loop: the import failed, and the user wants to try again.
error_msg = null;
}
async void import_synths(IReadOnlyList<ParsedSyntheticContract> parsed_synths, IEnumerable<SyntheticImportRowVM> rows) {
(uint? key, string error_msg)[] new_ids = null;
string error_msg = null;
try {
new_ids = await orders_model.spreader.create_synthetics(
rows.Where(r => !r.HasErrors)
.Select(r => r.result_tuple));
}
catch (Exception ex) {
Log.Ui.error($"Failed to save all synthetics during import op (one or more may have been successfully saved): {ex}.");
error_msg = $"Import was unsuccessful: {ex}.";
}
if (new_ids == null)
error_msg = "An unknown error occurred (check the synthetic management window to see if any synthetics were successfully added).";
if (error_msg != null) {
BTSMessageBox.Show(error_msg, "Import Synthetics", BTSMessageBoxButtons.OK);
} else {
Log.assert(new_ids != null);
var failed = new_ids
.Where(r => !r.error_msg.is_null_or_empty())
.ToList();
int num_succeeded = new_ids.Length - failed.Count;
if (num_succeeded < parsed_synths.Count) {
if (!BTSMessageBox.Confirm(
$"{num_succeeded}/{parsed_synths.Count} synthetics were successfully imported.\n" +
"Would you like to keep the imports that were successful?",
_import_title)) {
bus.post(SparkTags.SyntheticDeleteTag,
ImmutableHashSet.CreateRange(new_ids.Where(r => r.key.HasValue).Select(r => r.key.Value)));
}
} else {
BTSMessageBox.Show(
$"All {num_succeeded} synthetics were successfully imported.",
_import_title,
BTSMessageBoxButtons.OK,
MessageBoxImage.Information);
}
}
}
async Task init_sec_md_map() =>
sec_md_map = await bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().FirstOrDefaultAsync();
bool retry_on_error(string error_msg) {
Log.Ui.warn(error_msg);
return BTSMessageBox.Confirm(error_msg + "\nTry again?", _import_title, MessageBoxImage.Exclamation);
}
}
}
}
}
@@ -0,0 +1,307 @@
using bts.api;
using bts.spark;
using bts.utils;
using mbts;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
namespace bts.shell.spreader {
[Flags]
enum ContractLegType {
None = 0,
Quote = 1,
Hedge = 2
}
class ParsedSyntheticContract : IDisposable {
[ThreadStatic]
static int _id_counter;
public readonly int unpersisted_id = unchecked(_id_counter++);
public readonly Security result = new Security {
Synthetic = new SyntheticContract(),
ContractSpec = new ContractSpecification(),
Exchange = Venue.Bts
};
readonly ReactiveThresholdMonitor<ParsedContractLeg> _legs_valid_mon =
new ReactiveThresholdMonitor<ParsedContractLeg>(l => l.subscribe_is_valid());
IDisposable _dispose;
public ParsedSyntheticContract() {
_dispose = Disposable.Create(
ObservableAutoWrapper.connect_item_hooks(legs, _legs_valid_mon.join),
_legs_valid_mon.Subscribe(_ => _is_dirty = true));
}
bool
_is_dirty,
_is_valid;
(string text, bool success)
_name,
_tick_size;
public (string text, bool success) name { get => _name; set => _set(ref _name, value); }
public (string text, bool success) tick_size { get => _tick_size; set => _set(ref _tick_size, value); }
public ObservableCollection<ParsedContractLeg> legs { get; } = new ObservableCollection<ParsedContractLeg>();
public bool is_valid {
get {
if (_is_dirty) {
_is_valid = tick_size.success && name.success && !_legs_valid_mon.is_above_threshold;
_is_dirty = false;
}
return _is_valid;
}
}
public ParsedContractLeg current_leg =>
legs.LastOrDefault() ?? add_new_leg();
public ParsedContractLeg add_new_leg() {
var leg = new ParsedContractLeg();
legs.Add(leg);
result.Synthetic.Legs.Add(leg.result);
return leg;
}
public void Dispose() =>
Disposable.dispose(ref _dispose);
public bool Equals(ParsedSyntheticContract other) => other != null && unpersisted_id == other.unpersisted_id;
public override bool Equals(object obj) => Equals(obj as ParsedSyntheticContract);
public override int GetHashCode() => unpersisted_id.GetHashCode();
void _set<T>(ref T backing, T value) {
backing = value;
_is_dirty = true;
}
}
class ParsedContractLeg {
[ThreadStatic]
static int _id_counter;
public readonly ContractLeg result = new ContractLeg { };
public readonly LegSpreaderParams leg_params = new LegSpreaderParams();
bool
_is_dirty,
_is_valid;
(string text, bool success)
_security,
_quantity,
_price_multiplier,
_leg_type;
public event Action invalidated;
public int id { get; } = unchecked(_id_counter++);
public (string text, bool success) security { get => _security; set => _set(ref _security, value); }
public (string text, bool success) quantity { get => _quantity; set => _set(ref _quantity, value); }
public (string text, bool success) price_multiplier { get => _price_multiplier; set => _set(ref _price_multiplier, value); }
public (string text, bool success) leg_type { get => _leg_type; set => _set(ref _leg_type, value); }
public (ContractLeg leg, LegSpreaderParams leg_params) result_tuple => (result, leg_params);
public bool is_valid {
get {
if (_is_dirty) {
if (_is_valid != (security.success && quantity.success && price_multiplier.success && leg_type.success)) {
_is_valid = !_is_valid;
invalidated?.Invoke();
}
_is_dirty = false;
}
return _is_valid;
}
}
public IObservable<bool> subscribe_is_valid() =>
Observable.Create<bool>(o => {
o.OnNext(is_valid);
invalidated += on_invalidated;
return Disposable.Create(() => invalidated -= on_invalidated);
void on_invalidated() =>
o.OnNext(is_valid);
});
void _set<T>(ref T backing, T value) {
backing = value;
_is_dirty = true;
}
}
static class SyntheticSecurityParser {
public const string file_extension = "csv";
public static string display_name(this ContractLegType leg_type) {
if (leg_type == ContractLegType.None)
return "N";
var flags = EnumHelper.split_flags(leg_type);
return string.Join("", flags.Select(f => f.ToString())
.Where(str => str.Length > 0)
.Select(str => str.Substring(0, 1)));
}
public static Action<Security, ContractLeg, LegSpreaderParams, object> assign_component(SyntheticContractComponent component) {
switch (component) {
case SyntheticContractComponent.Name: return (sec, leg, leg_params, val) => sec.Synthetic.Name = val?.ToString() ?? string.Empty;
case SyntheticContractComponent.TickSize: return (sec, leg, leg_params, val) => sec.ContractSpec.LowTick = sec.ContractSpec.HighTick = ConvertUtils.try_convert<double?>(val) ?? 0.0;
case SyntheticContractComponent.PriceMultiplier: return (sec, leg, leg_params, val) => leg.PriceMultiplier = ConvertUtils.try_convert<double?>(val) ?? double.NaN;
case SyntheticContractComponent.Quantity: return (sec, leg, leg_params, val) => leg.Quantity = ConvertUtils.try_convert<double?>(val) ?? double.NaN;
case SyntheticContractComponent.Security: return (sec, leg, leg_params, val) => leg.Security = val as Security ?? (val is SecurityImmutable si ? si.CloneToMutable() : null);
case SyntheticContractComponent.LegType: return (sec, leg, leg_params, val) => assign_leg_type(leg_params, ConvertUtils.try_convert(val, out ContractLegType lt) ? lt : ContractLegType.None);
default: throw new NotImplementedException();
}
}
/// Note: the returned dictionary will have entries for each <see cref="SyntheticContractComponent"/>, regardless of whether or not that component has any conflicts.
public static Dictionary<SyntheticContractComponent, Dictionary<string, List<(ParsedSyntheticContract synth, ParsedContractLeg leg)>>> get_conflicts(IEnumerable<ParsedSyntheticContract> parsed_fills) {
var missing_components = EnumHelper.get_values<SyntheticContractComponent>().ToDictionary(c => c, c => new Dictionary<string, List<(ParsedSyntheticContract synth, ParsedContractLeg leg)>>());
var mk_list = new Func<string, List<(ParsedSyntheticContract synth, ParsedContractLeg leg)>>(_ => new List<(ParsedSyntheticContract, ParsedContractLeg)>());
foreach (var psc in parsed_fills) {
if (!psc.name.success) {
add_missing(SyntheticContractComponent.Name, psc.name.text, psc);
}
if (!psc.tick_size.success) {
add_missing(SyntheticContractComponent.TickSize, psc.tick_size.text, psc);
}
foreach (var leg in psc.legs) {
if (!leg.price_multiplier.success)
add_missing(SyntheticContractComponent.PriceMultiplier, leg.price_multiplier.text, psc, leg);
if (!leg.quantity.success)
add_missing(SyntheticContractComponent.Quantity, leg.quantity.text, psc, leg);
if (!leg.security.success)
add_missing(SyntheticContractComponent.Security, leg.security.text, psc, leg);
if (!leg.leg_type.success)
add_missing(SyntheticContractComponent.LegType, leg.leg_type.text, psc, leg);
}
}
return missing_components;
void add_missing(SyntheticContractComponent component, string text, ParsedSyntheticContract result, ParsedContractLeg leg = null) =>
missing_components[component].GetOrAdd(text ?? string.Empty, mk_list).Add((result, leg));
}
public static Dictionary<SyntheticContractComponent, Dictionary<string, List<(SyntheticImportRowVM row, ContractLegVM leg)>>> get_conflicts(IEnumerable<SyntheticImportRowVM> import_rows) {
var missing_components = EnumHelper.get_values<SyntheticContractComponent>().ToDictionary(c => c, c => new Dictionary<string, List<(SyntheticImportRowVM row, ContractLegVM leg)>>());
var mk_list = new Func<string, List<(SyntheticImportRowVM row, ContractLegVM leg)>>(_ => new List<(SyntheticImportRowVM, ContractLegVM)>());
foreach (var ir in import_rows) {
var psc = ir.parsed_synth;
if (!psc.name.success) {
add_missing(SyntheticContractComponent.Name, psc.name.text, ir);
}
if (!psc.tick_size.success) {
add_missing(SyntheticContractComponent.TickSize, psc.tick_size.text, ir);
}
foreach (var leg in ir.legs) {
if (!leg.parsed_leg.price_multiplier.success)
add_missing(SyntheticContractComponent.PriceMultiplier, leg.parsed_leg.price_multiplier.text, ir, leg);
if (!leg.parsed_leg.quantity.success)
add_missing(SyntheticContractComponent.Quantity, leg.parsed_leg.quantity.text, ir, leg);
if (!leg.parsed_leg.security.success)
add_missing(SyntheticContractComponent.Security, leg.parsed_leg.security.text, ir, leg);
if (!leg.parsed_leg.leg_type.success)
add_missing(SyntheticContractComponent.LegType, leg.parsed_leg.leg_type.text, ir, leg);
}
}
return missing_components;
void add_missing(SyntheticContractComponent component, string text, SyntheticImportRowVM row, ContractLegVM leg = null) =>
missing_components[component].GetOrAdd(text ?? string.Empty, mk_list).Add((row, leg));
}
// This implementation prioritizes simplicity over speed.
// The security desc lookup could be better optimized, but the speed at which fills are imported is probably not a paramount concern.
public static IEnumerable<ParsedSyntheticContract> parse_rows(IEnumerable<string> rows, IReadOnlyDictionary<uint, SecurityImmutable> security_map, ISet<string> used_names) {
var sec_lookup = security_map.ToLookup(kv => kv.Value.ToString());
var name_col = Cells.name(used_names.Contains);
var sec_col = Cells.contract_security(lookup_security);
return RowParser.parse_rows(rows, columns(), Cells.delimiter);
Security lookup_security(string sec_text) =>
sec_lookup.Contains(sec_text) ? sec_lookup[sec_text].First().Value.CloneToMutable() : null;
IEnumerable<RowParser.AssignColumnValue<ParsedSyntheticContract>> columns() {
yield return name_col;
yield return Cells.tick_size;
// if an imported synth has > 255 legs, assume it is an error.
const int max_legs = 255;
int leg_count = 0;
do {
yield return sec_col;
yield return Cells.contract_quantity;
yield return Cells.contract_price_multiplier;
yield return Cells.leg_type;
} while (leg_count++ < max_legs);
}
}
public static Func<string, TKey> reverse_lookup<TKey>(IReadOnlyDictionary<TKey, string> map) {
var lookup = map.ToLookup(kv => kv.Value, kv => kv.Key);
return cell_text => lookup.Contains(cell_text) ? lookup[cell_text].First() : default;
}
public static void assign_leg_type(LegSpreaderParams leg_params, ContractLegType leg_type) {
leg_params.IsActiveQuoting = (leg_type & ContractLegType.Quote) != 0;
leg_params.EnableHedging = (leg_type & ContractLegType.Hedge) != 0;
}
static class Cells {
public const char delimiter = ',';
public static RowParser.AssignColumnValue<ParsedSyntheticContract> name(Func<string, bool> name_exists) =>
(ref ParsedSyntheticContract psc, string cell_text) => {
psc.name = (cell_text, !name_exists(cell_text));
psc.result.Synthetic.Name = cell_text;
};
public static void tick_size(ref ParsedSyntheticContract psc, string cell_text) {
psc.tick_size = (cell_text, double.TryParse(cell_text.Trim(), out var val));
if (psc.tick_size.success) {
psc.tick_size = (cell_text, val.IsNonZero());
psc.result.ContractSpec.LowTick = psc.result.ContractSpec.HighTick = val;
}
}
public static RowParser.AssignColumnValue<ParsedSyntheticContract> contract_security(Func<string, Security> reverse_security_lookup) =>
(ref ParsedSyntheticContract psc, string cell_text) => {
var leg = psc.add_new_leg();
leg.result.Security = reverse_security_lookup(cell_text.Trim());
leg.security = (cell_text, leg.result.Security != null);
};
public static void contract_quantity(ref ParsedSyntheticContract psc, string cell_text) {
psc.current_leg.quantity = (cell_text, double.TryParse(cell_text.Trim(), out var val) && val.IsNonZero());
psc.current_leg.result.Quantity = val;
}
public static void contract_price_multiplier(ref ParsedSyntheticContract psc, string cell_text) {
psc.current_leg.price_multiplier = (cell_text, double.TryParse(cell_text.Trim(), out var val) && val.IsNonZero());
psc.current_leg.result.PriceMultiplier = val;
}
public static void leg_type(ref ParsedSyntheticContract psc, string cell_text) {
psc.current_leg.leg_type = (cell_text, _parse_leg_type(cell_text.Trim(), out var leg_type));
assign_leg_type(psc.current_leg.leg_params, leg_type);
}
static bool _parse_leg_type(string cell_text, out ContractLegType result) {
result = ContractLegType.None;
if (cell_text.IsNullOrEmpty())
return false;
switch (char.ToUpper(cell_text[0])) {
case 'Q': result = ContractLegType.Quote; break;
case 'H': result = ContractLegType.Hedge; break;
case 'N': break;
default: return false;
}
return true;
}
}
}
}
@@ -1,17 +1,52 @@
using bts.api;
using bts.spark;
using mbts.ui.shell;
using System;
namespace mbts.ui.spark {
class LegSpreaderParamsTuple : ViewModelBase, IEquatable<LegSpreaderParamsTuple> {
[ThreadStatic]
static int _id_counter;
SecurityImmutable? _security;
namespace mbts.ui.spark
{
internal class LegSpreaderParamsTuple
{
public LegSpreaderParamsTuple(ContractLeg leg, LegSpreaderParams leg_params) {
id = unchecked(_id_counter++);
this.leg = leg;
this.leg_params = leg_params;
if (leg.Security != null)
_security = new SecurityImmutable(leg.Security);
}
public event Action security_changed;
public int id { get; }
/// <summary>
/// Important: do NOT modify security via <see cref="ContractLeg.Security"/>,
/// use <see cref="security"/> instead ([only]the latter raises the PropertyChanged event).
/// </summary>
public ContractLeg leg { get; }
public LegSpreaderParams leg_params { get; }
public SecurityImmutable? security {
get => _security;
set {
if (!_security.Equals(value)) {
_security = value;
leg.Security = value?.CloneToMutable();
security_changed?.Invoke();
RaisePropertyChanged();
}
}
}
public uint SecurityKey => leg.Security.Id;
public bool Equals(LegSpreaderParamsTuple other) =>
ReferenceEquals(this, other);
public override bool Equals(object obj) =>
Equals(obj as LegSpreaderParamsTuple);
public override int GetHashCode() =>
id;
}
}
}
@@ -4,6 +4,8 @@ using bts.api.core;
using bts.api.synthetic;
using bts.collections;
using bts.spark;
using bts.utils;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
@@ -82,6 +84,61 @@ namespace mbts.spark {
(mdc, success) => success ? mdc.Data[0].Strategy : (ExecutionStrategyImmutable?)null,
"lookup strategy");
public Task<(uint? key, string error_msg)[]> create_synthetics(IEnumerable<(SecurityImmutable security, ExecutionStrategyImmutable strategy)> synthetics) {
var tasks = synthetics.Select(tup => {
var (sec, strat) = tup;
if (sec.ContractCase != Security.ContractOneofCase.Synthetic)
return Task.FromException<(Result<CreateResult>, Result<UpdateResult>?)>(ExceptionBuilder.Argument.unexpected_case(sec.ContractCase, nameof(sec)));
if (sec.Synthetic.UserId != _user_id) {
var msec = sec.CloneToMutable();
msec.Synthetic.UserId = _user_id;
sec = new SecurityImmutable(msec);
}
return impl();
async Task<(Result<CreateResult> sec, Result<UpdateResult>? strat)> impl() {
var result = await _service.create_synthetic_async(sec);
Result<UpdateResult>? strategy;
if (result.success && result.value.Error == CreateResult.Types.Error.Ok) {
strategy = await _service.update_security_metadata_async(new Metadata {
SecurityKey = result.value.Key,
Strategy = tup.strategy.CloneToMutable()
});
} else {
strategy = null;
}
return (result, strategy);
}
});
return _error.safe_execute_batch(tasks,
tup => {
var (sec, strat) = tup;
// security
string sec_error_msg = null;
uint? key = null;
if (!sec.success) {
sec_error_msg = sec.error_message;
} else if (sec.value.Error != CreateResult.Types.Error.Ok) {
sec_error_msg = sec.value.Error.ToString();
} else {
key = sec.value.Key;
}
// strategy
string strat_error_msg = null;
if (!strat.HasValue) {
strat_error_msg = "Strategy was not successfully saved";
} else if (!strat.Value.success) {
strat_error_msg = strat.Value.error_message;
} else if (strat.Value.value.Error != UpdateResult.Types.Error.Ok) {
strat_error_msg = strat.Value.value.Error.ToString();
}
// merge error msgs (result should be null if both errors are empty)
var error_msg = string.Join(Environment.NewLine, new[] { sec_error_msg, strat_error_msg }.ExceptNull());
return (key, error_msg.NullIfEmpty());
},
"Synthetic",
"creating synthetics");
}
public Task<uint?> create_synthetic(SecurityImmutable synthetic) {
if (synthetic.ContractCase != Security.ContractOneofCase.Synthetic)
return null;
@@ -106,10 +163,32 @@ namespace mbts.spark {
(r, success) => success,
"saving strategy");
public async Task<string[]> save_strategies(IEnumerable<(uint key, ExecutionStrategyImmutable strategy)> items) {
// Copy everything to a new list, to guarentee that it won't be altered during the op.
// The performance impact should be insignificant, as this should be a rare call.
var item_list = new List<(uint key, ExecutionStrategyImmutable strategy, int index)>(items.Select((item, index) => (item.key, item.strategy, index)));
string[] errors = new string[item_list.Count];
await Task.WhenAll(item_list.Select(impl));
return errors;
async Task impl((uint key, ExecutionStrategyImmutable strategy, int index) tup) {
var (key, strategy, index) = tup;
try {
var result = await _service.update_security_metadata_async(new Metadata {
SecurityKey = key,
Strategy = strategy.CloneToMutable()
});
if (!result.success)
errors[index] = result.error_message;
} catch (Exception ex) {
errors[index] = ex.ToString();
}
}
}
public async Task<bool> verify_name_is_unique(string name) {
var (result, success) = await strategy_tuple.FirstAsyncTask(SparkUtils.DefaultTimeOut);
name = name.Trim().ToLower();
return success && !result.Values.Any(st => st.md.security.Synthetic.Name.Trim().ToLower() == name);
}
}
}
}
@@ -1,6 +1,7 @@
using bts;
using bts.api;
using bts.shell;
using bts.shell.spreader;
using bts.spark;
using mbts.persist;
using mbts.ui;
@@ -18,7 +19,10 @@ using static bts.shell.SyntheticManagementWindowFactory;
namespace mbts.spark {
class SyntheticManagementWindow : SparkWindow<StartupData>, ISharedWindow {
const string _window_type = "SyntheticManagement";
const string
_window_type = "SyntheticManagement",
_import_title = "Import Synthetics";
static readonly TimeSpan _import_op_sec_map_timeout = TimeSpan.FromSeconds(30);
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
StartupData _state;
OrdersModel _orders_model;
@@ -27,7 +31,6 @@ namespace mbts.spark {
IReadOnlyList<SparkCommand> _commands;
readonly IWindowSettingsManager<GridSettings> _settings_manager;
public SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, IWindowSettingsManager<GridSettings> settings_manager)
: base("Synthetic Management", bus, factory, SparkCommands.ShowSyntheticManagement.icon) {
_orders_model = orders_model;
@@ -90,7 +93,11 @@ namespace mbts.spark {
var dialog = new ShellSettingsDialog { Title = "Synthetic Management Grid Settings", DataContext = vm };
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
}))
})),
new SparkCommandCommand(
"Import Synthetics...", Icons.Import, FileDialogCommand.open_file(import, extensions: SyntheticSecurityParser.file_extension)) {
show_in_toolbar = false
}
};
_subscriptions = Disposable.Create(
@@ -105,13 +112,16 @@ namespace mbts.spark {
r == null ? Observable.Return(new RowStrategy?()) :
_orders_model.spreader.security_strategy(r.security.Id).Select(strategy => RowStrategy.create(r.security, strategy));
uint select_security_id(RowModificationParams? rmp) => rmp.HasValue ? rmp.Value.security.Id : 0;
IImmutableSet<uint> select_security_id(RowModificationParams? rmp) => ImmutableHashSet.Create(rmp.HasValue ? rmp.Value.security.Id : 0);
(SecurityImmutable, ExecutionStrategyImmutable) select_row_strategy_tuple(RowStrategy? rs) => rs.HasValue ? (rs.Value.security, rs.Value.strategy) : default;
bool can_execute(RowModificationParams? rmp) => rmp.HasValue && !rmp.Value.has_working_orders;
_vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, _settings_manager, _state.sorting_criteria, _state.filters ?? new FilterStates());
if (_view == null) _view = new SyntheticManagementView { DataContext = _vm.value };
else _view.DataContext = _vm.value;
void import(string path) =>
SyntheticImporter.import_from_file(path, _bus, _orders_model);
}
public override void Dispose() {
@@ -145,4 +155,4 @@ namespace mbts.spark {
new RowStrategy(security, strategy);
}
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Cross Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross)
omv_cross.CrossTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.CrossTicks);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.CrossTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeOppositeMarketVolumeBasedCrossThresholdModel : RowModelBase<QuantityMeasure> {
IDictionary<uint, QuantityMeasure> _value_store = new Dictionary<uint, QuantityMeasure>();
IDictionary<int, QuantityMeasure> _value_store = new Dictionary<int, QuantityMeasure>();
public override string display_name { get; } = "Threshold";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, QuantityMeasure value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_omv_cross(leg_tup) is PostHedgeOppositeMarketVolumeBasedForcedCross omv_cross)
omv_cross.QuantityThreshold = value;
}
@@ -36,7 +36,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.OppositeMarketVolumeBasedForcedCross;
QuantityMeasure _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.QuantityThreshold);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgeOppositeMarketVolumeBasedForcedCross.QuantityThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgePriceBasedCrossMarketTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Cross Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_price_based_cross(leg_tup) is PostHedgePriceBasedForcedCross price_based_cross)
price_based_cross.MarketCrossTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.PriceBasedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgePriceBasedForcedCross.MarketCrossTicks);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgePriceBasedForcedCross.MarketCrossTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgePriceBasedCrossTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_price_based_cross(leg_tup) is PostHedgePriceBasedForcedCross price_based_cross)
price_based_cross.TicksThreshold = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.PriceBasedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgePriceBasedForcedCross.TicksThreshold);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgePriceBasedForcedCross.TicksThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeTimeBasedCrossMarketTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Cross Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_time_based_cross(leg_tup) is PostHedgeTimeBasedForcedCross time_based_cross)
time_based_cross.MarketCrossTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.TimeBasedForcedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeTimeBasedForcedCross.MarketCrossTicks);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgeTimeBasedForcedCross.MarketCrossTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PostHedgeTimeBasedCrossTimeModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Time(ms)";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_time_based_cross(leg_tup) is PostHedgeTimeBasedForcedCross time_based_cross)
time_based_cross.TimeInMsThreshold = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PostHedgeParams.TimeBasedForcedCross;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PostHedgeTimeBasedForcedCross.TimeInMsThreshold);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PostHedgeTimeBasedForcedCross.TimeInMsThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PreHedgePaylessThresholdModel : RowModelBase<QuantityMeasure> {
IDictionary<uint, QuantityMeasure> _value_store = new Dictionary<uint, QuantityMeasure>();
IDictionary<int, QuantityMeasure> _value_store = new Dictionary<int, QuantityMeasure>();
public override string display_name { get; } = "Threshold";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, QuantityMeasure value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_payless_ticks(leg_tup) is PreHedgeQuantityBasedPayLessTicks payless_ticks)
payless_ticks.QuantityThreshold = value;
}
@@ -36,7 +36,7 @@ namespace mbts.spark {
leg_tup.leg_params.PreHedgeParams.QuantityBasedPaylessTicks;
QuantityMeasure _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayLessTicks.QuantityThreshold);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayLessTicks.QuantityThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PreHedgePaylessTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Payless Ticks";
public override string help_text { get; } = string.Empty;
@@ -23,7 +23,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_payless_ticks(leg_tup) is PreHedgeQuantityBasedPayLessTicks payless_ticks)
payless_ticks.PaylessTicks = value;
}
@@ -35,7 +35,7 @@ namespace mbts.spark {
leg_tup.leg_params.PreHedgeParams.QuantityBasedPaylessTicks;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayLessTicks.PaylessTicks);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayLessTicks.PaylessTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PreHedgePayupThresholdModel : RowModelBase<QuantityMeasure> {
IDictionary<uint, QuantityMeasure> _value_store = new Dictionary<uint, QuantityMeasure>();
IDictionary<int, QuantityMeasure> _value_store = new Dictionary<int, QuantityMeasure>();
public override string display_name { get; } = "Threshold";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, QuantityMeasure value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_payup_ticks(leg_tup) is PreHedgeQuantityBasedPayupTicks payup_ticks)
payup_ticks.QuantityThreshold = value;
}
@@ -36,7 +36,7 @@ namespace mbts.spark {
leg_tup.leg_params.PreHedgeParams.QuantityBasedPayupTicks;
QuantityMeasure _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayUpTicks.QuantityThreshold);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayUpTicks.QuantityThreshold);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class PreHedgePayupTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Payup Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_payup_ticks(leg_tup) is PreHedgeQuantityBasedPayupTicks payup_ticks)
payup_ticks.PayupTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.PreHedgeParams.QuantityBasedPayupTicks;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayUpTicks.PayupTicks);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.PreHedgeQuantityBasedPayUpTicks.PayupTicks);
}
}
}
@@ -9,7 +9,7 @@ namespace mbts.spark {
{
class ConditionalQuotingPriceTypeModel : RowModelBase<PriceType>
{
IDictionary<uint, PriceType> _value_store = new Dictionary<uint, PriceType>();
IDictionary<int, PriceType> _value_store = new Dictionary<int, PriceType>();
public override string display_name { get; } = "Price Type";
public override string help_text { get; } = string.Empty;
@@ -26,7 +26,7 @@ namespace mbts.spark {
public override void set_value(LegSpreaderParamsTuple leg_tup, PriceType value)
{
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_value(leg_tup) is ConditionalQuoting cond)
cond.PriceType = value;
}
@@ -37,7 +37,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
PriceType _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.PriceType);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.PriceType);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class ConditionalQuotingTicksModel : RowModelBase<int> {
IDictionary<uint, int> _value_store = new Dictionary<uint, int>();
IDictionary<int, int> _value_store = new Dictionary<int, int>();
public override string display_name { get; } = "Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, int value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_value(leg_tup) is ConditionalQuoting cond)
cond.NTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
int _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.NTicks);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.NTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Windows;
namespace mbts.spark {
partial class SyntheticEditorVM {
class QuoteHiddenModel : RowModelBase<bool>, IHaveFontParams {
IDictionary<uint, bool> _value_store = new Dictionary<uint, bool>();
IDictionary<int, bool> _value_store = new Dictionary<int, bool>();
public override string display_name { get; } = "Hidden";
public override string help_text { get; } = "A hidden quote is not sent out until the market crosses the computed quoting price. When that happens, the quote is sent out as an IOC order. This continues until the synthetic spreader order is fully filled, or cancelled.";
@@ -21,7 +21,7 @@ namespace mbts.spark {
_get_throttle(leg_tup) != null;
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
leg_tup.leg_params.QuoteParams.QuoteHidden = value ? new QuoteHidden() : null;
}
@@ -32,7 +32,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.QuoteHidden;
bool _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => false);
_value_store.GetValueOrDefault(leg_tup.id, _ => false);
}
}
}
@@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class QuoteInsideMarketDistanceTowardModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Toward Distance";
public override string help_text { get; } = string.Empty;
@@ -21,7 +21,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_inside_market_distance(leg_tup) is QuoteInsideMarketDistanceThrottle inside_market_distance)
inside_market_distance.OnTowardDistance = value;
}
@@ -33,7 +33,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.QuoteInsideMarketDistanceThrottle;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey);
_value_store.GetValueOrDefault(leg_tup.id);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class QuoteInsideMarketThrottleMinMoveModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Min Move";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_inside_market_throttle(leg_tup) is QuoteInsideMarketDistanceWithTravel inside_market_throttle)
inside_market_throttle.Limit = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.QuoteInsideMarketDistanceWithTravel;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.QuoteInsideMarketDistanceWithTravel.Limit);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.QuoteInsideMarketDistanceWithTravel.Limit);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class QuoteInsideMarketThrottleTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_inside_market_throttle(leg_tup) is QuoteInsideMarketDistanceWithTravel inside_market_throttle)
inside_market_throttle.TicksFromInside = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.QuoteInsideMarketDistanceWithTravel;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.QuoteInsideMarketDistanceWithTravel.TicksFromInside);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.QuoteInsideMarketDistanceWithTravel.TicksFromInside);
}
}
}
@@ -9,7 +9,7 @@ namespace mbts.spark
{
class QuoteQueueHolderModel : RowModelBase<uint>, IHaveFontParams
{
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public QuoteQueueHolderModel()
{
}
@@ -27,7 +27,7 @@ namespace mbts.spark
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value)
{
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
leg_tup.leg_params.QuoteParams.NQueueHolderOrders = value ;
}
@@ -38,7 +38,7 @@ namespace mbts.spark
leg_tup.leg_params.QuoteParams;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => 0u);
_value_store.GetValueOrDefault(leg_tup.id, _ => 0u);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
internal class ShowQuantityQuantityModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name => "Quantity";
@@ -24,7 +24,7 @@ namespace mbts.spark {
_get_value(leg_tup) is NullableQuantity show_quantity ? show_quantity.Quantity : _value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_value(leg_tup) is NullableQuantity show_quantity) {
show_quantity.Quantity = value;
}
@@ -37,7 +37,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.ShowQuantity;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ShowQuantity.Quantity);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.ShowQuantity.Quantity);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class QuoteThrottleInsideTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Inside Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_throttle(leg_tup) is QuoteMuteZone throttle)
throttle.InsideTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.QuoteMuteZone;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.QuoteMuteZone.InsideTicks);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.QuoteMuteZone.InsideTicks);
}
}
}
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class QuoteThrottleOutsideTicksModel : RowModelBase<uint> {
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
IDictionary<int, uint> _value_store = new Dictionary<int, uint>();
public override string display_name { get; } = "Outside Ticks";
public override string help_text { get; } = string.Empty;
@@ -22,7 +22,7 @@ namespace mbts.spark {
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value) {
_value_store[leg_tup.SecurityKey] = value;
_value_store[leg_tup.id] = value;
if (_get_throttle(leg_tup) is QuoteMuteZone throttle)
throttle.OutsideTicks = value;
}
@@ -34,7 +34,7 @@ namespace mbts.spark {
leg_tup.leg_params.QuoteParams.QuoteMuteZone;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.QuoteMuteZone.OutsideTicks);
_value_store.GetValueOrDefault(leg_tup.id, _ => SyntheticDataHelper.Defaults.QuoteMuteZone.OutsideTicks);
}
}
}
@@ -42,8 +42,8 @@ namespace mbts.spark {
}
internal abstract class RowModelBase<T> : ViewModelBase, IRowModel {
readonly Dictionary<uint, ReplaySubject<bool>> _enabled_store = new Dictionary<uint, ReplaySubject<bool>>();
readonly Dictionary<uint, Subject<T>> _override_value_store = new Dictionary<uint, Subject<T>>();
readonly Dictionary<LegSpreaderParamsTuple, ReplaySubject<bool>> _enabled_store = new Dictionary<LegSpreaderParamsTuple, ReplaySubject<bool>>();
readonly Dictionary<LegSpreaderParamsTuple, Subject<T>> _override_value_store = new Dictionary<LegSpreaderParamsTuple, Subject<T>>();
protected RowModelBase() { }
@@ -59,22 +59,22 @@ namespace mbts.spark {
public virtual ICell create_cell(LegSpreaderParamsTuple leg_tup) => new Cell<T>(this, leg_tup);
public void set_enabled(LegSpreaderParamsTuple leg_tup, bool enabled) =>
_enabled_store.GetOrAdd(leg_tup.SecurityKey, sec => new ReplaySubject<bool>(1)).OnNext(enabled);
_enabled_store.GetOrAdd(leg_tup, sec => new ReplaySubject<bool>(1)).OnNext(enabled);
public void override_value(LegSpreaderParamsTuple leg_tup, T value) {
if (_override_value_store.TryGetValue(leg_tup.SecurityKey, out Subject<T> subject))
if (_override_value_store.TryGetValue(leg_tup, out Subject<T> subject))
subject.OnNext(value);
}
public IObservable<bool> observe_enabled(LegSpreaderParamsTuple leg_tup) =>
_enabled_store.GetOrAdd(leg_tup.leg.Security.Id, sec => {
_enabled_store.GetOrAdd(leg_tup, sec => {
var rs = new ReplaySubject<bool>(1);
rs.OnNext(_get_enabled(leg_tup));
return rs;
});
public IObservable<T> observe_override_value(LegSpreaderParamsTuple leg_tup) =>
_override_value_store.GetOrAdd(leg_tup.leg.Security.Id, sec => new Subject<T>());
_override_value_store.GetOrAdd(leg_tup, sec => new Subject<T>());
protected virtual bool _get_enabled(LegSpreaderParamsTuple leg_tup) => true;
#region IRowModel (explicit)
+17 -13
View File
@@ -1,6 +1,4 @@
using bts.api;
using mbts.collections;
using mbts.ui.controls;
using mbts.ui.controls;
using mbts.ui.spark;
using mbts.ui.utility;
using System;
@@ -18,8 +16,8 @@ namespace mbts.spark {
ParameterSection[] section_path { get; }
}
internal class RowVM : DataGridExRow<Security>, IParameterRowVM, IDisposable {
readonly ObservableKeyedCollection<KeyValuePair<uint, (ICell cell, LegSpreaderParamsTuple leg)>, uint> _cells = new ObservableKeyedCollection<KeyValuePair<uint, (ICell cell, LegSpreaderParamsTuple leg)>, uint>(kv => kv.Key);
internal class RowVM : DataGridExRow<LegSpreaderParamsTuple>, IParameterRowVM, IDisposable {
readonly ObservableCollection<(LegSpreaderParamsTuple leg, ICell cell)> _cells = new ObservableCollection<(LegSpreaderParamsTuple leg, ICell cell)>();
readonly Property<bool> _has_errors;
IDisposable _dispose;
@@ -34,15 +32,16 @@ namespace mbts.spark {
_has_errors = backing(nameof(has_errors), false);
_dispose = Disposable.Create(
ObservableAutoWrapper.connect(legs, _cells, lt => new KeyValuePair<uint, (ICell cell, LegSpreaderParamsTuple leg)>(lt.SecurityKey, (model.create_cell(lt), lt))),
ObservableAutoWrapper.connect_item_hooks(_cells, kv => {
kv.Value.cell.value_changed += raise_cell_value_changed;
ObservableAutoWrapper.connect(legs, _cells, lt => (lt, model.create_cell(lt))),
ObservableAutoWrapper.connect_item_hooks(_cells, tup => {
var (leg, cell) = tup;
cell.value_changed += raise_cell_value_changed;
return new CompositeDisposable {
Disposable.Create(() => kv.Value.cell.value_changed -= raise_cell_value_changed),
kv.Value.cell as IDisposable ?? Disposable.empty
Disposable.Create(() => cell.value_changed -= raise_cell_value_changed),
cell as IDisposable ?? Disposable.empty
};
void raise_cell_value_changed() =>
cell_value_changed?.Invoke(kv.Value.cell, kv.Value.leg);
cell_value_changed?.Invoke(cell, leg);
}, true));
}
@@ -56,8 +55,13 @@ namespace mbts.spark {
public bool read_only { get; }
public bool has_errors => _has_errors.Value;
public override object get_cell(Security col_key) =>
_cells.TryGetValue(col_key.Id, out KeyValuePair<uint, (ICell cell, LegSpreaderParamsTuple leg)> kv) ? kv.Value.cell : null;
public override object get_cell(LegSpreaderParamsTuple leg_tup) =>
// Note this is an O(n) op.
// If need ever arises to improve perf here,
// an id member could be added to the LegSpreaderParamsTuple,
// which could serve as a sorting key to enable Log(n) lookups.
leg_tup != null &&
_cells.TryGetFirst(t => t.leg == leg_tup, out var match) ? match.cell : null;
public ParameterSection this[string section_no] =>
int.TryParse(section_no, out int i) && i < section_path.Length ? section_path[i] : null;
@@ -88,7 +88,7 @@ namespace mbts.spark {
_close_new_leg_popup();
}
};
remove_leg = new DelegateCommand<Security>(s => leg_tuples.removeFirst(l => l.leg.Security.Id == s.Id));
remove_leg = new DelegateCommand<LegSpreaderParamsTuple>(l => leg_tuples.Remove(l));
commit = new AsyncCommand(commit_async, () => leg_count >= 2);
if (override_auto_set_behavior) {
@@ -112,6 +112,14 @@ namespace mbts.spark {
commit.RaiseCanExecuteChanged();
_validate_leg_count();
};
_resources.Add(ObservableAutoWrapper.connect_item_hooks(leg_tuples, l => {
l.security_changed += on_sec_changed;
return Disposable.Create(() => l.security_changed -= on_sec_changed);
void on_sec_changed() {
if (_set_name_automatically)
_deduce_name();
}
}));
async Task commit_async() {
RequestUpdate?.Invoke();
@@ -130,12 +138,7 @@ namespace mbts.spark {
if (valid) {
_synthetic.Synthetic.Legs.SetItems(leg_tuples.Select(l => l.leg));
_synthetic.Symbol = _deduce_symbol(_synthetic);
double tick_frac = _tick_factors.tick_unit / _tick_factors.tick_base;
_synthetic.ContractSpec.LowTick = tick_frac;
_synthetic.ContractSpec.HighTick = tick_frac;
_synthetic.ContractSpec.PriceBreak = double.MaxValue;
_synthetic.ContractSpec.MainFraction = (uint)MathUtils.factor_out(_tick_factors.tick_base, 10);
_synthetic.ContractSpec.PriceDisplayFormat = (uint)_tick_factors.display_digits();
_synthetic.ContractSpec = _tick_factors.to_spec();
uint? id = await model.create_synthetic(new SecurityImmutable(_synthetic));
if (id.HasValue && id != 0) {
if (await model.save_strategy(id.Value, get_strategy())) {
@@ -377,4 +380,4 @@ namespace mbts.spark {
else clear_error(nameof(leg_count));
}
}
}
}
@@ -5,6 +5,7 @@ using bts.collections;
using bts.shell.fills;
using bts.spark;
using bts.utils;
using bts.utils.csv;
using mbts.collections;
using mbts.persist;
using mbts.spark.filters;
@@ -23,6 +24,7 @@ using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using static bts.api.orderman.Fill.Types;
using static bts.shell.FillsWindowFactory;
@@ -38,10 +40,10 @@ namespace mbts.spark {
IWindowSettingsManager<GridSettings> _settings_manager;
int _total_row_count;
public FillsVM(IBus bus, OrdersModel model, IObservable<FillsSettings> state_obs, IWindowSettingsManager<GridSettings> settings_manager)
: this(bus, model, state_obs, _get_observable(bus, model, state_obs), settings_manager) { }
public FillsVM(IBus bus, OrdersModel model, FillsSettings settings, IWindowSettingsManager<GridSettings> settings_manager)
: this(bus, model, settings, _get_observable(bus, model, settings), settings_manager) { }
public FillsVM(IBus bus, OrdersModel model, IObservable<FillsSettings> state_obs, IObservable<TableUpdate<FillTuple, string>> fills_source, IWindowSettingsManager<GridSettings> settings_manager) {
public FillsVM(IBus bus, OrdersModel model, FillsSettings settings, IObservable<TableUpdate<FillTuple, string>> fills_source, IWindowSettingsManager<GridSettings> settings_manager) {
_model = model;
_settings_manager = settings_manager;
/// Columns
@@ -136,8 +138,6 @@ namespace mbts.spark {
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
if (_settings_manager != null)
grid_settings_manager.load_from(_settings_manager.current_settings());
var filter_src = Observable.FromEvent<Action<Func<FillTuple, bool>>, Func<FillTuple, bool>>(cb => _rows_manager.filter_changed += cb, cb => _rows_manager.filter_changed -= cb);
var comp_src = Observable.FromEvent<Action<IComparer<FillTuple>>, IComparer<FillTuple>>(cb => _rows_manager.comparer_changed += cb, cb => _rows_manager.comparer_changed -= cb);
TableUpdate<FillTuple, string> last_unfiltered_fills = TableUpdate<FillTuple, string>.mk_empty(FillTuple.key_fn);
var fills_buffer = Observable.Create<TableUpdate<FillTuple, string>>(o => {
// this is what .Cache() does, but is a little cheaper, since we know subscriptions and callbacks occur on the same thread (i.e., on this Dispatcher).
@@ -145,29 +145,36 @@ namespace mbts.spark {
_unfiltered_fills_changed += o.OnNext;
return Disposable.Create(() => _unfiltered_fills_changed -= o.OnNext);
});
if (settings.grid != null && _settings_manager != null) {
// this means we are not using defaults,
// Note: settings_manager in turn notifies grid_settings_manager of this update
_settings_manager.instance_settings = settings.grid;
}
_rows_manager.on_settings(settings.filters ?? new FillsFilterStates {
fill_time = new TimeFilterTuple(
TimeFilterMode.CMEOpen,
TimeTextTuple.FromDateTime(DateTime.Today),
TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1)))
},
settings.sorting_criterion);
// resources/subscriptions
_dispose = new CompositeDisposable(
ObservableAutoWrapper.connect(col_tups, columns, ct => ct.col),
ObservableAutoWrapper.connect_item_hooks(col_tups, ct => ct.filter as IDisposable),
RXCollection.connect(rows, fills_buffer.Filter(filter_src, FillTuple.key_fn), comp_src, ft => new Row(ft, model), r => r.fill_tuple),
RXCollection.connect(
rows,
fills_buffer.Filter(_rows_manager.observe_filter(), FillTuple.key_fn),
_rows_manager.observe_comparer(),
ft => new Row(ft, model),
r => r.fill_tuple,
Row.update,
r => r.is_selected,
(r, is_selected) => r.is_selected = is_selected),
fills_source.OnDispatcher().Subscribe(t => {
last_unfiltered_fills = t;
Set(ref _total_row_count, t.state.Count, nameof(total_row_count));
_unfiltered_fills_changed?.Invoke(t);
}),
state_obs.OnDispatcher().Subscribe(s => {
if (s.grid != null && _settings_manager != null) {
// this means we are not using defaults,
// Note: settings_manager in turn notifies grid_settings_manager of this update
_settings_manager.instance_settings = s.grid;
}
var fs = s.filters ?? new FillsFilterStates {
fill_time = new TimeFilterTuple(
TimeFilterMode.CMEOpen,
TimeTextTuple.FromDateTime(DateTime.Today),
TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1)))
};
_rows_manager.on_settings(fs, s.sorting_criterion);
}),
model.subscribe_all_algos().SelectKeyValue(AlgoDataVM.from_data, d => AlgoID.from_algo_key(d.data.key)).OnDispatcher().Subscribe(root_algo_type_observer),
settings_manager == null ? Disposable.empty : settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager)
);
@@ -262,33 +269,40 @@ namespace mbts.spark {
}
public class Row : SecurityRow, IEquatable<Row>, IRowVM {
public readonly FillTuple fill_tuple;
public static readonly Func<Row, FillTuple, Row> update =
(r, ft) => {
using (_change_detector.detect_changes(r)) {
r._badge_fill = null;
r._root_algo_badge_color = null;
r._order_metadata = null;
r.fill_tuple = ft;
}
return r;
};
static readonly IPropertyChangedDetector<Row> _change_detector =
ObjectSnapshot.build_detector_except<Row>(nameof(fill_key), nameof(is_selected));
readonly OrdersModel _model;
readonly FillImmutable _fill;
readonly OrderRecordImmutable _order;
AlgoData? _root_algo => fill_tuple.root_algo;
AlgoDefinitionMetadata? _root_algo_meta => fill_tuple.root_algo_meta;
Brush _badge_fill;
Color? _root_algo_badge_color;
OrderMetadata _order_metadata;
bool _is_selected;
public Row(FillTuple fill_tuple, OrdersModel model) :
base(fill_tuple.security ?? SecurityDataHelper.Defaults.SecurityImmutable) {
this.fill_tuple = fill_tuple;
_fill = fill_tuple.fill;
_order = fill_tuple.order ?? OrderDataHelper.Defaults.OrderRecordImmutable;
_model = model;
account = fill_tuple.account;
}
public FillTuple fill_tuple { get; private set; }
public DateTime fill_time => _fill.Timestamp.to_local_time();
public TimeInForce time_in_force => _order.time_in_force();
public OrderType order_type => _order.order_type();
public string display_name => OrderUtils.display_name(_order, fill_tuple.algo);
public OrderMetadata order_metadata => _order_metadata ?? (_order_metadata = _model.factory.get_metadata(_order.Def.CustomDef.CustomCase));
public Brush badge_fill => _badge_fill ?? (_badge_fill = OrderUtils.badge_fill(fill_tuple.algo_meta, order_metadata));
public string account { get; private set; }
public string account => fill_tuple.account;
public string user => _order.user();
public Venue exchange => security.Exchange;
public Side side => _fill.Side;
@@ -298,6 +312,11 @@ namespace mbts.spark {
public FillStatus fill_status => _fill.FillStatus;
public string fill_key => _fill.FillKey;
public string order_key => _order.Key.ToString();
public string symbol => fill_tuple.security?.Symbol;
public bool is_selected {
get => _is_selected;
set => Set(ref _is_selected, value);
}
// Root Algo
public OrderType root_algo_order_type => _root_algo.HasValue ? _root_algo.Value.value.CustomCase : OrderType.None;
@@ -305,12 +324,39 @@ namespace mbts.spark {
public Color root_algo_badge_color => _root_algo_badge_color ?? (_root_algo_badge_color = OrderUtils.badge_color(_root_algo_meta, root_algo_order_metadata)).Value;
public string root_algo_display_name => fill_tuple.root_order_security?.display_name() ?? OrderType.None.display_name();
FillImmutable _fill => fill_tuple.fill;
OrderRecordImmutable _order => fill_tuple.order ?? OrderDataHelper.Defaults.OrderRecordImmutable;
AlgoData? _root_algo => fill_tuple.root_algo;
AlgoDefinitionMetadata? _root_algo_meta => fill_tuple.root_algo_meta;
public bool Equals(Row other) => other != null && other._fill.Equals(_fill) && other._order.Equals(_order);
public override bool Equals(object obj) => Equals(obj as Row);
public override int GetHashCode() => _fill.FillKey.GetHashCode() ^ _order.Key.GetHashCode();
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(typeof(Row),
new string[] {
nameof(security),
nameof(symbol),
nameof(order_type),
nameof(root_algo_display_name),
nameof(side),
nameof(fill_time),
nameof(user),
nameof(account),
nameof(exchange),
nameof(time_in_force),
nameof(size),
nameof(price),
nameof(fill_kind),
nameof(fill_status),
nameof(fill_key),
nameof(order_key),
nameof(security_key)
});
public event Action invalidated;
void IRowVM.raise_invalidated() => invalidated?.Invoke();
}
}
}
}
@@ -9,7 +9,6 @@
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:u="clr-namespace:mbts.ui"
xmlns:api="clr-namespace:bts.api;assembly=spark_api"
xmlns:bts="clr-namespace:mbts.ui.controls"
FocusManager.IsFocusScope="True"
Background="{StaticResource CommonBackground}"
>
@@ -49,10 +48,15 @@
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
/>
</c:DIYGrid.CommandBindings>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<usp:FillsRowView Background="{Binding side, Converter={StaticResource SideToBrushConverter}}"/>
</DataTemplate>
<c:DIYGrid.RowStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
<Setter Property="IsSelected" Value="{Binding is_selected}"/>
</Style>
</c:DIYGrid.RowStyle>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<usp:FillsRowView Background="{Binding side, Converter={StaticResource SideToBrushConverter}}"/>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
@@ -3,10 +3,10 @@ using bts.api;
using bts.shell;
using bts.shell.fills;
using bts.spark;
using bts.utils.csv;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.shell;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
@@ -26,7 +26,6 @@ namespace mbts.spark {
FillsView _view;
FillsSettings _state;
IReadOnlyList<SparkCommand> _commands;
IDisposable _subscriptions;
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, FillsSettings state, WindowSettingsManager<GridSettings> settings_manager)
: base("Fills", bus, factory, SparkCommands.ShowFills().icon) {
@@ -36,8 +35,6 @@ namespace mbts.spark {
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().fills ? "Fills" : null;
}
event Action<FillsSettings> _state_changed;
public override string window_type => window_type_name;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
@@ -54,11 +51,11 @@ namespace mbts.spark {
public void thread_init() {
_init_guard.check_access();
Log.notnull(_state);
var state_src = Observable.FromEvent<Action<FillsSettings>, FillsSettings>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
_vm = new FillsVM(_bus, _orders_model, state_src, _settings_manager);
_vm = new FillsVM(_bus, _orders_model, _state, _settings_manager);
_view = new FillsView { DataContext = _vm };
_commands = new List<SparkCommand> {
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.rows.ToCsv(FillsVM.Row.csv_ordered_properties), "Fills")),
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
SparkCommands.window_settings_command(new DialogCommand(() => {
var vm = new ShellSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
@@ -77,15 +74,6 @@ namespace mbts.spark {
return dialog;
}))
};
_subscriptions = Disposable.Create(() =>
_state_changed?.Invoke(
_state = new FillsSettings(_state.orders,
_vm.get_sorting_criterion().ToList(),
_vm.get_filters(),
_vm.grid_settings_manager.create()
)
)
);
}
public override void Dispose() {
@@ -93,4 +81,4 @@ namespace mbts.spark {
Commands.OfType<IDisposable>().DisposeAll();
}
}
}
}
@@ -284,6 +284,7 @@ namespace mbts.spark {
favorite_algos = new LadderFavoriteAlgosSettings(null, 2, null);
show_warn_on_cross = _default_show_warn_on_cross;
enforce_max_clip = _default_enforce_max_clip;
status_message_visible = true;
}
public static LadderSettings Default {
@@ -351,6 +352,7 @@ namespace mbts.spark {
mini_ob = new LadderMiniOrderBookSettings(bts.shell.orderbook.OrderBookStartupData.default_mini_grid.general),
max_clip = 0,
price_display_format = PriceDisplayFormat.Default,
status_message_visible = true,
};
rv.row_height = LadderRender.measure_row_height(rv);
return rv;
@@ -519,6 +521,9 @@ namespace mbts.spark {
[ProtoMember(60)]
public PriceDisplayFormat price_display_format { get; set; }
[ProtoMember(61), DefaultValue(true)]
public bool status_message_visible { get; set; }
public void sanitize() =>
sanitize(this);
@@ -623,7 +628,8 @@ namespace mbts.spark {
a.mini_ob == b.mini_ob &&
a.enforce_max_clip == b.enforce_max_clip &&
a.max_clip == b.max_clip &&
a.price_display_format == b.price_display_format;
a.price_display_format == b.price_display_format &&
a.status_message_visible == b.status_message_visible;
public bool Equals(LadderSettings other) =>
profile_equals(this, other) &&
@@ -79,7 +79,8 @@ namespace mbts.spark {
_clear_visibility = new OrderEntryPanelItemVisibilityVM("Clear"),
_cancel_buys_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Buys"),
_cancel_sells_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Sells"),
_cancel_all_visibility = new OrderEntryPanelItemVisibilityVM("Cancel All");
_cancel_all_visibility = new OrderEntryPanelItemVisibilityVM("Cancel All"),
_status_message_visibility = new OrderEntryPanelItemVisibilityVM("Status Message");
public LadderSettingsVM(OrdersModel model, SecurityImmutable security) {
_model = model;
@@ -253,6 +254,7 @@ namespace mbts.spark {
yield return _cancel_buys_visibility;
yield return _cancel_sells_visibility;
yield return _cancel_all_visibility;
yield return _status_message_visibility;
}
}
@@ -296,6 +298,7 @@ namespace mbts.spark {
yield return _cancel_buys_visibility;
yield return _cancel_sells_visibility;
yield return _cancel_all_visibility;
yield return _status_message_visibility;
// other
yield return size_display;
yield return favorite_algos_vm;
@@ -361,6 +364,7 @@ namespace mbts.spark {
_cancel_all_visibility.show = settings.cancel_all_visible;
price_format = settings.net_change;
price_display_format = settings.price_display_format;
_status_message_visibility.show = settings.status_message_visible;
// safety checks
show_warn_on_cross = settings.show_warn_on_cross;
enforce_max_clip = settings.enforce_max_clip;
@@ -438,6 +442,7 @@ namespace mbts.spark {
clone.zoom_slider_visible = _zoom_slider_visbility.show;
clone.net_change = price_format;
clone.price_display_format = price_display_format;
clone.status_message_visible = _status_message_visibility.show;
// safety checks
clone.show_warn_on_cross = show_warn_on_cross;
clone.enforce_max_clip = enforce_max_clip;
@@ -32,7 +32,7 @@ namespace mbts.spark {
readonly Property<bool> _auto_clear;
readonly PropertyAutoWrapper<LadderSettings, bool> _show_day, _show_high, _show_low, _show_open, _show_settle, _show_volume, _show_account,
_show_tif, _show_order_type, _show_size_preset, _show_auto_clear, _show_clear, _show_cancel_buys,
_show_cancel_sells, _show_cancel_all, _show_zoom;
_show_cancel_sells, _show_cancel_all, _show_zoom, _show_status_message;
readonly PropertyAutoWrapper<LadderSettings, Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
readonly HashSet<AlgoKey> _sticky_algos = new HashSet<AlgoKey>();
double
@@ -116,6 +116,7 @@ namespace mbts.spark {
(_show_cancel_sells = bool_prop(nameof(show_cancel_sells), s => s.cancel_sells_visible)).set,
(_show_cancel_all = bool_prop(nameof(show_cancel_all), s => s.cancel_all_visible)).set,
(_show_zoom = bool_prop(nameof(show_zoom), s => s.zoom_slider_visible)).set,
(_show_status_message = bool_prop(nameof(show_status_message), s => s.status_message_visible)).set,
s => {
if (
Set(ref _show_account_position, s.account_position_visible, nameof(show_account_position)) |
@@ -341,6 +342,7 @@ namespace mbts.spark {
public bool show_cancel_sells => _show_cancel_sells.view;
public bool show_cancel_all => _show_cancel_all.view;
public bool show_zoom => _show_zoom.view;
public bool show_status_message => _show_status_message.view;
// left panel scroll props
public double left_panel_view_port_height {
get => _left_panel_view_port_height;
@@ -627,8 +627,8 @@
message to have the topmost Z order
-->
<GridSplitter Grid.Column="0" Grid.RowSpan="2" Width="5" />
<!-- Ladder Status message -->
<Button x:Name="_status" Grid.ColumnSpan="4" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Command="{Binding status_click_command}">
<!-- Ladder Status message -->
<Button x:Name="_status" Grid.ColumnSpan="4" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Command="{Binding status_click_command}" Visibility="{Binding show_status_message, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button.Template>
<ControlTemplate>
<TextBlock Text="{Binding status_message}" Background="{Binding status_background}" Padding="5,0" />
@@ -4,6 +4,7 @@ using bts.api.orderman;
using bts.collections;
using bts.spark;
using bts.utils;
using bts.utils.csv;
using mbts.ui;
using mbts.ui.controls;
using System;
@@ -11,6 +12,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Reactive.Linq;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Windows.Input;
using System.Windows.Media;
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
@@ -60,6 +62,35 @@ namespace mbts.spark {
public event Action<(OrderSecurityRootPair oldval, OrderSecurityRootPair newval)> updated;
public event PropertyChangedEventHandler PropertyChanged;
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(
typeof(OrderBookRowVM),
new string[] {
nameof(security_text),
nameof(symbol),
nameof(time_created),
nameof(last_change),
nameof(user),
nameof(account),
nameof(exchange),
nameof(time_in_force),
nameof(order_type),
nameof(root_algo_display_name),
nameof(order_sub_type),
nameof(side),
nameof(quantity),
nameof(filled_quantity),
nameof(price),
nameof(avg_price),
nameof(last_price),
nameof(piq),
nameof(status),
// last message (could contains commas, so skipping)
nameof(security_key),
nameof(order_key),
nameof(parent_order_key),
});
// Core
public OrderSecurityRootPair order_tuple { get; private set; }
public OrderRecordImmutable order => order_tuple.order.order;
@@ -245,7 +245,7 @@ namespace mbts.spark {
.Subscribe(is_visible => {
if (is_visible) {
var init = _cached_fills_settings.to_startup_data();
fills_vm.value = new FillsVM(bus, model, Observable.Return(init), model.on_fill_tuples.Filter(mk_fills_slave_predicate_src(), ft => ft.fill.FillKey, _ => false), _fills_settings_manager);
fills_vm.value = new FillsVM(bus, model, init, model.on_fill_tuples.Filter(mk_fills_slave_predicate_src(), ft => ft.fill.FillKey, _ => false), _fills_settings_manager);
} else if (fills_vm.value != null) {
_cached_fills_settings = _get_fills_settings(fills_vm.value);
fills_vm.value = null;
@@ -4,6 +4,7 @@ using bts.shell;
using bts.shell.fills;
using bts.shell.orderbook;
using bts.spark;
using bts.utils.csv;
using bts.view;
using mbts.persist;
using mbts.ui;
@@ -184,6 +185,7 @@ namespace mbts.spark {
show_grid_settings_command.group = CommandGroups.Auxiliary;
_commands = new SparkCommand[] {
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.rows.ToCsv(OrderBookRowVM.csv_ordered_properties), "OrderBook")),
toggle_show_right_panel_command,
attach_selected_orders_command,
detach_selected_orders_command,
@@ -383,7 +383,7 @@ namespace bts.shell.pnl {
// hide implicit cols
state.grid = _hide_cols(state.grid, anchors);
return new FillsVM(
_bus, _orders_model, Observable.Return(state), new WindowSettingsManager<GridSettings>.DisconnectedImpl(state.grid));
_bus, _orders_model, state, new WindowSettingsManager<GridSettings>.DisconnectedImpl(state.grid));
}
public FillsVM mk_fills_vm(PNLRowCollectionViewGroup group, ColumnKeys key) {
@@ -393,7 +393,7 @@ namespace bts.shell.pnl {
// hide implicit cols
state.grid = _hide_cols(state.grid, anchors.WrapSubRange(0, group.level));
return new FillsVM(
_bus, _orders_model, Observable.Return(state), _visible_fills_source(), new WindowSettingsManager<GridSettings>.DisconnectedImpl(state.grid));
_bus, _orders_model, state, _visible_fills_source(), new WindowSettingsManager<GridSettings>.DisconnectedImpl(state.grid));
}
public FillsVM mk_fills_vm(PNLHeaderRowDisplay header, ColumnKeys key) {
@@ -419,7 +419,7 @@ namespace bts.shell.pnl {
break;
}
return new FillsVM(
_bus, _orders_model, Observable.Return(state), src, new WindowSettingsManager<GridSettings>.DisconnectedImpl(state.grid));
_bus, _orders_model, state, src, new WindowSettingsManager<GridSettings>.DisconnectedImpl(state.grid));
}
public OrderBookVM mk_orderbook_vm(PNLHeaderRowDisplay header, ColumnKeys key) {
Loaded 100 of 500 files, more files were not shown because too many files have changed in this diff. Show more