Compare commits

..
1135 Commits
Author SHA1 Message Date
Kevin Darby cbf1fad2dd Merge branch 'master' of bitbucket.org:bluetradesys/spark into ml 2019-02-11 11:30:08 -05:00
Jake Norbut 8a3a35a822 Merged in SPARK-2025 (pull request #160)
RTM - SPARK-2025

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2019-02-11 15:01:35 +00:00
Pedro Pinto 924b654f5f Update release notes. 2019-02-11 09:53:30 -05:00
Pedro Pinto dac01b3004 Merge branch 'master' into SPARK-2025 2019-02-11 09:49:19 -05:00
Pedro Pinto 508c542d56 Merge branch 'master' into SPARK-2025 2019-02-11 09:46:57 -05:00
Jake Norbut 9586b72653 Merged in SPARK-1975 (pull request #155)
RTM - SPARK-1975

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-02-11 14:45:32 +00:00
Pedro Pinto ee21b4f597 Updated release notes. 2019-02-11 09:42:37 -05:00
Pedro Pinto 964b516504 Merge branch 'master' into SPARK-1975 2019-02-11 09:23:46 -05:00
Pedro Pinto be6124e72f Updated release notes. 2019-02-11 09:06:05 -05:00
Eric Harding 578b9e7570 Merged in api_manual (pull request #159)
RTM: Add ApiThreading man page
2019-02-11 14:01:52 +00:00
Pedro Pinto fd66ef6993 Merge branch 'master' into api_manual 2019-02-11 08:59:24 -05:00
Pedro Pinto 1301130afb Quieted the build. 2019-02-11 08:58:00 -05:00
Jake Norbut 7daa96d246 Merged in SPARK-1513 (pull request #158)
RTM - SPARK-1513
2019-02-11 13:56:09 +00:00
Pedro Pinto 49e5eab7d3 Updated release notes. 2019-02-11 08:55:10 -05:00
Michael Harding fee4541e38 Merged in clip_limits_fix (pull request #162)
RTM Fix clip limits test
2019-02-11 13:38:30 +00:00
Kevin Darby 1fbc1584e5 Merge branch 'algos2' of bitbucket.org:bluetradesys/spark into ml 2019-02-08 17:19:16 -05:00
Kevin Darby 02defef93d oco testing 2019-02-08 14:55:16 -05:00
Michael Harding 9132b4a178 Fix clip limits test 2019-02-08 12:37:09 -05:00
Kevin Darby 9390fb0639 per Brian standardizing on Stop Ticks, Stop Type, Profit Ticks, Profit Type 2019-02-06 17:41:24 -05:00
Kevin Darby 82a8d007bf only generate package on release build 2019-02-06 17:06:22 -05:00
Kevin Darby cff32ad09a for now I'd like to keep these client and server docs separate since they are two repos 2019-02-06 17:03:23 -05:00
Eric Harding 7b9071a1b2 Review feedback 2 2019-02-06 14:49:17 -05:00
Eric Harding 99f1d1fa01 Review feedback 2019-02-06 14:48:18 -05:00
Jake Norbut 03fe75cfd2 commit button now unselects row 2019-02-06 13:38:40 -06:00
Eric Harding a7d71fe170 Move ApiThreading doc 2019-02-06 14:31:04 -05:00
Eric Harding 00ce3adde5 Add ApiThreading man page (draft) 2019-02-06 13:10:18 -05:00
Kevin Darby 6d774d46c9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2019-02-05 17:51:13 -05:00
Kevin Darby b39812aad0 add RelativeSide enum for OCO. Fix SPARK-2022 2019-02-05 17:50:08 -05:00
Jake Norbut fb059e5571 refine comment 2019-02-05 14:36:51 -06:00
Jake Norbut 1864c27584 Anything that used to hit get_cme_gen_open now hits get_start_of_trading_day 2019-02-05 14:35:10 -06:00
Kevin Darby 8b7f31ccd6 Merged in update_sdk_docs (pull request #151)
RTM: SPARK-1983, SPARK-1985, SPARK-1982, SPARK-1981

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-02-05 19:37:39 +00:00
Eric Harding b91f998239 Merged in replay_fallback (pull request #156)
RTM: Replay fallback
2019-02-05 19:37:18 +00:00
Pedro Pinto 183916118e Merged in limit_bug (pull request #157)
Fixed limit enforcement bug in order activation.

Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
2019-02-05 19:36:57 +00:00
Pedro Pinto 8d200ee7f3 Added to release notes. 2019-02-05 14:32:25 -05:00
Pedro Pinto d59d6fd095 Fixed limit enforcement bug in order activation. 2019-02-05 14:19:01 -05:00
Eric Harding c53f69bc53 Fix Launch.json working directory 2019-02-05 14:06:50 -05:00
Eric Harding e234a76199 Fall back to data_files if http://mdreplay or download cache are not available. 2019-02-05 12:34:11 -05:00
Eric Harding cf432a82a0 Add a small md recording to the build for offline access. Automatically unxz data_files 2019-02-05 12:33:43 -05:00
Jake Norbut 5b40b4b4ca Hiding Security Key column in default settings in all grids 2019-02-05 11:14:40 -06:00
Jake Norbut dd6b415ce7 remove trade key column from Time and Sales 2019-02-05 11:06:55 -06:00
Kevin Darby f99717d880 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2019-02-05 10:03:16 -05:00
Jake Norbut b61cf170fe release notes refinement 2019-02-05 08:28:46 -06:00
Jake Norbut 7915c84b6a updating notes 2019-02-04 18:30:28 -06:00
Jake Norbut 3aece4fb97 Merged in SPARK-1987 (pull request #154)
RTM - SPARK-1986, SPARK-1987

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-02-04 22:40:39 +00:00
Kevin Darby f224b8be43 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2019-02-04 16:57:05 -05:00
Jake Norbut be4dd05ba6 hiding Hidden and price type 2019-02-04 14:43:22 -06:00
Jake Norbut 570b362a7e release notes - words 2019-02-04 14:20:50 -06:00
Jake Norbut 944714adc5 Updating release notes describing Floating Order Book 2019-02-04 14:19:43 -06:00
Pedro Pinto fee7dcedb8 Merged in dev_doc (pull request #127)
Dev doc
2019-02-04 20:14:30 +00:00
Pedro Pinto 0883b91ef8 dev_process.md edited online with Bitbucket 2019-02-04 20:14:17 +00:00
Kevin Darby f43269f688 Merged in proto36 (pull request #153)
RTM move externals pointer to proto36, small changes in spark to support that

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-02-04 20:10:00 +00:00
Jake Norbut 20728b7e8c Merged in miniob (pull request #149)
RTM - Mini OrderBook

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-02-04 20:08:31 +00:00
Pedro Pinto b53060d665 Added release notes 2019-02-04 15:05:49 -05:00
Pedro Pinto 01d94748a2 Merge branch 'miniob' of bitbucket.org:bluetradesys/spark into miniob 2019-02-04 15:02:59 -05:00
Pedro Pinto ee4ebd8ccf Trying to fix the linux/win war on CMAkeDeps.cmake 2019-02-04 15:02:16 -05:00
Eric Harding ef3e3d6edc Wait to give obvm obvm a chance to load rows and not flash 2019-02-04 14:55:33 -05:00
Jake Norbut 6d4ff5707c PR edits - Style cop 2019-02-04 13:20:39 -06:00
Jake Norbut 158b8ac64d PR edit - simplify LINQ expressions when using Where and FirstOrDefault 2019-02-04 13:17:24 -06:00
Jake Norbut c11fc37781 PR - naming 2019-02-04 13:12:33 -06:00
Jake Norbut e3ca2194d0 PR edit - changing orderbook_vm property to method to better reflect the nature of the beast 2019-02-04 13:11:24 -06:00
Jake Norbut e49c8e32f2 PR edits - Simplifying XAML since UserControl is a ContentControl 2019-02-04 13:01:51 -06:00
Jake Norbut 63754199bf PR edits - Hashset for the win over List for Contains 2019-02-04 12:40:19 -06:00
Jake Norbut 7af568a620 PR edit; Set is your friend 2019-02-04 12:34:56 -06:00
Jake Norbut dcf509d21b PR edits - devolving qty/dirty and price/dirty 2019-02-04 12:29:34 -06:00
Jake Norbut b8c3b36a98 PR edits 2019-02-04 11:34:04 -06:00
Jake Norbut 8e5afbfa40 PR edit; columns setter is now private 2019-02-04 11:27:58 -06:00
Jake Norbut 906031d1c3 MinWidth so that the vertical scrollbar does not cover up the content in the last column 2019-02-01 16:55:49 -06:00
Kevin Darby 5eedc1a977 moved that using std::vector out of header 2019-02-01 17:22:12 -05:00
Kevin Darby 021c163b64 move externals pointer to proto36, small changes in spark to support that 2019-02-01 16:41:11 -05:00
Jake Norbut c6c71f0b04 enforce max height on popop size 2019-02-01 15:38:55 -06:00
Jake Norbut bc8c01a789 move edit price immediately after edit qty 2019-02-01 15:24:29 -06:00
Jake Norbut b93f794116 remove ladder tooltip and backing property 2019-02-01 15:18:39 -06:00
Jake Norbut 5369e67c7a minor refactoring, some notes after talking w/ john 2019-02-01 12:04:34 -06:00
Jake Norbut 65c4b41c74 inline price ticker 2019-02-01 11:02:56 -06:00
Jake Norbut 2ce9f7db34 qty ticker layout changes 2019-01-31 14:29:04 -06:00
Jake Norbut ecad4471b3 auto close popup when no orders remain 2019-01-31 11:50:27 -06:00
Jake Norbut 9b4a99dcb7 darken popup border 2019-01-31 11:48:48 -06:00
Jake Norbut 8d805409d5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into miniob 2019-01-31 08:08:23 -06:00
Jake Norbut 6a100bf3a6 remove checkin cruft 2019-01-30 19:30:13 -06:00
Jake Norbut 4047e5bdee making row stay in edit mode when a change has not yet been committed but the selection has been moved off the row 2019-01-30 19:24:50 -06:00
Kevin Darby 5aef712065 SPARK-1983, SPARK-1985, SPARK-1982, SPARK-1981 2019-01-30 18:18:21 -05:00
Jake Norbut dd19ac4bce hopefully use the right Dispatcher this time 2019-01-30 14:27:00 -06:00
Eric Harding ff2102e0cd Merged in run_system_tests (pull request #150)
RTM: Run system tests

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2019-01-30 18:17:03 +00:00
Eric Harding 29762512ad Merged master into run_system_tests 2019-01-30 12:06:39 -05:00
Eric Harding 289d40f24a Need spark_test.dll for system tests 2019-01-30 11:18:31 -05:00
Jake Norbut 947a905423 mini ob - inline qty tick up/down; formatting 2019-01-30 08:05:17 -06:00
Eric Harding dfe9ea12c3 provide_command_line for system_tests -configserver 2019-01-29 16:55:12 -05:00
Eric Harding a9c2261671 Simplify getting a security 2019-01-29 16:06:31 -05:00
Taha Afzal b8766089f0 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2019-01-29 15:51:22 -05:00
Taha Afzal 18d3c268de Added trace corID to first quote udpate call (after getting the first ack) 2019-01-29 15:51:16 -05:00
Pedro Pinto fdf255a7a3 Improved md performance logs. 2019-01-29 14:58:13 -05:00
Pedro Pinto 995be6cfd0 Added forgotten security count metric back. 2019-01-29 14:53:53 -05:00
Eric Harding 52ac650c58 Merge branch 'master' of bitbucket.org:bluetradesys/spark into run_system_tests 2019-01-29 12:06:36 -05:00
Michael Harding 96c7cc1528 sparkcmd test --list now lists only the current TestType 2019-01-29 10:29:24 -05:00
Michael Harding d81ef8668c Return int from sparkcmd 2019-01-29 09:31:14 -05:00
Eric Harding dab95d7e91 Make sure every sparkcmd command respects -configserver 2019-01-29 09:23:54 -05:00
Eric Harding 88045ea432 Add netcore publish to jenkinsbuild.cmd 2019-01-29 09:23:35 -05:00
Pedro Pinto 01113e645a release_notes.md edited online with Bitbucket 2019-01-28 23:16:02 +00:00
Pedro Pinto 17c57878e1 removed duplicate entries in requote perf log. 2019-01-28 23:14:15 +00:00
Pedro Pinto 4357fbc410 Removed test code. 2019-01-28 17:21:52 +00:00
Pedro Pinto 7d216978a3 Updated release notes. 2019-01-28 12:02:32 -05:00
Taha Afzal fc3d84c02a Merged in SPARK-1939-HedgingSpeed (pull request #148)
RTM. Various hedging speede improvements
2019-01-28 16:59:46 +00:00
Taha Afzal f6fd9155b1 externa; 2019-01-28 11:58:32 -05:00
Taha Afzal 7723f1e004 merging from master 2019-01-28 11:09:17 -05:00
Taha Afzal 041a7988c1 Spreader.proto::enable_child_order_activate now controls the child order's activate call 2019-01-28 11:01:05 -05:00
Michael Harding daa9800af2 Merged in SPARK-1877-system-tests (pull request #147)
RTM SPARK-1877 system tests

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-01-28 14:54:36 +00:00
Michael Harding dcb10a3484 SPARK-1877: Add test for no rule. Implememnt PR suggestions. 2019-01-28 09:26:55 -05:00
Taha Afzal 70e826b212 Merge branch 'warmup_activate' into SPARK-1939-HedgingSpeed 2019-01-28 08:48:25 -05:00
Jake Norbut af737564aa Merged in SPARK-1977 (pull request #145)
RTM - high dpi grid column oddity

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-01-28 13:46:53 +00:00
Pedro Pinto 582f97d1ce Merged in ticker_events_in_api (pull request #144)
RTM. Ticker events in api

Approved-by: Taha Afzal <taha.afzal@bluetradesys.com>
Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-01-28 13:46:15 +00:00
Taha Afzal a83c1eb633 calling ChildOrder's warm_up_activate() from Spreader 2019-01-28 07:26:18 -05:00
Pedro Pinto 1f893c73ad Added warm_up_activate method to ChildOrder to warm up cache. 2019-01-25 16:32:42 -05:00
Michael Harding ef755ea6fb SPARK-1877: Test Runner for multiple test types 2019-01-25 15:57:21 -05:00
Taha Afzal ebfc1bc485 Merge branch 'master' into SPARK-1939-HedgingSpeed 2019-01-25 13:34:55 -05:00
Jake Norbut a8646ebb3f adding comment back in 2019-01-24 13:45:59 -06:00
Jake Norbut 28fd04293b adding single-click delete/cancel functionality 2019-01-24 13:41:33 -06:00
Michael Harding 111767522f SPARK-1877: Clip Limits Test 2019-01-24 13:45:03 -05:00
Taha Afzal 429e16996a Merge branch 'SPARK-1939-HedgingSpeed' of bitbucket.org:bluetradesys/spark into SPARK-1939-HedgingSpeed 2019-01-24 13:35:29 -05:00
Taha Afzal 1ed85af40e spreader reports from the timer callback now 2019-01-24 13:35:23 -05:00
Kevin Darby f5457e5ce4 Merge branch 'master' of bitbucket.org:bluetradesys/spark into ml 2019-01-23 13:45:26 -05:00
Kevin Darby 7465a38022 Merged in sdk_build (pull request #146)
SPARK-1980 we need 7z on the jenkins server, not unzip

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
2019-01-23 18:09:24 +00:00
Kevin Darby 8afa03cc62 SPARK-1980 we need 7z on the jenkins server, not unzip 2019-01-23 12:56:59 -05:00
Jake Norbut 26dfd89d6c Properly Dispose of the OrderBookVM when the popup closes 2019-01-23 10:07:07 -06:00
Pedro Pinto fe54d5b973 Fixed double counting bug in Iceberg when using pauses. 2019-01-23 10:57:43 -05:00
Jake Norbut e7b9c3e228 adding a border around the popup window 2019-01-23 09:43:19 -06:00
Jake Norbut ae5db799d4 MiniOB will have parent-child rows expanded by default 2019-01-23 09:43:03 -06:00
Kevin Darby 5ba746fd59 small merge issue 2019-01-22 16:31:11 -05:00
Kevin Darby ab5cb23062 Merge branch 'algos2' of bitbucket.org:bluetradesys/spark into ml 2019-01-22 16:11:28 -05:00
Kevin Darby 4ebdc603c5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2019-01-22 16:10:38 -05:00
Pedro Pinto d573688064 Update release notes. 2019-01-22 15:26:00 -05:00
Pedro Pinto 14c601034a Merged with master 2019-01-22 15:14:22 -05:00
Jake Norbut 3de65fb027 Create a new OrderbookVM each time; fixes crash when launching mini-ob from same badge multiple times 2019-01-22 12:37:53 -06:00
Jake Norbut 9de0c4f530 Remove ContextMenu from Badge and setup new Popup 2019-01-22 12:35:18 -06:00
Jake Norbut ce5998c5dd Adding a Popup dep prop to the Badge. Note - I don't think Ineed AffectsRender here 2019-01-22 12:27:57 -06:00
Jake Norbut dcbc6e756e DIYGrid - adding dep prop to allow rows horizontal scrollbar to be strictly collapsed 2019-01-22 12:23:00 -06:00
Pedro Pinto 4955c1a792 Improved performance of ll to time_point conversion. 2019-01-22 13:00:03 -05:00
Pedro Pinto 2990710b88 Avoided delivering entire trade buffer on first sub. 2019-01-22 12:59:16 -05:00
Pedro Pinto 280ae81860 size() is probably slower than empty() 2019-01-22 12:57:47 -05:00
Taha Afzal 5e713d9008 reverting hard coded changig in accidental push 2019-01-21 09:47:35 -05:00
Taha Afzal 763c2fa41a Merge branch 'master' into SPARK-1939-HedgingSpeed 2019-01-20 12:34:28 -05:00
Taha Afzal 9d170e32f1 log level for cache warming set to NoLog 2019-01-18 23:02:00 -05:00
Taha Afzal b4410f7412 Passing trace to Hedger 2019-01-18 18:37:32 -05:00
Michael Harding b702ce9479 System test in broken state for debugging 2019-01-18 16:41:21 -05:00
Michael Harding abc7b1e2e9 Merge branch 'master' into SPARK-1877-system-tests 2019-01-18 12:39:47 -05:00
Eric Harding f71ff0e306 Add the ability to control affinity for the md tail tool 2019-01-18 11:52:42 -05:00
Jake Norbut 3c82327f1a in case my machine starts on fire 2019-01-17 15:59:55 -06:00
Jake Norbut f3ac92c434 PR edits; Eric has ESP 2019-01-17 15:52:56 -06:00
Jake Norbut a8694d6fb2 Because this is the bugfix the spark client deserves, but not the ones it needs right now 2019-01-17 15:21:25 -06:00
Pedro Pinto 52e6536018 Merge branch 'SPARK-1939-HedgingSpeed' of bitbucket.org:bluetradesys/spark into SPARK-1939-HedgingSpeed 2019-01-17 13:46:24 -05:00
Pedro Pinto 42274edbc4 Added extra logging level for no logging code paths. 2019-01-17 13:45:26 -05:00
Taha Afzal 6d438b4764 New spreader test for muliple quoting legs 2019-01-17 13:19:56 -05:00
Pedro Pinto 3688ec0739 Moved externals pointer 2019-01-17 11:46:19 -05:00
Pedro Pinto ce8d27bb71 Added time to ticker update. 2019-01-17 11:44:37 -05:00
Pedro Pinto ee66ddf9a6 Fixed a few bugs in the ticker api code. 2019-01-17 10:48:11 -05:00
Pedro Pinto bea0e5ced7 Added ticker events to order manager api 2019-01-16 17:57:02 -05:00
Pedro Pinto b2d1d11826 Added ticker events to MD Source 2019-01-16 17:56:40 -05:00
Taha Afzal d075f2ad0c removed some extra logging data from CacheWarmingState 2019-01-16 13:37:04 -05:00
Taha Afzal 409bbdc0f9 when 'in_cache_warming_cycle' quoter order updates are surpressed (they should have been surpressed logically as well, but this is just an extra check since the outcome can be nasty) 2019-01-16 11:37:58 -05:00
Taha Afzal d4fd3900be added config key to enable cache warming 2019-01-15 14:39:38 -05:00
Taha Afzal e45736a692 putting the fake fill log messages o lower logging level 2019-01-15 09:25:06 -05:00
Taha Afzal bd8336e22b inlined add_new_hedger 2019-01-14 16:58:07 -05:00
Eric Harding f3dedfc4e3 Merged in SPARK-1966-add-cme-ticker-trade-details (pull request #143)
RTM: SPARK-1966 add cme ticker trade details
2019-01-14 21:35:51 +00:00
Jake Norbut 326bcb4544 Merged in SPARK-1960 (pull request #140)
RTM: locking _resources ResourceDictionary prior to indexer access

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-01-14 21:35:17 +00:00
Pedro Pinto 884cdef97b Merged in improve_perf_log_latency (pull request #139)
RTM. Improve perf log performance.
2019-01-14 21:34:48 +00:00
Eric Harding 5e3467e3b6 Add release note 2019-01-14 14:41:47 -05:00
Eric Harding 979c6e078a Remove misleading comments 2019-01-14 14:39:05 -05:00
Eric Harding 69f38daad7 SPARK-1966 Add CME ticker trade details 2019-01-14 14:37:51 -05:00
Taha Afzal a64d428a89 added comments and inlined some smaller functions 2019-01-14 14:34:49 -05:00
Taha Afzal 78c89c6677 Merge branch 'master' into SPARK-1939-HedgingSpeed 2019-01-14 11:07:16 -05:00
Taha Afzal 8d0d678c01 unused parameters rmeoved form add_new_hedger 2019-01-14 09:58:13 -05:00
Taha Afzal 8b9df305b4 took the ChildOrder initilization out of the critical hedging path 2019-01-13 21:57:13 -05:00
Taha Afzal 02ee5cc6d3 moved orderbuilders (not used) in the hot path away from the hot memory 2019-01-11 18:45:24 -05:00
Taha Afzal 207c93c972 All the leg data is in one place now inside the SpreaderLegData (as opposed to ref's to SpreaderBookkeepingData) 2019-01-11 17:49:23 -05:00
Eric Harding 14012a16da Add "all" option to sparkd md tail for emumba testing 2019-01-11 17:29:51 -05:00
Kevin Darby f4b7fbe6b5 didn't save 2019-01-11 16:05:57 -05:00
Kevin Darby 9901ef54d4 working on execution 2019-01-11 16:05:37 -05:00
Kevin Darby 293c13e11d we can load tf models in spark orders. Now we need to be able to execute them 2019-01-11 14:42:56 -05:00
Taha Afzal dc264e1063 fixing buid 2019-01-11 08:37:49 -05:00
Kevin Darby be4c69fad4 libtf 2019-01-10 23:45:37 -05:00
Taha Afzal 2e883808b6 LegPropertyData does not hold leg orderDefinitions any more 2019-01-10 21:34:45 -05:00
Kevin Darby 2c4dc8fbe5 externals 2019-01-10 18:23:22 -05:00
Kevin Darby 4ea93bc386 just needs some rpath and I'm in business 2019-01-10 18:22:03 -05:00
Michael Harding 184a900eea SPARK-1877: Add Limits Test; Limits Test sets and restores limits 2019-01-10 14:51:06 -05:00
Pedro Pinto 3bf086c73a Merged in SPARK-1970-manual-order-indicator (pull request #142)
RTM. SPARK-1970 manual order indicator
2019-01-10 19:39:31 +00:00
Pedro Pinto 9838d123c7 Updated release notes. 2019-01-10 14:15:04 -05:00
Pedro Pinto 9d5b80c990 Forced manual order indicator to false on algos. 2019-01-10 14:12:27 -05:00
Taha Afzal 040fc3af96 removed the if _active_quoting check because that will always be the case 2019-01-10 09:47:45 -05:00
Kevin Darby 1c6333b07a now I have to figure out how to load the model 2019-01-09 18:15:04 -05:00
Taha Afzal cc07e28aca quoters, child orders and order builders are only created for quoting legs 2019-01-09 16:42:45 -05:00
Taha Afzal d2aec0c7f2 quoters are only created for quoting legs 2019-01-09 15:39:00 -05:00
Taha Afzal a71953b871 SPARK-1967 foundational work 2019-01-09 12:56:29 -05:00
Taha Afzal 70e962000f changed the ordering of member variables from lower mem to higher 2019-01-09 11:18:06 -05:00
Taha Afzal f413938943 broke h_p end log into ch_term and rep 2019-01-09 10:17:12 -05:00
Taha Afzal a7250b4734 SPARK-1959 _cache_warm_fill_ts is static now 2019-01-08 11:55:59 -05:00
Taha Afzal 060b88e4c9 SPARK-1959 largechunk done 2019-01-08 10:03:22 -05:00
Kevin Darby 26d543af82 long journey with externals 2019-01-08 09:57:53 -05:00
Kevin Darby 99b301ad6f statically linking tf with sparklib 2019-01-07 23:00:56 -05:00
Kevin Darby 9cbe83e1a4 keep tf out in the sdk land for a bit 2019-01-07 18:32:36 -05:00
Kevin Darby 59369e1b2b moving towards a tflow example 2019-01-07 17:54:39 -05:00
Kevin Darby 7c42e75ddd getting close 2019-01-04 18:14:53 -05:00
Jake Norbut ae200b374b PR suggestion 2019-01-04 12:38:56 -06:00
Jake Norbut 64b94c58e7 locking _resources ResourceDictionary prior to indexer access 2019-01-04 11:20:21 -06:00
Pedro Pinto 8e907d7395 Fixed perf_log.sql 2019-01-04 12:04:10 -05:00
Pedro Pinto c567283d3f More performance improvements on perf_log 2019-01-04 11:31:36 -05:00
Pedro Pinto 6b84dc433a Making log analysis faster. 2019-01-04 09:23:59 -05:00
Pedro Pinto a833944ec5 Improved perf log latency. 2019-01-03 19:07:18 -05:00
Kevin Darby b453be40fe strings... 2019-01-03 18:11:13 -05:00
Taha Afzal 97d16115e8 SPARK-1959 new order is constructed by the Hedger in init 2019-01-03 17:01:27 -05:00
Kevin Darby 96709c8121 ai algo sample 2019-01-03 16:20:15 -05:00
Taha Afzal 2e3957a7ac SPARK-1959 added get_new_hedger_order to ISpreaderManager 2019-01-03 15:18:53 -05:00
Taha Afzal b866157bff SPARK-1959 partially done 2019-01-03 14:24:33 -05:00
Michael Harding 8e9c7383b6 Merge branch 'master' into SPARK-1877-system-tests 2019-01-03 13:17:05 -05:00
Kevin Darby 5de9f63665 new proto36 2019-01-02 17:14:00 -05:00
John Colvin 930f3b23b8 Merged in SPARK-1903 (pull request #133)
RTM SPARK-1903 front end crash when sorting

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-01-02 19:28:18 +00:00
Pedro Pinto d3fb52246c Merged 2019-01-02 19:24:23 +00:00
Pedro Pinto e173fe5515 Update release notes. 2019-01-02 19:23:35 +00:00
Pedro Pinto dbe02e44fd Merged in SPARK-1935-orderstate.display_status-doe (pull request #136)
RTM. Propagated display status through order state update event.
2019-01-02 19:13:24 +00:00
Jake Norbut 7453540140 Merged in SPARK-1918 (pull request #135)
RTM. SPARK-1918
2019-01-02 19:12:50 +00:00
Eric Harding 9830dcbe31 Merged in immutablefix (pull request #134)
RTM: It is not always safe to convert Foo to FooImmutable. It is always safe to convert it to Nullabke<FooImmutable>

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2019-01-02 19:11:38 +00:00
Eric Harding 69bc734b6a Merged in ReplayOptions (pull request #137)
RTM: Update spark replay defaults
2019-01-02 19:11:08 +00:00
Pedro Pinto 2e69966d44 Merged in SPARK-1954-make-sim-track-the-external-m (pull request #138)
RTM. Fixes sim drift awy from MD source.
2019-01-02 19:07:23 +00:00
Eric Harding c2070b8e52 CR feedback - immutables 2019-01-02 13:40:57 -05:00
Eric Harding 64e2408b59 It is not always safe to convert Foo to FooImmutable. It is always safe to convert it to Nullabke<FooImmutable> 2019-01-02 13:40:57 -05:00
Michael Harding 17a8d47452 Spark-1877: Add TestType to TestGroupAttribute 2019-01-02 13:40:57 -05:00
Taha Afzal b4eabf4eb8 cleaning up logs, and indentation 2019-01-02 13:37:56 -05:00
Taha Afzal f12c1d5e74 SPARK-1946 2019-01-02 12:28:16 -05:00
Pedro Pinto 714dfd5038 Fixes sim drift awy from MD source. 2019-01-02 17:05:38 +00:00
Eric Harding 6dfe9b3892 Add CoconutSecurityStatus to md tail 2019-01-01 10:50:05 -05:00
Eric Harding 15e557a209 Update cmakedeps 2019-01-01 10:49:41 -05:00
Eric Harding 8075bafb86 Update spark replay defaults 2018-12-31 13:07:18 -05:00
Taha Afzal e2f4a86fcf added tests to isolate the first hedge condition 2018-12-31 13:05:27 -05:00
Kevin Darby 6089b209d1 we want caffe2 in c++ not libtorch 2018-12-28 23:09:30 -05:00
Kevin Darby d7a12d0dd9 checkpoint 2018-12-28 17:40:32 -05:00
Pedro Pinto 95ae7b780e Propagated display status through order state update event. 2018-12-27 21:35:18 +00:00
Taha Afzal 0644d301b2 merging from master 2018-12-27 13:24:48 -05:00
Jake Norbut 7e68dc4ff7 fixing tabs in notes 2018-12-26 13:58:03 -06:00
Jake Norbut 879963b828 updating release notes 2018-12-26 13:55:38 -06:00
Jake Norbut 5320e16088 commenting out anything having to do w/ window width and height since that interaction is still mysterious; fixing scale factor restortation 2018-12-26 11:02:07 -06:00
Taha Afzal 6116e24df0 added comments to hedger, and formatted 2018-12-25 23:50:26 -05:00
Taha Afzal 6ba1de8c5c changing order_key name 2018-12-25 10:31:35 -05:00
Taha Afzal 0d66054e8e dealing with the Hedger._key() for hedgers with no working orders 2018-12-24 15:11:40 -05:00
Taha Afzal daf39324fd merging 2018-12-24 12:45:02 -05:00
Taha Afzal ab9d3cede3 odified some test code 2018-12-24 09:14:22 -05:00
Kevin Darby 2f87fe31be mlpack 2018-12-21 18:25:19 -05:00
Jake Norbut 7429c42de2 checking this in b/c i've lost things too many times keeping stuff in the stash and switching branches; this is just really testing, no sln yet regarding window borders 2018-12-21 16:24:57 -06:00
Jake Norbut 9147b4605a adding zoom_factor to window_specifics 2018-12-21 16:21:34 -06:00
Kevin Darby 5a9749d551 testing this out in the sdk 2018-12-21 17:17:18 -05:00
Kevin Darby ea950c8786 pushed by mistake 2018-12-21 17:05:24 -05:00
Jake Norbut 6fc8a6c7fe Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-1918 2018-12-21 16:02:08 -06:00
Pedro Pinto b537c0c5bd Updated release notes. 2018-12-21 21:42:19 +00:00
Pedro Pinto 400ae269e3 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-12-21 21:34:23 +00:00
Pedro Pinto 30cd749d7c Tuned logging to produce simpler and more compact traces. 2018-12-21 21:31:27 +00:00
Kevin Darby 30891bfb49 need a little boost 2018-12-21 15:28:45 -05:00
Jake Norbut efbf45167c ICS CBT round 2 2018-12-21 13:34:11 -06:00
Jake Norbut 913b81b8b6 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-12-21 13:27:25 -06:00
Jake Norbut 606d37744f adding ICS spreads to cpp gen tools and updating defaults 2018-12-21 13:27:10 -06:00
John Colvin 8a4d3d4c97 SPARK-1903 CR - unit test for SortedTree.TryReplace, comments in
OrderBookRowCollection
2018-12-21 12:25:01 -06:00
Eric Harding 7cbf0dc683 CR feedback - immutables 2018-12-21 10:38:56 -05:00
Eric Harding e05ce79abf It is not always safe to convert Foo to FooImmutable. It is always safe to convert it to Nullabke<FooImmutable> 2018-12-20 20:30:10 -05:00
John Colvin 2ea95e7938 SPARK-1903 front end crash when sorting 2018-12-20 16:15:54 -06:00
Taha Afzal 641141810e Cleaned up HedgerPriceQuantity 2018-12-20 17:07:52 -05:00
Kevin Darby 34319c4536 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2018-12-20 16:10:58 -05:00
Pedro Pinto 65c4c23b22 Merged in SPARK-1937-perflog-generates-too-much-da (pull request #132)
Reduced the ammount of irrelevant perf log entries.

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-12-20 18:09:45 +00:00
Pedro Pinto 54dde449ce Updated release notes. 2018-12-20 18:07:56 +00:00
Pedro Pinto c206d38982 Fixed spelling typo. 2018-12-20 18:05:53 +00:00
Pedro Pinto 72a1bddbd2 Increase defaults for logging window. 2018-12-20 18:05:08 +00:00
Pedro Pinto 8ee63d7b8e Avoided logging a requote event on first spreader activation. 2018-12-20 18:04:23 +00:00
Pedro Pinto 73cd8d3f26 Used BTS_ARRAYSIZE per PR feedback. 2018-12-20 18:03:47 +00:00
Pedro Pinto 86a87c501c Fixed typo in script. 2018-12-20 17:03:03 +00:00
Pedro Pinto 4705368097 Reduced the ammount of irrelevant perf log entries.
This was necessary because in production the number of events logged
was in the order of 1/4 billion a day, which consumes too much disk
and makes analysis impossible. Most of these events were irrelevant,
so we now accumulate the most recent log entries in a circular buffer
which only gets flushed to disk when a fix message is sent to the
exchange.
2018-12-20 16:58:23 +00:00
Taha Afzal c3ac2957d2 added comments to SpreaderChildOrder and cleaned it up a bit 2018-12-20 10:05:07 -05:00
Kevin Darby 196c518ac6 remove tick order 2018-12-20 09:43:44 -05:00
Kevin Darby f04af5af9d fix build 2018-12-20 09:39:05 -05:00
Taha Afzal 950509e5d6 added new hedger behaviour (recycle) test 2018-12-19 18:58:18 -05:00
Kevin Darby 8f4da463a7 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2018-12-19 18:24:14 -05:00
Kevin Darby f3adf780d0 OCOOrder will be the basis of a StopLoss template
Added the beginnings of pricer order but need something from John
2018-12-19 18:15:04 -05:00
Taha Afzal 9002ca6183 some hedger interface cleanup, and tst code 2018-12-19 09:20:54 -05:00
Taha Afzal b36715a6a0 fixing build 2018-12-18 15:57:53 -05:00
Taha Afzal 2305c3af32 removed the vector access ot hedger 2018-12-18 15:42:39 -05:00
Eric Harding 034aaa5b98 SPARK-1934 - Settlement will fail to send to client when first request is not entitled 2018-12-18 14:30:46 -05:00
Pedro Pinto cbbd678112 Merged in SPARK-1916-tag-37-sent-twice (pull request #129)
RTM. Fixes tag 37 duplication in SPARK-1916
2018-12-18 17:53:34 +00:00
Pedro Pinto d62a2ea9d4 Fixes tag 37 duplication in SPARK-1916 2018-12-18 17:50:48 +00:00
Jake Norbut 6259f8b03f fixing build 2018-12-18 10:53:52 -06:00
Jake Norbut 3866e63a70 merging master and fixing 2018-12-18 10:53:23 -06:00
Jake Norbut 877e0192a1 Merged in SPARK-1913 (pull request #124)
RTM. Order Entry Panel polishing

Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-18 15:24:06 +00:00
Pedro Pinto 6d3b7305df Edited release notes. 2018-12-18 15:22:34 +00:00
Pedro Pinto aa975ea1c5 Merged 2018-12-18 10:20:30 -05:00
Jake Norbut 49445c6871 updating release notes 2018-12-18 09:15:46 -06:00
Pedro Pinto ef7c3c70da Merged in SPARK-1887-speed-up-order-manager (pull request #128)
RTM. SPARK-1887 speed up order manager
2018-12-18 15:07:21 +00:00
Pedro Pinto 404a193068 Merge branch 'SPARK-1887-speed-up-order-manager' of bitbucket.org:bluetradesys/spark into SPARK-1887-speed-up-order-manager 2018-12-18 15:04:44 +00:00
Pedro Pinto 42a59c9c92 Updated release notes. 2018-12-18 15:03:31 +00:00
Pedro Pinto 5078b74f72 Dev doc 2018-12-18 09:22:12 -05:00
Taha Afzal 809bd16e99 added the 0 leg check 2018-12-18 09:03:19 -05:00
Pedro Pinto 04446df8cc Merge 2018-12-18 13:56:41 +00:00
Taha Afzal 6a1a2d5f0a organizing around HegderManager 1 2018-12-18 07:12:33 -05:00
John Colvin d998d3ccbb SPARK-1933 LadderSettings workaround to avoid overwriting panel/column widths when the user saves new defaults 2018-12-17 18:54:48 -06:00
Kevin Darby 664c1f28ed Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2018-12-17 18:05:59 -05:00
Jake Norbut 62892f1fb9 PR - allowing the left panel to shrink just a bit further 2018-12-17 17:01:05 -06:00
Kevin Darby 8e75c80e02 Merged in wheel_login (pull request #120)
RTM - fix SPARK-1914

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-12-17 22:58:04 +00:00
Kevin Darby aae927d773 gui for new algos 2018-12-17 17:38:22 -05:00
Taha Afzal b90f6cb45c all spreader unit tests restored 2018-12-17 16:50:35 -05:00
Taha Afzal 1cc5a77d7f more tests enabled 2018-12-17 16:39:50 -05:00
Taha Afzal fb14a9c3af restoring tests 2018-12-17 16:17:08 -05:00
Jake Norbut 1a2a82eda3 PR suggestion - change price label text (short vs long) based on left panel width 2018-12-17 15:05:13 -06:00
Kevin Darby 00dba92636 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2018-12-17 15:36:32 -05:00
Kevin Darby 71147d038c Merge branch 'master' of bitbucket.org:bluetradesys/spark into wheel_login 2018-12-17 15:34:01 -05:00
Taha Afzal e82db39f8c merging from SPARK-1887-speed-up-order-manager 2018-12-17 14:45:54 -05:00
Pedro Pinto c3292d2f60 Merge branch 'master' into SPARK-1887-speed-up-order-manager 2018-12-17 19:10:38 +00:00
Eric Harding e749a24a77 Fix stream with qualifier broken by api_security merge 2018-12-17 14:06:27 -05:00
Kevin Darby 5a5de65292 OCO and Pricer Algos 2018-12-17 13:56:24 -05:00
Pedro Pinto b706c36edb Merged master 2018-12-17 13:35:11 -05:00
Jake Norbut 7823972bfd Fixing smushed check in checkbox 2018-12-17 11:09:19 -06:00
Jake Norbut f958ee19ad PR suggestion - Clear visibility 2018-12-17 11:05:28 -06:00
Taha Afzal dffb9c5320 guard around SpreaderChildOrder::order_key() to deal with in active state 2018-12-17 11:34:48 -05:00
Pedro Pinto 63cbd74744 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-12-17 16:00:20 +00:00
Pedro Pinto fc78475037 Updated release notes. 2018-12-17 15:59:49 +00:00
Pedro Pinto 3ef42a453a Merged master 2018-12-17 15:29:18 +00:00
Jake Norbut 469acd23a5 Merged in SPARK-1882 (pull request #119)
RTM - MV filters - reevaluate fitler when data changes

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-12-17 15:26:50 +00:00
Pedro Pinto d2a0efebd3 Merge branch 'master' into SPARK-1882 2018-12-17 15:13:38 +00:00
Eric Harding 5cde9d55a7 Merged in dev_process (pull request #123)
Verification.md
2018-12-17 15:10:53 +00:00
Eric Harding ddd08cb036 Merged in api_security (pull request #121)
RTM: Api security

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
2018-12-17 15:10:09 +00:00
Pedro Pinto 5bb064197f Updated release notes 2018-12-17 15:00:33 +00:00
Pedro Pinto 2417c6b57f Merge branch 'api_security' of bitbucket.org:bluetradesys/spark into api_security 2018-12-17 14:41:25 +00:00
Kevin Darby a1addee1b3 Merged in cleanup_mdclient (pull request #122)
RTM: Cleanup mdclient - based on api_security branch

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-12-17 14:38:27 +00:00
Taha Afzal 2e1bd765f6 fixing spreader tests 2018-12-17 09:23:50 -05:00
Pedro Pinto d108ff3151 Merge branch 'master' into api_security 2018-12-17 14:15:59 +00:00
Pedro Pinto 0f7cae6de7 Merged 2018-12-17 14:02:18 +00:00
Taha Afzal 2be4eb1c5c adding is_permenantly_cancelled to hedger 2018-12-16 22:15:39 -05:00
Pedro Pinto 1ff84f26d2 Perf logs. 2018-12-16 18:04:19 +00:00
Taha Afzal 175204efdf removed the init call from Hedger 2018-12-14 17:50:44 -05:00
Jake Norbut dbd8a7d742 Zoom - proto, settings, UI 2018-12-14 15:06:40 -06:00
Taha Afzal 8894aea1ad removed the hedge vector from SpreaderBookkeepingData 2018-12-14 13:42:05 -05:00
Kevin Darby e45123fd76 adding a couple nice auth accessors 2018-12-14 12:08:54 -05:00
Eric Harding 4924a873a2 Merged in emumba_fixes (pull request #126)
RTM: Some fixes for emumba
2018-12-14 17:08:23 +00:00
Jake Norbut f0ba6b72fc PR suggestions 2018-12-14 10:10:46 -06:00
Eric Harding 5e6a0799d2 Some fixes for emumba 2018-12-14 10:27:15 -05:00
Jake Norbut bea84c03f7 PR suggestions for the Thumb - null checks 2018-12-14 09:17:24 -06:00
Jake Norbut 869faa5c6c fix grid splitter vs ladder status message z order 2018-12-14 08:59:45 -06:00
Taha Afzal 43e82fb8c4 Added HedgerManager that keeps track of activ hedgers 2018-12-13 22:04:56 -05:00
Pedro Pinto 141c28ed78 Allow empty scoped log entry to facilate legacy interop. 2018-12-13 19:48:44 +00:00
Eric Harding 4d9bce82ab Add 'md channels' tool 2018-12-13 14:43:29 -05:00
Pedro Pinto 8428423ff9 Fixed perf_trace.sql ordering. 2018-12-13 19:02:47 +00:00
Pedro Pinto fa18cf5aba Merge branch 'SPARK-1887-speed-up-order-manager' of bitbucket.org:bluetradesys/spark into SPARK-1887-speed-up-order-manager 2018-12-13 18:33:08 +00:00
Pedro Pinto de11e00d89 Perf tools. 2018-12-13 18:32:55 +00:00
Taha Afzal 8feab27332 Merge branch 'SPARK-1887-speed-up-order-manager' of bitbucket.org:bluetradesys/spark into SPARK-1887-speed-up-order-manager 2018-12-13 12:12:24 -05:00
Taha Afzal 8c26ffd5d3 removing the _keep_hedging_path_hot call for now 2018-12-13 12:12:13 -05:00
Pedro Pinto dad7d030bc Perf tools 2018-12-13 07:10:16 -05:00
Pedro Pinto 7e7af8f606 Merge branch 'SPARK-1887-speed-up-order-manager' of bitbucket.org:bluetradesys/spark into SPARK-1887-speed-up-order-manager 2018-12-12 23:19:04 +00:00
Pedro Pinto d0647fb8d9 Perf tools. 2018-12-12 23:18:45 +00:00
Taha Afzal bb2564fb0a Merge branch 'SPARK-1887-speed-up-order-manager' of bitbucket.org:bluetradesys/spark into SPARK-1887-speed-up-order-manager 2018-12-12 17:55:24 -05:00
Taha Afzal 6b0e8b6600 speed improvements for hedging 2018-12-12 17:55:14 -05:00
Pedro Pinto 9e768b7a24 Merged, added more tools 2018-12-12 16:39:42 -05:00
Jake Norbut a3774c8d01 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-1913 2018-12-12 15:12:16 -06:00
Eric Harding 59a88f76a8 Verification.md 2018-12-12 13:04:11 -05:00
Jake Norbut 24d8929e88 Enforce MinWidth on Ladder cols so they can't be resized into oblivion 2018-12-12 10:53:34 -06:00
Jake Norbut dcaae15e28 SPARK-1917 - resizable order entry panel 2018-12-12 09:23:45 -06:00
Pedro Pinto a94d753fdc Added more perf logging statements. 2018-12-12 14:04:53 +00:00
Taha Afzal bbd95db97b added another test 2018-12-11 23:12:25 -05:00
Taha Afzal a048874734 merged from hedge_perf_calc 2018-12-11 17:26:25 -05:00
Jake Norbut 6b24745ae8 client with previously saved workspaces should be upgraded with all existing widgets *still* visible 2018-12-11 14:04:28 -06:00
Taha Afzal a981965fd9 _send_hedge reverted back to its original state, ie without if 2018-12-11 12:51:58 -05:00
Pedro Pinto c4910f011f More perf log improvements. 2018-12-11 17:48:34 +00:00
Kevin Darby 0c2593cb20 PR feedback 2018-12-11 12:20:41 -05:00
Jake Norbut 37b4657023 fix little square that clips over buttons when resizing ladder 2018-12-11 10:58:42 -06:00
Taha Afzal a608b5b99d added a timer local to the spreader to track intra-spreader perf 2018-12-11 11:32:12 -05:00
Jake Norbut b1fb6ed06a Ladder left panel now scrolls when vertical height is small (no scroll viewer visible, taco-style) 2018-12-11 10:08:12 -06:00
Jake Norbut c88db07474 don't shrink Settings button when resizing; fix xaml indentions that were driving me crazy 2018-12-10 17:10:01 -06:00
Kevin Darby dbea159fa9 spark.exe, spark_sdk.exe, excel all get market data and can send orders 2018-12-10 16:22:50 -05:00
Jake Norbut b33b671342 Remove 'spacing' rows from Grid so that non-adjacent rows will align properly when neighboring rows are collapsed 2018-12-10 15:21:48 -06:00
Jake Norbut 70570164c7 show/hide tab, protos and settings 2018-12-10 14:58:21 -06:00
Kevin Darby 9338328b1f Merge branch 'api_security' of bitbucket.org:bluetradesys/spark into cleanup_mdclient 2018-12-10 12:42:17 -05:00
Kevin Darby 70db5ceb10 added spark_md_last to excel 2018-12-10 12:19:16 -05:00
Kevin Darby e07d602cf5 client MD refactor 2018-12-10 12:13:11 -05:00
Pedro Pinto 077505509a Improved perf logging. 2018-12-09 19:14:53 +00:00
Taha Afzal fd85f0507c added _keep_hedging_path_hot to keep calling compute hedge on book update 2018-12-08 10:54:28 -05:00
Kevin Darby 24f0b48f9d revamp MarketDataClient to use same MD as shell 2018-12-07 18:13:57 -05:00
Pedro Pinto 30ed5b296e Fixed reentrancy issues with new direct order interaction. 2018-12-07 22:52:48 +00:00
Taha Afzal b7a620d30e added logs and new test 2018-12-07 16:07:13 -05:00
Eric Harding dcee2930f5 Fix F# warnings 2018-12-07 15:47:43 -05:00
Eric Harding 1b2aeee069 Update AdminServiceServer to use AuthenticatedAdminServiceBase 2018-12-07 15:33:28 -05:00
Eric Harding c4393637e7 Update ApiGateway to copy input token 2018-12-07 15:33:12 -05:00
Eric Harding 04dadc46d5 ServiceContext.token needs more atomic 2018-12-07 14:38:22 -05:00
Pedro Pinto b64cd3b031 Merge branch 'SPARK-1887-speed-up-order-manager' of bitbucket.org:bluetradesys/spark into SPARK-1887-speed-up-order-manager 2018-12-07 19:17:01 +00:00
Pedro Pinto 5e1c4143db Improved speed and robustness of perf log. 2018-12-07 19:16:41 +00:00
Eric Harding ba4b6aa1ff Update QueryServer to use AuthenticatedMarketDataRelayBase 2018-12-07 13:39:58 -05:00
Eric Harding ad8e803a8c SPARK-1901 C++ AuthToken* is unwieldy. Replace with empty token 2018-12-07 13:20:31 -05:00
Eric Harding 601eec3ddb Convert shell to log into the context 2018-12-07 11:44:43 -05:00
Eric Harding 38edcb27f1 Token must not be treated as null terminated string 2018-12-07 11:44:34 -05:00
Eric Harding e1b638a4b6 ServiceContext.login() needs to return more information 2018-12-07 08:55:35 -05:00
Kevin Darby b80fe1e748 moved admin model down for some changes upstream 2018-12-06 15:07:24 -05:00
Kevin Darby 0419d084f4 moved co2 stuff down to spark_api to refactor or remove MarketDataClient.cs 2018-12-06 15:01:40 -05:00
Eric Harding f26faa5676 merge from master 2018-12-06 14:54:36 -05:00
Eric Harding 09d4d1f907 SPARK-1901 ServiceContext.login( existing token). 2018-12-06 14:09:50 -05:00
Jake Norbut bdbf7e53e8 more PR edits 2018-12-06 12:48:32 -06:00
Eric Harding 9c5ddba7f7 SPARK-1901 'real' aes 2018-12-06 12:07:16 -05:00
Michael Harding 91dab2c841 Add timeout to perf trace ui 2018-12-06 10:22:53 -05:00
Pedro Pinto 8cfca8e40e Corrected channel in correlation id. 2018-12-05 23:31:26 +00:00
Pedro Pinto 9cb619d374 Merge branch 'SPARK-1887-speed-up-order-manager' of bitbucket.org:bluetradesys/spark into SPARK-1887-speed-up-order-manager 2018-12-05 22:11:03 +00:00
Jake Norbut fba8233c63 Removing PnLPriceCell which is no longer used after previous PR edits 2018-12-05 16:09:53 -06:00
Jake Norbut 631158ebbc PR suggestions 2018-12-05 16:07:44 -06:00
Pedro Pinto 5f12e2f1b2 Retrofied new perf log to stats web page. 2018-12-05 22:05:57 +00:00
Pedro Pinto 807f8b5876 Retrofied new perf log to stats web page. 2018-12-05 22:03:39 +00:00
Pedro Pinto e6a9c7e433 Merged. 2018-12-05 21:52:49 +00:00
Pedro Pinto 096787d710 Merge 2018-12-05 16:50:48 -05:00
Eric Harding 0468fe4f4d Add AuthToken unit tests 2018-12-05 15:37:40 -05:00
Eric Harding 69b75c5f5e SPARK-1901 Protobuf doesn't want null here 2018-12-05 14:08:28 -05:00
Eric Harding 5626614e2d SPARK-1901 Delegates don't do anything if they aren't used 2018-12-05 13:54:21 -05:00
Eric Harding 67d0a0351a SPARK-1901 Fix race with server startup in c# api client 2018-12-05 13:36:20 -05:00
Eric Harding 97925a82d4 SPARK-1906 - C++ client 2018-12-05 13:35:57 -05:00
Eric Harding 0bdd2dfca0 Api: centralize formatting stream topic+qualifier 2018-12-05 11:33:13 -05:00
Eric Harding a0c572ef2e Remove generated call to format_qualifier 2018-12-05 11:08:25 -05:00
Eric Harding b5c24880a1 SPARK-1901 - Send qualifier to stream access check 2018-12-05 10:51:37 -05:00
Pedro Pinto 31c8a50200 Merged from master 2018-12-05 14:11:40 +00:00
Pedro Pinto 0219dd2013 First cut at replacing log calls with new perf log. 2018-12-05 14:07:05 +00:00
Kevin Darby 91cb215b09 fix SPARK-1914 2018-12-04 18:11:34 -05:00
Pedro Pinto 4446ef0f57 Recoverd perflog 2018-12-04 16:52:22 -05:00
Pedro Pinto 41d662b3ba Improving trace logs 2018-12-04 16:30:06 -05:00
Jake Norbut 05d0eee8c7 MV filters - reevaluate fitler when data changes 2018-12-04 14:45:08 -06:00
Pedro Pinto 93abf315a3 Added dev process doc. 2018-12-04 20:01:25 +00:00
Kevin Darby 0d706efb10 Merged in sdk_doc (pull request #114)
Sdk doc - Ready to merge

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 22:52:35 +00:00
Pedro Pinto 73fe142904 Merged from master 2018-12-03 17:51:04 -05:00
Pedro Pinto 8668c54784 Updated release notes 2018-12-03 17:49:44 -05:00
Kevin Darby f29e04751d Merged in SPARK_1909 (pull request #118)
added to release_notes, SPARK-1909
2018-12-03 22:43:39 +00:00
Pedro Pinto f9f64c23c1 Merge branch 'sdk_doc' of bitbucket.org:bluetradesys/spark into sdk_doc 2018-12-03 22:37:39 +00:00
Pedro Pinto d3e9f15e99 --no-undefined is a link flag, not a compilation flag.
this was breaking the clang build.
2018-12-03 22:36:29 +00:00
Eric Harding 112a5703e0 SPARK-1906 Stream Auth : C++ Server 2018-12-03 17:35:01 -05:00
Kevin Darby 6dba376e8b PR feedback 2018-12-03 17:21:46 -05:00
Kevin Darby 99b380a9f3 added to release_notes, SPARK-1909 2018-12-03 16:59:10 -05:00
Pedro Pinto 175061a81e Merged 2018-12-03 16:44:47 -05:00
Eric Harding 3daad4f7e7 Merged in SPARK-1512 (pull request #113)
SPARK-1512 Allow the same name for a new spread when previous one was deleted

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 21:39:37 +00:00
Kevin Darby 8bcf2fde87 Merge branch 'master' of bitbucket.org:bluetradesys/spark into sdk_doc 2018-12-03 16:05:42 -05:00
Pedro Pinto a5d20157f0 Merged 2018-12-03 15:51:07 -05:00
Kevin Darby d3deac46d4 Merged in fix_mics (pull request #98)
Fix mics - Ready to Merge

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-12-03 20:45:57 +00:00
Eric Harding 288e8a364a Merged in no_mono (pull request #116)
No mono - ready to merge

Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2018-12-03 20:08:17 +00:00
Eric Harding 61c6d0513c Merged in SPARK-1907 (pull request #117)
SPARK-1907 fix crash on server start due to failure to check Result success
2018-12-03 19:48:12 +00:00
Eric Harding b0f2b8e906 SPARK-1907 fix crash on server start due to failure to check Result success 2018-12-03 14:22:27 -05:00
Pedro Pinto c194e76e46 Merge branch 'fix_mics' of bitbucket.org:bluetradesys/spark into fix_mics 2018-12-03 18:58:28 +00:00
Eric Harding e7f8cfaec1 Merged master into no_mono 2018-12-03 13:53:35 -05:00
Kevin Darby 0aa4f80752 add mics deployment notes to ops_notes 2018-12-03 11:26:59 -05:00
Pedro Pinto b8eecfd853 Merge branch 'master' into fix_mics 2018-12-03 15:50:39 +00:00
Jake Norbut 2960ff0996 Merged in jetfix_tools (pull request #110)
iLink purge - removing purged MBX session; adding output for CGW config prints
2018-12-03 15:42:43 +00:00
Michael Harding a071b62560 Merged in Documentation (pull request #109)
Documentation

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 15:41:45 +00:00
Pedro Pinto 95004adbad Cleaned up release notes 2018-12-03 15:39:56 +00:00
Pedro Pinto f28334f551 Merge branch 'master' into Documentation 2018-12-03 15:39:03 +00:00
Eric Harding 718a12200b Merged in SPARK-954 (pull request #111)
SPARK-954

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 15:36:10 +00:00
Pedro Pinto d6dab59254 Updated release notes 2018-12-03 15:25:13 +00:00
Pedro Pinto 9e6bdf74f6 Merge branch 'master' into SPARK-954 2018-12-03 15:14:48 +00:00
Pedro Pinto 030a8d5614 Updated release notes 2018-12-03 15:05:35 +00:00
Pedro Pinto 39946baf94 Merged master 2018-12-03 09:57:51 -05:00
Jake Norbut 19be7d7740 Merged in SPARK-1880 (pull request #115)
Security order in synthetic creation follows grid sort

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-12-03 14:49:33 +00:00
Pedro Pinto 37856b9768 Fixed release notes merge 2018-12-03 09:48:22 -05:00
Pedro Pinto dd2067802f Merged master 2018-12-03 09:47:42 -05:00
Eric Harding de6c53a765 Merged in SPARK-1902 (pull request #112)
SPARK-1902 OrderRecordTupleSource Iterator invalidated

Approved-by: John Colvin <john.colvin@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-12-03 14:41:46 +00:00
Pedro Pinto b2a20b47ff Updated release notes. 2018-12-03 14:37:20 +00:00
Jake Norbut be16bc2a27 PR - adding 1.4.2 release notes section and documenting this under bug fixes 2018-12-03 08:23:13 -06:00
Eric Harding 71c66f127d What about this? 2018-12-03 08:46:53 -05:00
Kevin Darby 4987fc9fc1 this? 2018-12-02 21:20:00 -05:00
Kevin Darby 16a4c315a2 PR comments 2018-12-02 11:05:21 -05:00
Eric Harding 71619c2e15 web build script should check for npx and paket at start 2018-12-01 22:35:58 -05:00
Eric Harding 5392b706df Remove web dependency on mono 2018-12-01 12:33:55 -05:00
Kevin Darby cbda28c8b7 save some build time, only generate package on release build 2018-11-30 18:37:11 -05:00
Kevin Darby 3956455e81 readme typo 2018-11-30 17:42:40 -05:00
Eric Harding 955b9d1fec SPARK-1901 Additional expunging of structured qualifier 2018-11-30 17:32:54 -05:00
Eric Harding d652646e93 SPARK-1906 Stream Auth : C# Server 2018-11-30 17:22:42 -05:00
Kevin Darby 76eb541878 Eric removed the dependency on defaults, I'm cleaning up all the places we try to copy them 2018-11-30 17:21:50 -05:00
Eric Harding 86199755f5 SPARK-1906 Stream Auth : C# Client 2018-11-30 17:05:37 -05:00
Kevin Darby d17c56b799 Merge branch 'static_defaults' of bitbucket.org:bluetradesys/spark into sdk_doc 2018-11-30 16:29:04 -05:00
Eric Harding e3795f9498 This file is obsolete and producing warnings 2018-11-30 16:14:40 -05:00
Kevin Darby 8da2569a27 permanent fix for bts_trace-- the definition is in sparklib now, not in sparkd, market_datapublisher_example, etc
this setup will allow us to run .net from linux if we copy the libspark.so to the right place
2018-11-30 16:14:05 -05:00
Eric Harding d68156f214 Remove api stream "structured qualifiers". Nobody uses this feature and it is very fragile 2018-11-30 15:56:49 -05:00
Eric Harding a598188c14 Load defaults from program segment instead of files 2018-11-30 15:21:43 -05:00
Eric Harding 3648f18a09 Generate an include file with the defaults text as as a string 2018-11-30 15:05:23 -05:00
Eric Harding 895bf3be6a Add AuthenticatedServiceBase class C# 2018-11-30 14:08:44 -05:00
Eric Harding 860132469f Add AuthenticatedServiceBase base class to opt in to service authentication 2018-11-30 13:37:37 -05:00
Eric Harding e1568c1e67 SPARK-1905 Attach token to api request header (c++) 2018-11-30 11:24:58 -05:00
Eric Harding f4ee9434cc Rename Result.result to Result.value() to match c# 2018-11-30 11:24:03 -05:00
Eric Harding 3bbef9f328 SPARK-1905 Attach token to api header C# 2018-11-30 11:22:31 -05:00
Kevin Darby 736e051225 works on linux, let's see if this is sustainable 2018-11-30 10:59:11 -05:00
Kevin Darby e007958afd testing linux 2018-11-30 10:08:48 -05:00
Jake Norbut 1682513af1 PR edits, Dicts intsead of HashSets 2018-11-30 09:02:39 -06:00
Pedro Pinto d3043e9a19 Internalized ExecutionGateway inside OrderManager. 2018-11-30 01:55:27 +00:00
Kevin Darby cc7e66aa61 looks like excel will work in the sdk now 2018-11-29 16:46:42 -05:00
Eric Harding c6b494db16 Move AuthToken type to apicore 2018-11-29 16:34:33 -05:00
Jake Norbut cea153df5a Security order in synthetic creation follows grid sort 2018-11-29 14:36:14 -06:00
Pedro Pinto e8f797739c Internalized inter order communication in OrderManager.
For speed and control over event priority.
2018-11-29 20:29:49 +00:00
Kevin Darby 9903b340a6 added brian's excel 2018-11-29 15:05:55 -05:00
Eric Harding fc5e4039b2 SPARK-1907 .value() on failed result should be null instead of an exception that nobody will remember to catch 2018-11-29 14:59:43 -05:00
Eric Harding d6c1285ada SPARK-1901 Rename UserService->AuthService 2018-11-29 14:55:09 -05:00
Kevin Darby f07b272cff client side stuff works on .netcore2 2018-11-29 12:50:45 -05:00
Kevin Darby 9d74a9d114 sever side stuff updated with group feedback 2018-11-29 11:39:15 -05:00
Kevin Darby e8f9bb8226 slight change to windows batch 2018-11-29 09:38:07 -05:00
Eric Harding 033c1ec5de Add timestamp option to servicetail 2018-11-29 09:37:55 -05:00
Kevin Darby 7fd2c075a1 added excel to client side sdk 2018-11-28 23:24:50 -05:00
Kevin Darby 125773b840 client side sdk to gh 2018-11-28 18:33:10 -05:00
Eric Harding 4cb743004b Move service tailer to it's own top level command 2018-11-28 18:00:55 -05:00
Eric Harding 9dbc9df2a4 Add "sparkcmd service --tail=ServiceName.stream" to watch service updates in the console 2018-11-28 17:12:30 -05:00
Kevin Darby 7c1016d940 script to set up client side sdk 2018-11-28 14:26:55 -05:00
Pedro Pinto a5c0c95e4e Reduced allocations when creating a new order. 2018-11-28 18:32:15 +00:00
Eric Harding 43198f9602 SPARK-1512 Allow the same name for a new spread when previous one was deleted 2018-11-28 10:24:15 -05:00
Eric Harding 8b1a195121 SPARK-1512 Fix iterator invalidation bug in MDSource 2018-11-28 10:23:30 -05:00
Pedro Pinto 308c5e5579 Added some experimental stuff. 2018-11-28 14:21:50 +00:00
Eric Harding 1cde234be4 SPARK-1902 OrderRecordTupleSource Iterator invalidated 2018-11-28 09:00:08 -05:00
Kevin Darby bba6da76fb straighten up readme and github scripts 2018-11-27 17:31:16 -05:00
Kevin Darby 0f68a7f56b added a little doc and some scripts to help the sdk 2018-11-27 15:48:55 -05:00
Brian Muhr a4963f8bd9 Spreader.md edited online with Bitbucket 2018-11-27 20:35:51 +00:00
Eric Harding 58a762a18a merge from master 2018-11-27 14:08:51 -05:00
Eric Harding 90c13d526a Additional synthetic tests 2018-11-27 14:03:37 -05:00
Eric Harding 743d8e0e81 Add round_to_quote_price for IntPrice 2018-11-27 13:44:18 -05:00
Kevin Darby eb7a79488f starting sdk docs 2018-11-26 18:22:35 -05:00
Jake Norbut 3dd7b9638b HMAC update after iLink purge and new CGW ilink 2018-11-26 15:23:39 -06:00
Jake Norbut 771d4976e9 iLink purge - removing purged MBX session; adding output for CGW config prints 2018-11-26 15:11:01 -06:00
Eric Harding f0a08c6f83 Rename some functions for easier profiling 2018-11-26 13:43:11 -05:00
Kevin Darby c7097faba2 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-26 13:42:41 -05:00
Michael Harding e9a341a013 Pass manual through a grammer checker 2018-11-26 12:57:08 -05:00
Michael Harding 15839ba75d Add videos to manual 2018-11-26 10:55:48 -05:00
Eric Harding cd0b24c16a Add 'allblank' synthetic merge test 2018-11-26 09:44:46 -05:00
Pedro Pinto 67b5aa7f15 Merge 2018-11-25 11:43:21 -05:00
Pedro Pinto 70bcf3ff0d Refactored OM to allow speed up. 2018-11-25 16:37:28 +00:00
Pedro Pinto 5dfca66665 Refactored OM to allow speed up. 2018-11-24 21:53:22 +00:00
Pedro Pinto 5e1fd41935 Ran clang-format. 2018-11-24 20:26:39 +00:00
Pedro Pinto ef00559f1e Removed unused OrderManager codes. 2018-11-24 18:43:06 +00:00
Pedro Pinto 7151619645 Dev docs. 2018-11-24 16:35:39 +00:00
Pedro Pinto 684c4037b6 Fixed clang build. 2018-11-24 16:34:41 +00:00
Eric Harding 97a50faf16 Switch from precise to pessimistic(tt) building of synthetic books 2018-11-23 16:31:04 -05:00
Eric Harding ca2a6a1e60 SPARK-954 Update synthetic book merge to floating point sizes 2018-11-23 11:29:35 -05:00
Eric Harding 61a112c221 Add a short sleep to watch_tests 2018-11-21 14:11:04 -05:00
Eric Harding 8e079a970f Update watch_tests.sh to not try to build. Just run when sparkd changes 2018-11-20 19:31:57 -05:00
Eric Harding a4bc22fc3b Add std=c++1z to sdk build script 2018-11-20 17:19:03 -05:00
Kevin Darby 0f81ce1716 hopefully last fix for fix_mics
Enforce at least 1 account selection on User Creation. One can currently create a User and not assign any Account Permissions. If you do this, you as the creator of said user will not see the user in your UI.

Also if a user can't add users he can't clone or remove them either
2018-11-20 17:16:50 -05:00
Eric Harding 97deb63deb Merged in simplify_md_sdk (pull request #102)
Simplify md sdk

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-11-20 21:56:17 +00:00
Eric Harding a3b91a4363 Should have included exchange key in Shmdump 2018-11-20 16:55:55 -05:00
Eric Harding 2551a0f11e Code review feedback 2018-11-20 16:19:11 -05:00
Eric Harding 2f5b06b628 merge from master 2018-11-20 16:08:19 -05:00
Pedro Pinto 575195c67a Updated release notes. 2018-11-20 20:51:52 +00:00
Jake Norbut 9c20fe892e Merged in tabs (pull request #106)
Tabs

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-11-20 20:37:09 +00:00
Taha Afzal 4b626a160d Merged in SPARK-1879_risk_check (pull request #104)
SPARK-1879 risk check

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-11-20 19:35:01 +00:00
Pedro Pinto bfef852de7 Merged in trim_account_name_fix (pull request #105)
Trimmed account name from fill record.
2018-11-20 19:16:57 +00:00
Jake Norbut dbb859cce9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into tabs 2018-11-20 13:05:51 -06:00
Michael Harding abb451349c Merged in perf_trace_unique_sessions (pull request #108)
Fix unique sessions filter
2018-11-20 18:12:25 +00:00
Michael Harding e77ecaa48c Fix unique sessions filter 2018-11-20 13:08:51 -05:00
Eric Harding 1895a75bc7 Merged in journal_mode_off_is_stupid (pull request #107)
Switch journal mode to WAL instead of OFF because off is stupid
2018-11-19 22:19:36 +00:00
Eric Harding 0da184ab73 SPARK-954 add unit test 2018-11-19 17:19:22 -05:00
Eric Harding f9357d0860 Switch journal mode to WAL instead of OFF because off is stupid 2018-11-19 17:05:37 -05:00
Jake Norbut f7bd209efa Deleting the right 0-sized file this time; still no idea how this was created 2018-11-19 14:41:27 -06:00
Jake Norbut f794ffdace Git thought this file was 0 sized but it's not; i'm confused 2018-11-19 14:27:44 -06:00
Jake Norbut 504323d5b7 I don't know how this empty file was added 2018-11-19 14:18:58 -06:00
Jake Norbut 9450a952d4 CMakeDeps.cmake, of course 2018-11-19 13:55:12 -06:00
Jake Norbut 62394d0e71 Merge branch 'master' of bitbucket.org:bluetradesys/spark into tabs 2018-11-19 13:51:38 -06:00
Jake Norbut 8d9697d06d SPARK-1804 2018-11-19 13:46:05 -06:00
Kevin Darby d4744d300b jakes #7 2018-11-19 14:19:00 -05:00
Kevin Darby 9b7275e713 let's see if I can get this by Jake 2018-11-19 14:11:28 -05:00
Kevin Darby 51ed3a4c2c some of jake's feedback 2018-11-19 12:02:02 -05:00
Taha Afzal d9e967610e static casting uint to int before passing it to a function that takes in ints 2018-11-19 10:44:03 -05:00
Pedro Pinto 2cfbd655cb Trimmed account name from fill record.
At some point in the past this was saved with spaces to the side of
it, and that messes up matching with the account names. The implicit
assumption is that we will never want account names with spaces at the
end.
2018-11-19 15:43:44 +00:00
Taha Afzal 3ed5e5275e Merge branch 'master' into SPARK-1879_risk_check 2018-11-19 10:22:45 -05:00
Pedro Pinto d997aeca1e Merged in QBConnection_fix (pull request #103)
Fixed bug in quickbus handling of broken connections.

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-11-19 14:12:04 +00:00
Pedro Pinto 3b371f3588 Fixed bug in quickbus handling of broken connections. 2018-11-19 13:54:48 +00:00
Kevin Darby 256b296521 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-16 23:14:43 -05:00
Eric Harding 38668f8de1 Code review feedback 2018-11-16 13:09:05 -05:00
Pedro Pinto b68aaa0a00 Merged 2018-11-16 11:50:18 -05:00
Pedro Pinto 36e58378e4 Made DynamicIcebergOrder only available in dev. 2018-11-16 16:40:13 +00:00
Pedro Pinto 0ef2378dc9 Updated release notes. 2018-11-16 16:40:01 +00:00
Michael Harding d1d4a1bfb7 Update release notes 2018-11-16 11:38:55 -05:00
Eric Harding e22649c19d Merge branch 'master' of bitbucket.org:bluetradesys/spark into simplify_md_sdk 2018-11-16 11:23:51 -05:00
Eric Harding 25f749bb0c Simplify md security key generation for spreads. 2018-11-16 11:23:01 -05:00
Pedro Pinto e45ee1730e Merged SPARK-1799-add-hedge-perf-traces into master 2018-11-16 11:00:11 -05:00
Pedro Pinto 6bd3c6b0ca Merge branch 'SPARK-1799-add-hedge-perf-traces' of bitbucket.org:bluetradesys/spark into SPARK-1799-add-hedge-perf-traces 2018-11-16 15:56:02 +00:00
Michael Harding 6f65a958ae Only show total for requotes in perf_trace UI 2018-11-16 10:25:25 -05:00
Pedro Pinto 53a4ab4974 Merge branch 'SPARK-1799-add-hedge-perf-traces' of bitbucket.org:bluetradesys/spark into SPARK-1799-add-hedge-perf-traces 2018-11-16 14:23:25 +00:00
Taha Afzal 6e4ed3f328 for the first time at start up, net pos is enforced 2018-11-15 17:25:10 -05:00
Taha Afzal 4a292c8030 limit enforcing logic added 2018-11-15 17:25:10 -05:00
Michael Harding 29fbfd4479 SPARK-1877: product not security 2018-11-15 16:35:14 -05:00
Michael Harding 500e5d508a SPARK-1879: riskLimitEnforcement radio buttons 2018-11-15 14:51:09 -05:00
Taha Afzal 396ac023c4 adding field to proto 2018-11-15 13:33:41 -05:00
Michael Harding e32f40caee Fix session handling in perf_trace ui 2018-11-15 10:34:56 -05:00
Eric Harding 4b3cdf9ca4 Add tests for SecurityIDRecorder += SpreadResolver 2018-11-14 19:58:31 -05:00
Eric Harding 2fc4c8bd60 Move sqlite_utils.h out of utils:: namespace 2018-11-14 19:13:23 -05:00
Pedro Pinto 47e7d69897 Merge branch 'SPARK-1799-add-hedge-perf-traces' of bitbucket.org:bluetradesys/spark into SPARK-1799-add-hedge-perf-traces 2018-11-14 22:06:54 +00:00
Pedro Pinto 4f830cdd40 Removed obsolete stats. 2018-11-14 22:06:39 +00:00
Michael Harding 00e9de3b1e Hedges only shows total 2018-11-14 17:03:31 -05:00
Pedro Pinto ddb4e55d13 Changed public to protected member access 2018-11-14 21:36:31 +00:00
Kevin Darby 7e0d79ac68 Merge branch 'master' of https://bitbucket.org/bluetradesys/spark into fix_mics 2018-11-14 15:17:59 -06:00
Pedro Pinto c2dfcbe343 Made sql store a bit more tolerant to errors. 2018-11-14 20:56:41 +00:00
Pedro Pinto 582fffb1f4 Merged master 2018-11-14 11:02:56 -05:00
Pedro Pinto e67756d055 Merge branch 'prod' 2018-11-14 15:58:58 +00:00
Kevin Darby 42b73006a8 AdminReport.cpp edited online with Bitbucket 2018-11-13 23:06:47 +00:00
Kevin Darby 2fc85d6810 AdminReport.cpp edited online with Bitbucket 2018-11-13 22:55:17 +00:00
Pedro Pinto 141e1a4ea0 Fixed build. 2018-11-13 22:41:54 +00:00
Pedro Pinto 097db2f340 Merged master 2018-11-13 17:17:18 -05:00
Pedro Pinto b49c764c54 Sped up hedging. 2018-11-13 22:15:30 +00:00
Michael Harding 385387b739 Merge master into fix_mics 2018-11-13 16:11:04 -05:00
Eric Harding 87218962e1 Update md publisher SDK sample - FixedSpreadSecurity 2018-11-13 15:47:39 -05:00
Eric Harding 88d8248f63 Merged in SPARK-1798-md-benchmark (pull request #101)
SPARK-1798 md benchmark
2018-11-13 20:30:14 +00:00
Eric Harding ffbda10200 Fix capture of transient 'this' pointer 2018-11-13 14:14:42 -05:00
Eric Harding 7eb8b21cea Simplify SecurityIDRecorder api for 3rd party consumption 2018-11-12 16:58:06 -05:00
Michael Harding 986a791801 Update Release Notes 2018-11-12 14:29:35 -05:00
Eric Harding c834a6e840 Switch tbb::queueing_mutex to std::mutex 2018-11-12 13:57:23 -05:00
Michael Harding f14c36223f Merged in SPARK-1703-algo-tooltips (pull request #99)
SPARK-1703: Add tool-tips for lean, join, cover, top

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-11-12 14:32:37 +00:00
Taha Afzal d66c8e0ad8 Added a line to Conditional Quoting help tooltips 2018-11-10 17:34:55 -05:00
Taha Afzal 8673f366d7 conditional quotes are not pulled until after 5 secs of the condition 2018-11-09 18:02:58 -05:00
Michael Harding 88e48ebcf7 SPARK-1703: Add tool-tips for lean, join, cover, top 2018-11-09 17:31:03 -05:00
Kevin Darby ea3eacbbae async blows up brian's laptop, adding it as an option 2018-11-09 15:40:26 -05:00
Kevin Darby 9f3941d1a1 favor the async excel interface for our api calls 2018-11-09 13:39:06 -05:00
Eric Harding 1a6a10a834 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-1798-md-benchmark 2018-11-09 13:09:58 -05:00
Eric Harding d780e2aab2 SPARK-1798 sqlite import does not want spaces after comma in csv 2018-11-09 12:29:15 -05:00
Eric Harding a4bc34e633 SPARK-1798 Add CSV output option (default) 2018-11-09 12:15:19 -05:00
Kevin Darby 9c8bb7efeb excel is in with the mics 2018-11-09 12:12:31 -05:00
Kevin Darby d9b37efbbd wrong enum 2018-11-09 10:42:51 -05:00
Taha Afzal d23b7942b6 removing the hard coded n_ticks used for testing 2018-11-08 19:53:25 -05:00
Kevin Darby f41c3a5334 ConditionalQuoteEnabledModel.cs edited online with Bitbucket 2018-11-09 00:08:29 +00:00
Kevin Darby d8e8f90131 something is smishing this UI, but I've asked John for help 2018-11-08 18:32:36 -05:00
Taha Afzal 0358afccf9 simplified the n ticks away logic 2018-11-08 17:49:05 -05:00
Michael Harding 9f58327d0a Merged in new-order-cancel (pull request #97)
SPARK-1678 Order Cancel in the Spark Admin

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-11-08 22:16:32 +00:00
Taha Afzal d3ed6e21e4 integrated with SingleLegquoter._compute_price_quantity 2018-11-08 14:45:28 -05:00
Kevin Darby a171abb741 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-08 13:39:22 -05:00
Kevin Darby 52825e0e59 sparkd adminreport md 2018-11-08 13:38:42 -05:00
Eric Harding de83f19388 Adjust column widths 2018-11-08 13:33:23 -05:00
Eric Harding 4d993f2ef7 SPARK-1798 - Tool to benchmark market data by dumping to stdout
periodically
2018-11-08 13:19:05 -05:00
Michael Harding ca81be3865 Add manual test for canceling orders in the Spark Admin 2018-11-08 13:17:08 -05:00
Michael Harding 19ae05cb7d SPARK-1678: loading spinner (cherry picked) 2018-11-08 12:14:14 -05:00
Michael Harding 79ec41556e SPARK-1678: Refresh orders after one second (cherry picked) 2018-11-08 12:12:44 -05:00
Michael Harding 088377582d SPARK-1678: Small code tweaks based on Eric's suggestions 2018-11-08 12:02:13 -05:00
Michael Harding c8b3973b8f Remove Beta from SparkAdmin 2018-11-08 12:00:15 -05:00
Kevin Darby efc490cc0d -if a user creates an account, he is given admin permissions on that account
-if a user creates a user, he assigns that user trading permissions in one of the accounts he can administer (so he can still see that user, someone else can undo)
-bts user has the 'wheel'
-bts user can grant other users 'create' permissions, and those users can in turn grant other users create permissions
2018-11-08 11:52:58 -05:00
Kevin Darby 82a2ba4a86 Merged in fix_algo_tests (pull request #96)
remove out of date test
2018-11-08 16:15:55 +00:00
Kevin Darby ccbe20eb57 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_algo_tests 2018-11-08 11:07:11 -05:00
Kevin Darby 70a2feb92a remove out of date test 2018-11-08 11:06:41 -05:00
Pedro Pinto a4d542067a Benchmarked order state copy. Its slow. 2018-11-08 15:01:46 +00:00
Pedro Pinto efd222133e Better perf tools. 2018-11-08 15:01:36 +00:00
Pedro Pinto 361491ebd2 Removed obsolete test. 2018-11-08 15:01:23 +00:00
Eric Harding 6aeba4f599 Remove unused MarketData config field 2018-11-07 15:59:07 -05:00
Kevin Darby 559ab69940 can_create is enforced 2018-11-07 15:53:40 -05:00
Pedro Pinto b35dae9fab Improved logging of hedge trace. 2018-11-07 20:00:41 +00:00
Taha Afzal 9d96fcaa45 added proto 2018-11-07 01:52:23 -05:00
Kevin Darby e75be340e2 GatewayLoginResult 2018-11-06 18:12:31 -05:00
Jake Norbut 523f02b4c2 Merged in SPARK-1864 (pull request #95)
SOD tm_isdst bug regarding our defaults; new defaults

Approved-by: John Colvin <john.colvin@bluetradesys.com>
Approved-by: Taha Afzal <taha.afzal@bluetradesys.com>
2018-11-06 22:19:05 +00:00
Kevin Darby a5b7332fa4 admin ui almost works with permissions 2018-11-06 16:17:21 -05:00
Jake Norbut edb4ed02c9 Merged in xofy (pull request #94)
SPARK-1869 - x of y for all

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-06 21:01:20 +00:00
Jake Norbut 96da8e3276 SOD tm_isdst bug regarding our defaults; new defaults 2018-11-06 14:46:40 -06:00
John Colvin c056bfcf7a Merged in SPARK-1560 (pull request #93)
SPARK-1560 Synthetic Management browser maintain order

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-06 20:43:54 +00:00
John Colvin 2ddf1a1762 Merged in SPARK-1870 (pull request #91)
SPARK-1870

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-06 20:40:26 +00:00
Jake Norbut 8a9ddfded7 SPARK-1869 - x of y everywhere 2018-11-06 13:59:21 -06:00
John Colvin a83f97e1ef SPARK-1560 Synthetic Management browser maintain order 2018-11-06 13:54:25 -06:00
Michael Harding 3630bf5b9a Test updates 2018-11-06 14:51:49 -05:00
Pedro Pinto b9d3a4d04c Fixed typos in SocketConnection perf log. 2018-11-06 17:14:05 +00:00
Eric Harding c7fb636e49 Factor out MD argument parsing 2018-11-06 10:52:18 -05:00
Kevin Darby c7ea6c4ca4 Merged in fix_algo_tests (pull request #92)
clarified  algo tests for new functionality
2018-11-06 15:42:51 +00:00
Kevin Darby 1ffda9c413 clarified algo tests for new functionality 2018-11-06 10:39:55 -05:00
Pedro Pinto e2f314d007 Added optional pause between orders to iceberg. 2018-11-06 13:43:37 +00:00
Pedro Pinto 80d8307822 Cleaned up docs build. 2018-11-06 13:41:45 +00:00
John Colvin 1ca5ed4520 fix typeo in comment 2018-11-05 17:57:43 -06:00
John Colvin 8115f9df74 remove launchSettings.json (added by accident) 2018-11-05 17:52:23 -06:00
John Colvin 306cbaa837 SPARK-1870 / SPARK-1871 if an order's security arrives late, the security cell view doesn't automatically refresh 2018-11-05 17:48:40 -06:00
John Colvin efa31059d9 SPARK-1870 Orderbook - if an order's security arrives after the order, it is always displayed as 'unknown-security' 2018-11-05 17:12:27 -06:00
John Colvin 73089634f9 Merged in SPARK-1805 (pull request #90)
SPARK-1805 Removing a synthetic disables the remove menu for the life of the window

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-05 18:44:08 +00:00
John Colvin a84e5e3460 SPARK-1805 Removing a synthetic disables the remove menu for the life of the window 2018-11-05 11:26:35 -06:00
Pedro Pinto eca11d0317 Merge branch 'master' into SPARK-1799-add-hedge-perf-traces 2018-11-05 15:18:18 +00:00
Pedro Pinto ced485856e Added some experimental perf related code. 2018-11-05 14:24:00 +00:00
Kevin Darby 08c54f75eb Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-04 21:32:36 -05:00
Pedro Pinto 6c0cc76773 Significantly improved engine performance when updating multiple orders. 2018-11-04 22:14:14 +00:00
John Colvin 4d53bfb3f0 Merged in SPARK-1863 (pull request #89)
SPARK-1863

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-04 19:19:47 +00:00
Kevin Darby e332edb36b users should be active when created 2018-11-02 18:18:45 -04:00
Kevin Darby 70b5d9adc6 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-02 18:15:24 -04:00
Kevin Darby 23ee9508c2 now all we need to do is filter the webUI 2018-11-02 18:13:12 -04:00
John Colvin 717391afb3 SPARK-1863 Fills Time Filter 2018-11-02 14:26:34 -05:00
John Colvin 38e0d40364 DateTimePicker - time textbox behavior should commit text on 'Enter' (i.e. update binding source) 2018-11-02 14:06:02 -05:00
John Colvin 7053c2ee03 DateTimePicker - internal view-Controller should update TimeTuple prop with SetCurrentValue 2018-11-02 14:02:23 -05:00
John Colvin cdde730fc5 TimeFilterControl - hyperlink commands should update props with SetCurrentValue (i.e. don't break bindings) 2018-11-02 13:59:27 -05:00
John Colvin 95b0119d22 DateTimePicker - make time-text input regex more flexible 2018-11-02 13:54:26 -05:00
John Colvin 07a965a3e0 DateTimePicker - remove 'delay' from text-input bindings 2018-11-02 13:52:36 -05:00
John Colvin eba153e289 DateTimePicker.xaml - change bad staticresource name (had '2' in it!) 2018-11-02 11:06:31 -05:00
John Colvin fc5238c56f move CompareUtils into bts.utils namespace 2018-11-02 10:51:17 -05:00
John Colvin bc367b9f56 rename ObjectHelper -> ConvertUtils 2018-11-02 10:44:21 -05:00
Eric Harding 8adcc98ff1 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-1798-md-benchmark 2018-11-02 11:24:52 -04:00
Eric Harding ba07041c26 Avoid PriceSize_from_double in example code. (poor performance) 2018-11-02 11:17:48 -04:00
Eric Harding d3101b1cd8 Add PLATFORM_MDP3EMUMBA 2018-11-02 11:17:24 -04:00
Eric Harding 3a9a86020b Fix broken link in manual 2018-11-02 11:15:27 -04:00
Pedro Pinto 42afdc2994 Added a bit more ordermanager perf instrumentation. 2018-11-02 14:42:55 +00:00
Kevin Darby 1dc87de975 now just need to filter accounts and users in the webui 2018-11-01 23:41:42 -04:00
John Colvin 6c93208e11 update SortedTreeTests 2018-11-01 17:48:33 -05:00
Kevin Darby 49ba2e72a9 no idea why this doesn't work 2018-11-01 18:35:19 -04:00
Eric Harding bf1ca4e5fa Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-11-01 16:55:04 -04:00
John Colvin 3f9e64016b Merge remote-tracking branch 'origin/orderbook-performance' 2018-11-01 15:09:43 -05:00
John Colvin c672967caf OrderBookView.xaml - fix status bar bindings 2018-11-01 14:59:26 -05:00
John Colvin 1e0dac0fbc SortedTree - include orphans in the main _tree as root-nodes 2018-11-01 13:40:56 -05:00
John Colvin aec8de54bd move some utils from spark -> spark_core 2018-11-01 13:40:56 -05:00
Jake Norbut a960093cf6 Workspaces animate fix - Becuase this is the fix Worksapce ovwerite animation deserves, but not the one it needs right now. 2018-11-01 12:49:05 -05:00
Jake Norbut 4601e49106 Workspaces - fix highlighting around shared vs nonshared workspaces 2018-11-01 12:49:05 -05:00
Jake Norbut 7e65db38cf Fixing log message typo; i before e, except after c, etc 2018-11-01 12:49:05 -05:00
Eric Harding d70bd63659 sparkd md dumpsecurity should dump synthetic metadata 2018-11-01 13:26:29 -04:00
Kevin Darby 47dc0d579b mic aliases 2018-11-01 12:18:10 -04:00
Kevin Darby ef6662d4f8 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-11-01 11:59:20 -04:00
Eric Harding 246f9240e7 Tweak md dump tool for easier human consumption 2018-11-01 11:53:47 -04:00
Michael Harding a8ebb9cdcb Manual test updates 2018-11-01 11:41:00 -04:00
Michael Harding bdb2d6cf9e SPARK-1862 2018-11-01 11:41:00 -04:00
Kevin Darby b9aaaa7d37 mic mapping for brian 2018-11-01 10:37:09 -04:00
Kevin Darby ec43039b94 ok michael, all yours 2018-10-31 18:05:39 -04:00
Kevin Darby 4939b85819 I think I got it 2018-10-31 18:01:54 -04:00
Kevin Darby fab7596d06 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_mics 2018-10-31 17:27:13 -04:00
Kevin Darby 81d67f640b my head hurts from the f# 2018-10-31 17:26:34 -04:00
Eric Harding 1966c8e0c6 always bypass entitlement take 2 2018-10-31 15:49:37 -04:00
Jake Norbut a418f47d1c Updating Sounds test 2018-10-31 14:36:57 -05:00
Jake Norbut df516fbd2d ICMP ping - increasing default logging threshold 2018-10-31 13:02:22 -05:00
John Colvin b8e298c524 Revert "OrderBookColumnCollection - add 'finally' block to try/catch in InsertItem()"
This reverts commit 4ddb8fe9c5.
2018-10-31 11:10:18 -05:00
Jake Norbut d96c9b16bc Merged in SPARK-1736 (pull request #88)
SPARK-1736 - client-based ICMP ping
2018-10-31 14:43:16 +00:00
Eric Harding 3df7156ba3 Disable entitlement checks in QueryServer 2018-10-31 10:34:24 -04:00
Eric Harding 4376605d5e Merge orderbook-perf 2018-10-31 10:33:18 -04:00
Michael Harding 3233187fd9 Improve perf trace ui handling of incomplete data 2018-10-31 10:26:16 -04:00
Jake Norbut 4d8a2f28d3 SPARK-1736 - client-based ICMP ping 2018-10-31 08:44:20 -05:00
Pedro Pinto 930f74ff27 Merge branch 'prod' 2018-10-31 13:31:36 +00:00
John Colvin 4ddb8fe9c5 OrderBookColumnCollection - add 'finally' block to try/catch in InsertItem() 2018-10-30 17:17:32 -05:00
John Colvin 74aed2144d OrderBookColumnCollection - add comment explaining the preference for columns that implement ICVOrderBookColumn 2018-10-30 17:16:18 -05:00
John Colvin 87437883a6 SortedTree - in Reset() method, clear existing items if new_items are null 2018-10-30 17:03:36 -05:00
John Colvin d946cb4765 OrderBookWindowFactory - ensure default UserSettings.order_book_grid != null 2018-10-30 16:57:15 -05:00
John Colvin 37c9707c63 DeleteSyntheticDaemon - show support dialog if delete request fails due to working-orders verification timeout 2018-10-30 16:55:49 -05:00
Jake Norbut 02d1badf0d Adding vertical fudge factor so that no scrollbar is initally present on the SparkCommands; nice catch Eric 2018-10-30 16:17:08 -05:00
John Colvin 2a295237a6 refactor string extension ic_equals -> iequals 2018-10-30 15:54:04 -05:00
John Colvin dc8b0b8359 OrderBookRowCollection - reword comment 2018-10-30 15:50:51 -05:00
John Colvin d36a5b30e6 SortedTree - change Replace() to TryReplace() 2018-10-30 15:47:31 -05:00
Kevin Darby fe8f217587 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-10-30 14:57:18 -04:00
Michael Harding b69958007d Fixing typo that any other language would have called an error 2018-10-30 13:22:20 -04:00
John Colvin 616fbdba00 Merge remote-tracking branch 'origin/prod' into orderbook-performance 2018-10-30 11:49:04 -05:00
Michael Harding f433a3163b Fix rows layout 2018-10-30 12:39:35 -04:00
Eric Harding 753221e429 Easier to see shell latency across version upgrades 2018-10-30 12:07:48 -04:00
Jake Norbut d5f41d8405 WorkLoop execessive_cycle_ms to use TotalMilliseconds and not Milliseconds 2018-10-30 11:04:42 -05:00
Jake Norbut 3490834108 Merged in SPARK-1403 (pull request #87)
SPARK-1403

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-10-30 13:49:58 +00:00
John Colvin 64bef0bd06 missed one 2018-10-29 23:57:15 -05:00
John Colvin e4ea5cd769 SortedTreeTests - update comments, break up linq query for readability 2018-10-29 23:56:08 -05:00
John Colvin c8790f766a SortedTree - replace AddAll() with Reset(), and support absent parents 2018-10-29 21:38:19 -05:00
John Colvin 2243dc566e move DictionaryExtensions to spark_core 2018-10-29 21:25:40 -05:00
Jake Norbut 94c2d9182c Updating release notes for footers and 1.3.7 here in this txt file 2018-10-29 17:43:32 -05:00
John Colvin 12d6374158 SortedTreeTests - reverse id-comp for name-descending comparer 2018-10-29 17:24:24 -05:00
John Colvin 58b9c27098 SortedTreeTests - new test: 'test_sorted_tree_state_transitions' 2018-10-29 17:22:15 -05:00
Jake Norbut f68aac2015 Merge branch 'footers' into prod 2018-10-29 17:19:18 -05:00
John Colvin ca5334fd87 OrderBook/SortedTree - comparer needs to be 100% deterministic 2018-10-29 17:17:20 -05:00
Michael Harding 71961637f1 Update release notes 2018-10-29 17:10:54 -04:00
Eric Harding 5aab80aed6 Merged cave_paintings into prod 2018-10-29 16:34:49 -04:00
Eric Harding 12e7c82698 Merged prod into cave_paintings 2018-10-29 16:32:59 -04:00
Pedro Pinto bb82590e5e Added sim execution for globex securities, this should facilitate production testing 2018-10-29 20:17:04 +00:00
Taha Afzal 0c4cdd0bc9 Merge branch 'net_change_final' into prod 2018-10-29 16:11:29 -04:00
Pedro Pinto 1855fe3045 Merge branch 'prod' into SPARK-1799-add-hedge-perf-traces 2018-10-29 18:21:46 +00:00
Pedro Pinto e3dba306a7 Merge branch 'SPARK-1799-add-hedge-perf-traces' of bitbucket.org:bluetradesys/spark into SPARK-1799-add-hedge-perf-traces 2018-10-29 17:58:21 +00:00
Pedro Pinto 74f79a2f52 Added a few utilities for perf testing. 2018-10-29 17:56:44 +00:00
John Colvin ca0c17a41b OrderBookVM - change comment in load_from() 2018-10-29 12:55:34 -05:00
John Colvin 3acaf5b2c0 OrderBookVM - slight optimization in load_from(startupdata) method 2018-10-29 11:57:20 -05:00
Taha Afzal 00d8ea9322 Avg fill price reporting for net change spreads fixed 2018-10-28 22:25:34 -04:00
John Colvin eedf19474e SortedTree - when node is removed, any remaining children (orphans) go to the nursery 2018-10-28 15:57:08 -05:00
Taha Afzal a586b75226 case statamenet was missing the new state, settlement_populated fixed 2018-10-27 22:13:01 -04:00
John Colvin c6039723da Synthetic Editor - re-enable 'Market Data Calculation' param-row 2018-10-26 22:17:37 -04:00
Taha Afzal 6f26164072 net_change is done from the server side 2018-10-26 19:23:13 -04:00
John Colvin a3de2c1983 SortedTree - _replace was not re-adding node if parentkey changed 2018-10-26 14:44:58 -05:00
John Colvin 8bd2e5e5f3 OrderBook - guard against null GridSettings 2018-10-26 14:41:00 -05:00
Jake Norbut 7b01fdcb23 Merge branch 'prod' of bitbucket.org:bluetradesys/spark into prod 2018-10-26 14:25:41 -05:00
Jake Norbut d6df085d8e SPARK-1648 - My Windows, now with more Scrollbar 2018-10-26 14:25:31 -05:00
John Colvin 5e2a7c82c0 remove OrderBookCell.cs 2018-10-26 14:01:05 -05:00
Kevin Darby c6f2fe9b13 Merged in nupkg (pull request #81)
added <PackageOutputPath> to spark_sdk/spark_sdk.csproj so the nupkg can be shipped with the build

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-10-26 18:28:59 +00:00
John Colvin c56b41bf47 add SoundNotifications toggle.conf 2018-10-26 11:35:47 -05:00
Jake Norbut 484c7a98a8 Sound crash fixed in both INotifier and SoundAnimationView (the preview sound) 2018-10-26 10:57:34 -05:00
Jake Norbut 21b65a92fe Sound crash fixed in both INotifier and SoundAnimationView (the preview sound) 2018-10-26 10:35:12 -05:00
Michael Harding 2c384af157 SPARK-1800: Add Hedge Traces 2018-10-26 10:15:43 -04:00
Taha Afzal 559b6f8fd1 changed the state transition, so START->WAITING_FOR_DATA->WORKING 2018-10-25 21:12:43 -04:00
John Colvin 7149a20865 OrderBookRowCollection - add total_row_count property (for upcoming status bar) 2018-10-25 20:03:38 -05:00
John Colvin 407dbc4c0a GridSettingsManager - invalidate all rows after settings update to reflect changes 2018-10-25 19:55:25 -05:00
John Colvin eff1954260 DIYGrid - accidentally removed an 'empty_if_null()' call 2018-10-25 19:35:06 -05:00
John Colvin 81880779c3 OrderBook - retain column sorting when syncing with new default grid settings 2018-10-25 19:33:18 -05:00
Taha Afzal 41fa3046f1 Added new spreader state WAITING_FOR_INIT_DATA, not hooked up though 2018-10-25 20:01:32 -04:00
John Colvin 21a3386574 Merge branch 'prod' into orderbook-performance 2018-10-25 18:45:20 -05:00
Jake Norbut a9ef7e47b5 tweaking animation to make it more pronounced 2018-10-25 17:08:24 -05:00
Eric Harding 0c19f82a24 If enabled segfault 2018-10-25 17:54:05 -04:00
Jake Norbut 9762a81a59 this is working now, thx Eric 2018-10-25 16:45:53 -05:00
Eric Harding c34c99a874 Merge prod -> master 2018-10-25 17:17:52 -04:00
John Colvin c19aad55a7 OrderBook - load/save filters and sorting 2018-10-25 15:57:09 -05:00
Jake Norbut 744260bea5 Workspaces - trying to just get a proof of conept of something happenning in the UI; not working in our UI at the moment, but I can see it kind of working in snoop? 2018-10-25 15:22:51 -05:00
John Colvin dfe480d1f7 OrderBookStartupData.FilterStates implement IEquatable 2018-10-25 13:28:40 -05:00
John Colvin 7620b7878e UInt/Double min-max filters implement IEquatable 2018-10-25 13:27:37 -05:00
Eric Harding 99fd77c1f1 Add emumba platform 2018-10-25 13:01:38 -04:00
Eric Harding fdf21c71bb Enable separate thread config for different market data channels 2018-10-25 12:49:10 -04:00
Eric Harding f03453f7c5 Let threads float by default. We will override to tune specific systems 2018-10-25 12:28:42 -04:00
Jake Norbut 9ebdb107fa fixing SODTime defaults 2018-10-25 10:28:38 -05:00
Jake Norbut e0f1137ee2 initial commit of fotters; waiting until after john does the OB perf commit until revisiting this again 2018-10-25 07:43:26 -05:00
John Colvin 079a7ad439 OrderBookRowCollection - when filter changes, call clear/addall instead of sync_with 2018-10-24 22:33:43 -05:00
John Colvin ae100f66c7 OrderBookRowCollection - optimize expand/collapse all (avoid noisy intermittent collection changes) 2018-10-24 19:04:51 -05:00
Eric Harding 057d5df6da Add --name to spark_vm_shell.sh 2018-10-24 17:24:30 -04:00
Eric Harding f539659ab5 Update ReleaseNotesParade (but leave disabled) 2018-10-24 17:07:55 -04:00
Eric Harding e4d9b902d8 Teach webserver.cpp where manual is 2018-10-24 17:03:23 -04:00
Eric Harding 9fb180ff20 Add --name to spark_vm_shell.sh 2018-10-24 16:26:43 -04:00
Eric Harding a6fde760ff Docs build 2018-10-24 16:08:57 -04:00
Pedro Pinto 7b0e3edfcf Merged 2018-10-24 15:12:43 -04:00
Michael Harding 019bbaa0d2 update release notes 2018-10-24 14:58:07 -04:00
Pedro Pinto 188a226165 Updated release notes 2018-10-24 18:46:36 +00:00
Pedro Pinto 9aba824be0 Merge branch 'grid-settings' into prod 2018-10-24 18:19:33 +00:00
Pedro Pinto a958be45dd Added command to facilitate discovery of legged orders 2018-10-24 18:17:54 +00:00
John Colvin 7fd0375283 SPARK-1807 Synthetic Add Leg OOM Exception 2018-10-24 12:49:35 -05:00
Pedro Pinto dc2879e2ff Merged 2018-10-24 13:46:41 -04:00
Eric Harding 7fa75b8090 Force implied merge to use the same precision as direct to avoid ladder overlap 2018-10-24 12:48:20 -04:00
John Colvin 94f3c5a6c4 OrderBookRowCollection - when comparer changes, don't reset if refresh is deferred 2018-10-24 11:30:38 -05:00
John Colvin 5e58aaf199 Synthetic Editor - disable 'Market Data Calculation' param-row 2018-10-24 11:16:07 -05:00
Taha Afzal 54231e2d74 spreader test for implieds 2018-10-24 10:56:51 -04:00
Michael Harding c3f9e5fa91 Update Release notes 2018-10-24 10:45:29 -04:00
Eric Harding 0042835842 Combine 2 sim config keys into one. 2018-10-24 10:17:56 -04:00
Pedro Pinto 99b71ad2aa Merge branch 'grid-settings' into SPARK-1799-add-hedge-perf-traces 2018-10-24 08:51:24 -04:00
John Colvin c127f488ff OrderBookRowCollection - when sorting changes, call clear/addall instead of sync_with 2018-10-23 20:37:26 -05:00
John Colvin ba7a28aa8a SPARK-1806 orderbook-performance crash 2018-10-23 18:32:07 -05:00
John Colvin 95a7f7ccb6 OrderBookRowCollection - show whole branch if any order passes filter 2018-10-23 18:27:29 -05:00
John Colvin f90961ce22 SortedTree - add nursery-dictionary for items without parents 2018-10-23 18:19:27 -05:00
Kevin Darby a53d48a6e3 FirstOrDefault is incompatible with immutable.... 2018-10-23 17:37:31 -04:00
Kevin Darby d3ffc9eed5 Merged in bts_user_override (pull request #86)
allow bts user to see all fills/orders/p&l, etc per Pedro

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-10-23 21:14:49 +00:00
Kevin Darby dcfa809b9e allow bts user to see all fills/orders/p&l, etc per Pedro 2018-10-23 17:10:21 -04:00
Jake Norbut fa31c2ef11 MView - Better (Implied, Direct, Merged) Header Text defaults, column size adjusted appropriately 2018-10-23 15:39:47 -05:00
Eric Harding 7c28938ba6 Stop slowly publishing md_permissions when nobody is listening. 2018-10-23 16:10:25 -04:00
Eric Harding 0a426b84e3 Fix sparkd crash in webserver.cpp 2018-10-23 15:28:00 -04:00
Eric Harding 45f5e485d3 Remove unused md defaults 2018-10-23 15:27:45 -04:00
Eric Harding 853f8d2e84 Start ExchangeSim earlier to avoid race condition. 2018-10-23 14:18:58 -04:00
Jake Norbut d1784355d0 icons.cs - applying a layer of frost 2018-10-23 11:51:47 -05:00
Pedro Pinto 58c5eeb6d1 Merge branch 'grid-settings' into SPARK-1799-add-hedge-perf-traces 2018-10-23 11:15:26 -04:00
Pedro Pinto f045d272c1 Moved docker ptr 2018-10-23 15:14:00 +00:00
Pedro Pinto faa6528400 Finished prod merge. 2018-10-23 15:05:28 +00:00
Pedro Pinto be33ecc798 Added some queue related benchmarks. 2018-10-23 15:04:58 +00:00
Eric Harding 37f3550831 subrepos are deranged 2018-10-23 09:45:40 -04:00
Eric Harding e2d454da1e advance externals pointer... hopefully. 2018-10-23 09:02:43 -04:00
Eric Harding 7af2467713 Merge from prod 2018-10-23 09:01:37 -04:00
Pedro Pinto d8e203264d Merge branch 'grid-settings' of bitbucket.org:bluetradesys/spark into grid-settings 2018-10-22 22:25:37 +00:00
Pedro Pinto 4a0cefd240 Added support for late joiners in settlement update events. 2018-10-22 22:25:09 +00:00
Jake Norbut e5aef7ee5b OB GridSettings - fixing NullRef when using Dan's DB 2018-10-22 14:27:45 -05:00
John Colvin 6c03a263a1 OrderBookRowCollection - create new SortedTree on reset, rather than Clearing current 2018-10-22 12:27:18 -05:00
John Colvin 862e199495 make SortedTree/INode public (again) 2018-10-22 12:02:37 -05:00
John Colvin 5218b748e8 SPARK-1791 / SPARK-1792 Improve OB performance 2018-10-21 18:20:53 -05:00
John Colvin ba9bd53c1e DIYGrid - attach/detach Sorting event if DataContext implements IHandleGridSorting 2018-10-21 18:20:40 -05:00
John Colvin 8b816f1491 SPARK-1801 OrderRecordTupleSource - introduce updates w/ 'change' member, broadcast order_securities 2018-10-21 17:29:17 -05:00
John Colvin 04cc18553b DIYGrid.IRowVM - remove 'row_animations' and 'cell_stores' members 2018-10-21 16:52:02 -05:00
Pedro Pinto cbb638ff7a More perf logging. 2018-10-20 18:26:54 +00:00
Pedro Pinto 9cd2518853 Added do_work loop data to perf trace. 2018-10-20 16:40:14 +00:00
Pedro Pinto 2db8d717d4 Added logging of mercury events to uncover performance problems. 2018-10-20 16:36:21 +00:00
Pedro Pinto df56c85e7b Added C string logging for fast logging of string literals. 2018-10-20 16:34:03 +00:00
Pedro Pinto fb12d429cb Added hedge traces to perf stats. 2018-10-19 20:52:53 +00:00
Eric Harding c1c067522c Advance externals post merge 2018-10-19 12:41:12 -04:00
Eric Harding cdf537afaa Fixup from merge 2018-10-19 12:36:18 -04:00
Eric Harding 7867cc934b Merge from master 2018-10-19 12:23:02 -04:00
Eric Harding 1ec7c141b9 Remove unused open_interest 2018-10-18 10:23:09 -04:00
Pedro Pinto 9f2ac024cb Fixed rouding problem in Sim price levels. 2018-10-16 20:09:00 +00:00
Eric Harding 92a8cb507a Merge branch 'SPARK-1659' of bitbucket.org:bluetradesys/spark into grid-settings 2018-10-16 09:58:07 -04:00
Eric Harding ea87dcb52a Respect flag which enable/disable implied forwarding in simulator 2018-10-16 09:01:43 -04:00
Eric Harding 467a66565d Merge branch 'SPARK-1659' of bitbucket.org:bluetradesys/spark into SPARK-1659 2018-10-16 08:51:06 -04:00
John Colvin a52b477b0c SPARK-1778 VAP bars disappear when the book goes away 2018-10-14 18:36:49 -05:00
John Colvin dbd0bfed10 SPARK-1776 / SPARK-1788 default the volume at price column to NOT showing 2018-10-14 18:16:08 -05:00
John Colvin ee25d3ed9a SPARK-1776 / SPARK-1785 LadderRender - skip volumes with nan prices 2018-10-14 18:07:05 -05:00
John Colvin d662a60f6a SPARK-1787 Hyperlink Cells - bind width to Column.ActualWidth (fixes stale background when column width changes) 2018-10-14 17:57:02 -05:00
John Colvin c0a80182e6 HyperlinkCell - only draw BackgroundOverlay when Background != null 2018-10-14 17:36:08 -05:00
John Colvin 4360a1154c Ladder - market size display settings 2018-10-14 16:17:38 -05:00
John Colvin 1cb6066aea Merge branch 'SPARK-1659' into grid-settings 2018-10-14 15:56:48 -05:00
Taha Afzal e85d7b5d0c 1.IntPrice comparison can now compare IntPrices of different exponents 2.copy_book for direct with holes fixed 2018-10-11 17:19:48 -04:00
Jake Norbut 143f660f37 adjusting Symbol column default width 2018-10-11 14:58:40 -05:00
Jake Norbut 4107ac093b OrderBook spit and polish 2018-10-11 14:00:17 -05:00
Pedro Pinto 0b7cdd656e Updated deps files 2018-10-11 17:42:51 +00:00
John Colvin c90752b4f2 OrderBook - small fixes for grid-settings conversion 2018-10-11 11:49:43 -05:00
Jake Norbut f97ccf249f GridSettingsVM is now ipmlements ITemplateSelectorKey so that we can actually see the settings 2018-10-11 09:33:46 -05:00
Kevin Darby c705867218 added <PackageOutputPath> to spark_sdk/spark_sdk.csproj so the nupkg can be shipped with the build 2018-10-10 18:06:12 -04:00
John Colvin 2d3e942f93 fix OrderBook grid settings use_defaults behavior 2018-10-10 16:56:50 -05:00
Jake Norbut d965674ff5 ContentTemplateSelector is now used in SimpleGridSettingsDialog; Ladder now uses this instead of LadderSettingsDialog 2018-10-10 15:52:18 -05:00
Eric Harding a174336baf MDSource should always use the complete book 2018-10-10 11:22:51 -04:00
Eric Harding 746e858f76 SPARK-1659 Typo in simulator implieds 2018-10-10 09:16:24 -04:00
Eric Harding 48702ce266 Merge branch 'SPARK-1659' of bitbucket.org:bluetradesys/spark into SPARK-1659 2018-10-10 08:42:42 -04:00
John Colvin 8ba354eda8 SPARK-1780 Orderbook settings - use [new]standard SimpleGridSettingsDialog 2018-10-09 18:48:56 -04:00
Taha Afzal 74b18cf71a added a manual test for implieds in the UI 2018-10-09 17:23:41 -04:00
Eric Harding 65d3b858b3 Fix typo in ExchangeSim which caused implied ask to be shown as bid. 2018-10-09 14:40:07 -04:00
Eric Harding 1e8fd9589b Fix logging crash during shell shutdown 2018-10-09 14:08:22 -04:00
Eric Harding c331c8dde5 Add flag to enable/disable implied forwarding in simulator 2018-10-09 14:08:15 -04:00
Taha Afzal 21a47e2d43 VAP merging into 1659 made the price go to NAN because of the PrizeSize changes 2018-10-09 12:14:05 -04:00
Taha Afzal 8cbe6d7199 MD Aux Service's VAP now uses context.poll instead of its own thread 2018-10-09 12:14:05 -04:00
Michael Harding 58481c9b17 SPARK-1538: Clones incoming 2018-10-09 12:14:05 -04:00
Eric Harding 1b5256d699 SPARK-1742 test can fail when time moves foward 2018-10-09 12:14:05 -04:00
Eric Harding 1e64f930d9 Pass though impileds and settles in simulator 2018-10-09 09:52:52 -04:00
Eric Harding 9630851f1f Add ToString to managed PriceSize 2018-10-09 09:52:42 -04:00
Jake Norbut 46878f009a Updating MinWidth so that remaining windows will all open without a horizontal scrollbar 2018-10-08 15:41:31 -05:00
Jake Norbut ef77826042 MView - shrinking default bid/ask cols from the enlargement I just made 2018-10-08 15:24:20 -05:00
Jake Norbut 344f82dc8f MView spit and polish 2018-10-08 15:16:30 -05:00
Jake Norbut bc362b9778 HyperlinkCell - removed a size i never ended up using 2018-10-08 14:35:53 -05:00
Jake Norbut f17b2cb06d HyperlinkColumn now responds to changes in HorizatonalAlignment 2018-10-08 14:34:19 -05:00
Jake Norbut 1d5878e2f5 HyperlinkCell mouseover behavior now works for all HorizontalAlignments 2018-10-08 14:33:09 -05:00
Jake Norbut 42cfba5ccc TaS spit and polish 2018-10-08 11:30:17 -05:00
Jake Norbut 9e178cf4a1 Remove GetSortingCriteria ext method that is no longer used 2018-10-08 11:04:50 -05:00
Jake Norbut 74cac5815c Fix a variety of small bugs after implementing GridSettings in PnL 2018-10-08 10:43:34 -05:00
Taha Afzal 9b95625603 Wrapper around AdminModel with helper functions 2018-10-08 08:18:55 -04:00
Taha Afzal b47055d814 increasing log period of VAP publisher 2018-10-05 18:36:25 -04:00
Jake Norbut 1a760a579a PnL now support settings 2018-10-05 16:43:13 -05:00
Kevin Darby 76ef5558ea clean up excel build artifacts, fix a null ref. Brian will take it from here 2018-10-05 17:15:32 -04:00
Eric Harding f6b5635715 SPARK-1659 packet may contain implied or direct or both. need to preserve the other market. 2018-10-05 15:45:42 -04:00
Kevin Darby c0e1951f27 caught a reentrancy issue on Brian's version of excel 2018-10-05 15:04:58 -04:00
Jake Norbut 1a7d0d75a8 merge fix problem w/ the DefaultColumns in mview 2018-10-05 13:28:05 -05:00
John Colvin e41a9351c2 Merged in pnl-to-diy (pull request #80)
Pnl to diy

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-10-05 18:07:27 +00:00
John Colvin 7a1ffa1ea9 fix NumericSignBrushConverter 2018-10-05 14:00:09 -04:00
John Colvin db34468bc4 SPARK-1774 pnl - switch to DIYGrid 2018-10-05 13:40:38 -04:00
John Colvin f5d7ea9a47 restructure NumericSignBrushConverter 2018-10-05 13:36:42 -04:00
Jake Norbut dc8c0df0a4 Fix build after merge of SPARK-1659 2018-10-05 11:23:11 -05:00
Kevin Darby 00f2ea75ac Merge branch 'excel' of bitbucket.org:bluetradesys/spark 2018-10-05 10:27:23 -04:00
Michael Harding e4d59e702f SPARK-1769: Remove "Getting Started" as it seems to be causing confusion 2018-10-05 09:28:09 -04:00
Kevin Darby 3def034af8 added sorting and filtering for subscriptions 2018-10-04 18:15:17 -04:00
Jake Norbut b3d3071316 Synth Man - better column width defaults 2018-10-04 16:09:54 -05:00
Jake Norbut 88e352457f Synthetic Management now support Grid settings; need to test still 2018-10-04 15:49:38 -05:00
Eric Harding f1bb6af757 SPARK-1659 Fix for implieds on client side showing up as direct. 2018-10-04 15:59:55 -04:00
Michael Harding 779466ed8d Merged prod into master 2018-10-04 15:36:54 -04:00
John Colvin 97a1cf0162 SPARK-1743 disable column visibility settings for synth ladders 2018-10-04 15:16:35 -04:00
Kevin Darby 38a1139475 got orders working, we need to get rid of MarketDataClient 2018-10-04 15:12:18 -04:00
John Colvin 1821ff8cc7 LadderSettingsView - volume settings help text 2018-10-04 14:20:43 -04:00
John Colvin dd397987ca LadderSettingsView - remove unused code 2018-10-04 14:20:42 -04:00
Eric Harding a5f02c09d8 Extend auto-entitlement to synthetics 2018-10-04 14:12:37 -04:00
Jake Norbut 9b2b320255 merging synth-to-diy; all is well in the grid-settings branch 2018-10-04 12:42:00 -05:00
Jake Norbut 655a89289a Merge branch 'synth-to-diy' into grid-settings 2018-10-04 12:38:30 -05:00
Eric Harding c795feff74 Entitle all sim exchange symbols 2018-10-04 12:58:49 -04:00
John Colvin 911cf8a19c SPARK-1750 Ladder - gracefully handle NaNs in the data 2018-10-04 12:46:48 -04:00
Jake Norbut 7d9aaeac55 ISlowWindow and thsis SyntheticManagement are the bane of my existence 2018-10-04 11:41:14 -05:00
Eric Harding c99d7db058 Merge branch 'SPARK-1669' of bitbucket.org:bluetradesys/spark 2018-10-04 11:35:26 -04:00
Michael Harding 8beccfb510 SPARK-1765: Move Micdata to Market Data Tab 2018-10-04 11:02:38 -04:00
John Colvin 6546ee531f SPARK-1660 Implied Market in MarketView 2018-10-03 19:45:44 -04:00
John Colvin 9ea0e214b6 PriceSize - include 'direct_size' in Equality logic 2018-10-03 19:45:44 -04:00
Kevin Darby 16d43c6766 I think we got orders, need to test tomorrow 2018-10-03 18:35:22 -04:00
Jake Norbut 0ffe8ba972 SyntheticManagement to DIYGrid 2018-10-03 17:00:19 -05:00
Eric Harding 41eb89ed35 SPARK-1749 Invalidate Synthetic book when metadata changes 2018-10-03 17:46:38 -04:00
Kevin Darby 6a0147e0f4 adding custom api for orders from excel the generated ones don't really work 2018-10-03 16:58:47 -04:00
Eric Harding 4d1ea957c3 SPARK-1669 Add super user bit to AuthToken so Sim/Recorder can bypass entitlement 2018-10-03 16:45:11 -04:00
Eric Harding 6f9bad5f11 SPARK-1669 2/2 Enforce entitlement in market data server 2018-10-03 15:03:40 -04:00
Eric Harding 3182bc68ab Fix typo in client.pb.h (qualierifer) 2018-10-03 15:03:32 -04:00
Eric Harding f24ac03034 Improve spark login token (i.e. Dont' hard code it) 2018-10-03 15:02:50 -04:00
John Colvin 9ae087eca1 SPARK-1737 Implied Market in Ladder 2018-10-03 14:58:26 -04:00
John Colvin 08d8dcd53f LadderGridHost - remove redundant 'refresh_depth' method 2018-10-03 14:58:24 -04:00
Taha Afzal 1a279298fd added cons to Book to construct it with a security, thats used by the Spreader now 2018-10-03 13:12:59 -04:00
Eric Harding 479eba940e Fix build from merge 2018-10-03 12:55:09 -04:00
Kevin Darby 74f00e4763 can't quite create an order today, but probably tomorrow. 2018-10-02 18:22:23 -04:00
Kevin Darby 1ce33fe146 spacing 2018-10-02 18:15:39 -04:00
Kevin Darby 19827be1d8 now excel can call in to spark. I can probably send an order 2018-10-02 18:14:27 -04:00
Jake Norbut 4aa64a7b5d Settings - Be sure to launch SimpleGridSettingsDialog with any changes reflected in the vm_factory (user may have made changes on the grid gui which need to be reflected when the settings form launches) 2018-10-02 16:47:17 -05:00
John Colvin 459d097239 Merge branch 'volume_at_price' into SPARK-1659 2018-10-02 17:45:33 -04:00
Taha Afzal 188616d1ce Adding sub event for settlement (which was causing a crash) 2018-10-02 16:46:09 -04:00
John Colvin 9ef1fdb795 ColumnBase raises PropertyChanged for font properties 2018-10-02 16:35:01 -04:00
Kevin Darby c789392e0a generating excel bindings 2018-10-02 16:25:45 -04:00
Eric Harding c09485affc SPARK-1669 1/2 - send login token with market data query 2018-10-02 15:34:20 -04:00
John Colvin e241d13edc SPARK-1665 Net Change and Implied market enums in Synthetic UI 2018-10-02 15:28:56 -04:00
John Colvin df7d791ba1 GridSettings - hook up fontfamily 2018-10-02 14:03:34 -04:00
Jake Norbut b0db7ad4b3 pulling prod in here since i'm gonna make a PR soon 2018-10-02 10:42:45 -05:00
Jake Norbut a29731cb3c TaS - fix default sorting; create GridSettingsManager with rows_vm so that settings changed can be applied to all existing rows and not just new rows 2018-10-02 10:36:08 -05:00
Taha Afzal 9b187797f8 more tests for implied and net_change 2018-10-02 11:26:46 -04:00
Taha Afzal 56ba30e7f4 added tests 2018-10-02 11:26:46 -04:00
Jake Norbut f776099c98 TaS: whoops, more refactoring 2018-10-02 09:41:18 -05:00
Jake Norbut 2825ad833f TaS - Fixing new settings; minor refactoring 2018-10-02 09:37:59 -05:00
Jake Norbut 795915fddc MarketView - don't always save the current GridSetting as Instance settings, they could be defaults 2018-10-01 15:03:50 -05:00
Jake Norbut 1ff0fe0208 Fills - Changing from using startup_data to an IObservable thereof, as such our fills_source is now an IConnectableObservable 2018-10-01 14:47:26 -05:00
Eric Harding 490ad06cf5 SPARK-1698 unless you change the windows' name favorite cannot be removed 2018-10-01 15:31:50 -04:00
Jake Norbut 6d03493bfb Fills - forgot to add various key columns; don't always save the current GridSetting as Instance settings (they could be defaults) 2018-10-01 14:21:46 -05:00
Jake Norbut 080c8cdaa6 WindowSettings - Apply button should only Apply to instance settings 2018-10-01 14:18:00 -05:00
Eric Harding 76ce62f942 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-10-01 14:56:57 -04:00
Eric Harding 6db1aeea3f SPARK-1666 Add tests for synthetic md calculation 2018-10-01 14:39:04 -04:00
Eric Harding a6214a47b5 Intprice subtraction : handle NaN 2018-10-01 14:38:14 -04:00
John Colvin 2da1644f8e KeyDrivenObservableSeries - check 'is_disposed' before subscribing to next inner observable 2018-10-01 14:28:51 -04:00
Jake Norbut e15764eb2b Fills - save new grid settings with state 2018-10-01 10:48:04 -05:00
Jake Norbut 08a6daab00 misc 2018-10-01 09:28:32 -05:00
Jake Norbut ef03b87357 Adding settings to Fills 2018-10-01 09:26:36 -05:00
Eric Harding 0f339f603b Fix: Only publish final settlements 2018-10-01 09:36:25 -04:00
Eric Harding 49796a5c33 Only publish final settlements. 2018-09-28 17:35:01 -04:00
Taha Afzal 51cc64f031 throttling the vap timer log 2018-09-28 13:26:35 -04:00
John Colvin 6bcdc944bf DragRectAdorner - don't animate rect on top of scrollbar 2018-09-28 13:04:34 -04:00
John Colvin 42b4345485 LadderSettings - user can toggle visibility of 'Volume' and 'Last' columns 2018-09-28 12:10:41 -04:00
Eric Harding 20d77d1cf3 Cannot link sdk with benchmarks in sparklib so comment them out 2018-09-28 11:31:26 -04:00
John Colvin 9dd6fc8832 fix CollectionHelper.sync_with_never_duplicate 2018-09-28 11:10:15 -04:00
Eric Harding 3a5f6b331d SPARK-1740 Replace std::vector with boost::small_vector for 100ns improvement. 2018-09-28 09:47:14 -04:00
Jake Norbut d09dbb9422 pair programming at its finest 2018-09-27 16:10:01 -05:00
John Colvin 81afd71d84 GridSettings dialog - column vms maintain immutable properties through load-factory call 2018-09-27 16:59:39 -04:00
Eric Harding da21c57cca Enable selecting release for 'runsparkd.cmd' (for benchmarks) 2018-09-27 15:23:52 -04:00
Eric Harding d6fafee64c Handle possibility of a missing LegSpreaderParams 2018-09-27 15:23:29 -04:00
Eric Harding 0ed884a5ab Enable safe subtration for IntPrice 2018-09-27 14:21:52 -04:00
Jake Norbut 1822ed6aa2 fixing settings immutable bug - default vs NullableBlah?() - i like seeing null, it's make it easier to read 2018-09-27 11:29:37 -05:00
Eric Harding eabfb6a695 SPARK-1712 Use implied or direct market data for synthetic legs 2018-09-27 11:49:09 -04:00
Jake Norbut 58d0a3c688 Fixing sorting - too many criterions 2018-09-27 10:46:43 -05:00
Taha Afzal 8b3d4ba9aa Merge branch 'SPARK-1659' of bitbucket.org:bluetradesys/spark into SPARK-1659 2018-09-26 18:50:02 -04:00
Taha Afzal 8ff94037eb net price and implieds done for spreader 2018-09-26 18:49:52 -04:00
Eric Harding 065f667a79 Fix problem with pesky NaN implied 2018-09-26 18:26:24 -04:00
John Colvin f61b69f6bd fix grid-column-settings immutable values 2018-09-26 18:24:13 -04:00
Eric Harding 9ec85c324c merge from prod 2018-09-26 16:48:55 -04:00
Eric Harding 5ab506e9cd Fix some broken tests so I can more easily write other tests. (tech debt) 2018-09-26 16:41:40 -04:00
Jake Norbut e51d7de309 Freezing all icons; changing Picker icons; misc refactoring 2018-09-26 14:57:31 -05:00
John Colvin 28307b0681 LadderSettingsView.xaml.cs - remove unused field 2018-09-26 14:45:59 -04:00
John Colvin 5af68ccfd0 SPARK-1738 ladders using default settings should clone the settings object, instead of sharing the same one 2018-09-26 14:04:39 -04:00
John Colvin 4002f80ede UserSettingsHub.current getter should be concurrent 2018-09-26 13:57:16 -04:00
Eric Harding 65915a9784 Enable SyncronizeCollectionTests 2018-09-26 13:50:20 -04:00
Eric Harding 7259397e02 Move SecurityTests to spark_test 2018-09-26 13:47:01 -04:00
Jake Norbut 281b4e10b0 functionality matches the spec; I still need to clean up some things 2018-09-26 12:33:08 -05:00
Eric Harding b17a0b1eea Fix logging in Queryserver implied 2018-09-26 09:38:36 -04:00
Kevin Darby 183e070cad Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel 2018-09-25 21:22:07 -04:00
Kevin Darby 6dad1e9965 . 2018-09-25 18:17:38 -04:00
Kevin Darby fe3b46eb14 joined fills and orders with security 2018-09-25 18:12:06 -04:00
Michael Harding d03bde5e9d Merged in CMEMicData (pull request #77)
SPARK-1724: MicData

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-09-25 20:24:13 +00:00
Michael Harding 70426087fc SPARK-1724: Implement Erics suggestions 2018-09-25 16:22:04 -04:00
Eric Harding d7e2c87830 Fix SettlementRecorder 2018-09-25 15:56:36 -04:00
John Colvin 22b1bfa777 SPARK-1671 Volume at price level (UI) 2018-09-25 15:39:48 -04:00
John Colvin 4ea9c6c28d Export MarketDataAuxiliaryService in ShellServices 2018-09-25 15:23:04 -04:00
Eric Harding 2cb5cddb91 Increment market data shm version 2018-09-25 14:58:22 -04:00
Eric Harding cc0dd649ea SPARK-1712 Change settlement price to IntPrice 2018-09-25 14:57:01 -04:00
Kevin Darby 8bcec6b898 got orders 2018-09-25 14:09:32 -04:00
Eric Harding 49e24804f7 SPARK-1659 move settlement into hot data section so we can use it to compute books 2018-09-25 13:35:28 -04:00
Eric Harding 7958ce236f SPARK-1659 Make PriceSize 3rd argument type safe 2018-09-25 10:22:28 -04:00
Taha Afzal f3c55f0981 Merging with net_change 2018-09-25 10:15:23 -04:00
Eric Harding 195049f010 Add POST_CLOSE exchange status enum value 2018-09-25 09:58:14 -04:00
Taha Afzal dc9df4af09 net change pricing hooked up, need some optimizations in PriceSize for speed 2018-09-25 09:46:07 -04:00
Kevin Darby a26955e07d cleanup 2018-09-24 18:26:42 -04:00
Kevin Darby 9330b40d14 cleaning up excel code 2018-09-24 18:13:48 -04:00
Kevin Darby e61f4c96b3 excel for spark 2018-09-24 17:50:50 -04:00
Eric Harding add6f99189 SPARK-1659 Fix operator < (cpp) 2018-09-24 16:32:18 -04:00
Eric Harding 37c6b5d46d SPARK-1711 Expose direct/implied markets in managed 2018-09-24 16:32:04 -04:00
Michael Harding 58e4de2f77 SPARK-1724: MicData 2018-09-24 16:29:42 -04:00
Jake Norbut 0ff7adf73b SimpleGridSettingsManager now reflects settings changes immediately after they are made 2018-09-24 15:13:41 -05:00
Eric Harding 3ffc130889 SPARK-1711 Refactor PriceSize.cs 2018-09-24 13:41:41 -04:00
Eric Harding 69d75292ee SPARK-1712 add synthetic leg parameter for implied/direct book 2018-09-24 12:55:39 -04:00
Eric Harding d6539348ea SPARK-1659 Refactor to use IntPrice everywhere to reduce logic duplication 2018-09-24 12:54:52 -04:00
Jake Norbut 4d00be1ab5 fix build error; fix Columns VerticalAlgnment so that the expected behavior is followed when collapsed 2018-09-24 11:22:48 -05:00
Taha Afzal 505b01cdcb Settlements hooked to the order infrastructure 2018-09-23 20:14:40 -04:00
Eric Harding e8b210913c SPARK-1659 Refactor PriceSize to PriceSizeSize 2018-09-21 16:48:59 -04:00
John Colvin 2e10e997b0 prepare simple-grid-settings default\custom infrastructure 2018-09-21 15:46:33 -04:00
Kevin Darby 9be342f0c1 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-21 13:20:42 -04:00
Michael Harding f1af9e31d0 SPARK-1699: Merge release notes into single file in the manual 2018-09-21 12:48:58 -04:00
Michael Harding 51e58c0e58 SPARK-1699: Fix link to h1 in manual 2018-09-21 12:48:58 -04:00
Jake Norbut 530975d9de before john's pr to help me get a handle on the SimpleGridSettingsManager and the WindowSettings 2018-09-21 11:11:31 -05:00
Kevin Darby 81ae1ef059 Merge branch 'excel' of bitbucket.org:bluetradesys/spark into excel 2018-09-21 11:59:37 -04:00
Kevin Darby a7caf78e6c builds 2018-09-21 11:28:28 -04:00
Kevin Darby 05ca579dcb moving excel code over 2018-09-21 11:28:28 -04:00
Kevin Darby 498d46320a spark now has an excel api 2018-09-21 11:28:28 -04:00
Eric Harding 58a1e486fb Merge prod->master 2018-09-21 11:27:27 -04:00
Kevin Darby 2eb3ff4347 builds 2018-09-21 11:21:08 -04:00
Michael Harding 79e1adbf7b Merged SPARK-1701-move-about-spark-in-the-help-menu into master 2018-09-21 10:44:22 -04:00
Michael Harding 5f214dc933 SPARK-1701: going down 2018-09-21 10:43:39 -04:00
Kevin Darby 6582065be0 moving excel code over 2018-09-21 09:38:53 -04:00
Kevin Darby 3b6c998f67 spark now has an excel api 2018-09-20 18:20:40 -04:00
Eric Harding f560a9e603 Shrink PriceSize 2018-09-20 15:34:25 -04:00
Taha Afzal 83889f2cbc Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-09-19 22:03:10 -04:00
Kevin Darby d8810478fe Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-19 18:16:25 -04:00
Eric Harding d20e937a79 SPARK-1713 Add implied to QueryServer 2018-09-19 18:08:52 -04:00
Eric Harding 1a4ba59a2a Fix build 2018-09-19 16:55:02 -04:00
Kevin Darby ff19a700d9 whoops 2018-09-19 16:50:24 -04:00
Kevin Darby c5ee98ebaa Merge branch 'dynamic2' of bitbucket.org:bluetradesys/spark 2018-09-19 16:49:23 -04:00
Kevin Darby 3dbfd2ebfd Eric's PR comments 2018-09-19 16:47:42 -04:00
Eric Harding ec3f246576 Fix build. Can't seem to get this right today. 2018-09-19 11:31:24 -04:00
Eric Harding 78b1fe8663 Fix build. 2018-09-19 11:16:23 -04:00
Eric Harding cfe51cdf82 base64 encode jake's favorite log message 2018-09-19 10:00:39 -04:00
Taha Afzal b7a99047ed Moved the VolumeAtPrice toggle to inside the MarketDataAuxiliatyServiceServer.cpp 2018-09-19 09:48:40 -04:00
Taha Afzal 98eb65c99f Merge conflict that was left out accidentally 2018-09-18 19:37:16 -04:00
Taha Afzal d1f346b28c Added buy/sell/passive volume to Volume At Price 2018-09-18 19:21:05 -04:00
Kevin Darby a71b6b3b99 cleaned up Top, Join algos, added Offset Algo. 2018-09-18 18:16:58 -04:00
Taha Afzal 39bf6ebaa5 git cherry-pick add file 2018-09-18 17:54:34 -04:00
Taha Afzal 9011df884e fixing header inclusions 2018-09-18 17:53:23 -04:00
Taha Afzal 92490e1a75 SPARK-1672(Volume at price) 2018-09-18 17:51:01 -04:00
Taha Afzal 8ee6d6d860 cherry pick add file 2018-09-18 17:50:05 -04:00
Taha Afzal 543dfa8509 SPARK-1672 scaffolding 2018-09-18 17:47:27 -04:00
Taha Afzal a8bc7a071e SPARK-1672 VAP initial check in 2018-09-18 17:44:18 -04:00
Eric Harding 02071c1289 SPARK-1709 Update OnixS to write direct/implied 2018-09-18 16:45:27 -04:00
Jake Norbut ce80e12451 really fix GridColumnSettings.with() regarding horizontal_alignment 2018-09-18 15:13:27 -05:00
Jake Norbut a04df917d4 fixing null Ref when creating ToS settings 2018-09-18 15:06:54 -05:00
Jake Norbut 0a7d8f931b fix GridColumnSettings.with() function (don't override horizontal_alignments inadvertantly) 2018-09-18 14:44:10 -05:00
Kevin Darby 0e28c9d01c John is going to route this to the right place 2018-09-18 15:16:10 -04:00
John Colvin 77105029d3 fix GridColumnSettings.with() function (don't override width inadvertantly) 2018-09-18 14:48:35 -04:00
Kevin Darby e722e666c2 restricted UI to primitives, enums, and a single child. Works though 2018-09-18 14:47:17 -04:00
John Colvin 5fe0b0c58b SimpleGridSettingsManager - fix immutable col settings flags 2018-09-18 14:28:21 -04:00
Taha Afzal 8730a7def3 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-18 14:11:35 -04:00
Kevin Darby 5fa6bee510 didn't need that converter after all 2018-09-17 23:24:03 -04:00
Kevin Darby 8adf682946 made some progress; need some help on the converter 2018-09-17 23:12:09 -04:00
Kevin Darby b2bf529ed7 starting dyn order templating stuff 2018-09-17 18:15:34 -04:00
Jake Norbut f77f7b091d MView - Fixing Size and Price fg color, disallowing fg color overrides 2018-09-17 15:00:19 -05:00
Kevin Darby 2203086c87 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-17 13:06:57 -04:00
Jake Norbut 4f9111b8a0 MView - now supports GridSettings 2018-09-17 11:10:26 -05:00
Jake Norbut 94cda9a09b TOS: hooking up selection highlight property 2018-09-17 11:08:18 -05:00
Eric Harding 094fc47990 Update CMEConnection::feed_type to string instead of char for "SMBO" type channels. 2018-09-17 10:59:03 -04:00
Taha Afzal 92c1508b8c Put the VolumeAtPrice behind a toggle config 2018-09-14 14:58:35 -04:00
Jake Norbut 466915f6e9 Updating MICS conf to use unquoted enum string rather than enum value 2018-09-14 13:57:37 -05:00
Pedro Pinto 1c68cfa08e Fixed clang warning. 2018-09-13 20:44:28 +00:00
Pedro Pinto cc75872c04 Fixed crashing bug in order plugin. 2018-09-13 20:28:48 +00:00
Pedro Pinto 7e93866596 Tweaks to metrics tools. 2018-09-13 20:28:48 +00:00
Taha Afzal f4213eefe9 fixing header inclusions 2018-09-13 16:26:06 -04:00
John Colvin 29afa7f3b1 create SimpleGridSettingsManager/VM (no sound-triggers) 2018-09-13 15:42:57 -04:00
Taha Afzal 72bfafb376 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-09-13 15:24:58 -04:00
Taha Afzal 5dbacfddd5 SPARK-1672(Volume at price) 2018-09-13 15:24:49 -04:00
Pedro Pinto e29ea5d89b SPARK-1514, Server side SDK code complete. 2018-09-12 18:43:17 +00:00
Eric Harding 45283593ce SPARK-1710 Add good test coverage for read_combined() 2018-09-12 13:03:43 -04:00
Eric Harding 00d91353a4 SPARK-1710 add read_direct/read_implied/read_merged accessors for market data 2018-09-11 17:27:15 -04:00
Eric Harding 2885d929a8 Safe me from my own mistyping 2018-09-11 15:19:10 -04:00
Eric Harding a3e5bc4fba Prune unused script from tools 2018-09-11 12:50:20 -04:00
Eric Harding 7944338516 add test watching script (linux) 2018-09-11 16:15:39 +00:00
Taha Afzal bb19f737d5 SPARK-1672 hooked up the timer for polling 2018-09-10 22:28:48 -04:00
John Colvin 77854a3cf0 SPARK-1714 Ladder account - unable to pick a selection on server restart 2018-09-10 18:56:41 -04:00
Taha Afzal d29ec26beb SPARK-1672 scaffolding 2018-09-10 15:11:18 -04:00
Michael Harding d64810e234 Merged in SPARK-1678 (pull request #72)
SPARK-1678
2018-09-10 17:44:43 +00:00
Taha Afzal f3d2c9e5b9 SPARK-1672 VAP initial check in 2018-09-10 12:39:22 -04:00
Eric Harding 63ac084a17 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-10 11:24:37 -04:00
Eric Harding 29f7c24a00 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-10 10:40:33 -04:00
Taha Afzal 58c298fed8 Changing the AdminService -> OrderService call to async 2018-09-10 10:18:05 -04:00
Taha Afzal ee825f76c7 SPARK-1696 completed 2018-09-09 21:27:54 -04:00
Taha Afzal dfb33efd45 SPARK-1696 util functions added 2018-09-09 11:00:51 -04:00
Taha Afzal dd7ebc54a2 Permissions functions in AminServiceUtils 2018-09-07 19:22:32 -04:00
Kevin Darby 8f99d8d9ca Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-09-07 17:37:57 -04:00
Kevin Darby c411a0c792 Merge branch 'order-def-templates' of bitbucket.org:bluetradesys/spark 2018-09-07 17:37:17 -04:00
Michael Harding bc8513778b Move Account controls into tab contents 2018-09-07 17:33:20 -04:00
Michael Harding 501dc37e94 F# type system is less resilient than it thinks it is 2018-09-07 17:17:42 -04:00
Michael Harding c58b5fb0e6 SPARK-1678: Cancel orders for account 2018-09-07 17:10:19 -04:00
Michael Harding f89f2b8ad0 Remove Beta from SparkAdmin 2018-09-07 17:10:18 -04:00
Pedro Pinto 5e3e85641d Merged from prod 2018-09-07 21:00:35 +00:00
John Colvin 4a5f71e7f2 fix build 2018-09-07 16:37:23 -04:00
Kevin Darby 92d7503528 cmake? 2018-09-07 16:18:46 -04:00
Kevin Darby 773f3db976 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-07 16:17:47 -04:00
Eric Harding dd5b489e8f SPARK-1659 Update producer api to handle implied markets (push early for emumba) 2018-09-07 09:29:41 -04:00
Taha Afzal 7bda1cc1cc Permissions proto and service member added 2018-09-06 21:51:10 -04:00
Eric Harding 58e88812b1 SPARK-1693 - Code quota reached. Have to delete some. 2018-09-06 13:54:02 -04:00
Eric Harding db80631901 Fix "some projects had trouble loading" in vscode 2018-09-06 12:21:05 -04:00
Taha Afzal 593944266d merging 2018-09-06 10:47:29 -04:00
Taha Afzal b8883fc8ae SPARK-1668 reporting stream is done 2018-09-06 10:31:33 -04:00
Taha Afzal efac4a6077 SPARK-1668 foundation 2018-09-06 10:31:33 -04:00
Jake Norbut cb22d21f70 Merged parsing_mics into master 2018-09-06 07:57:15 -05:00
Jake Norbut f6b17d9541 Merge branch 'master' of bitbucket.org:bluetradesys/spark into parsing_mics 2018-09-06 07:56:18 -05:00
Pedro Pinto 7a1298e015 Merge from QA 2018-09-05 18:36:49 -04:00
Jake Norbut 0a81521e6e changing to Taha's proto, not int32, silly man 2018-09-05 13:56:27 -05:00
Jake Norbut 8771281954 Updating script to parse and dump mics 2018-09-05 13:26:50 -05:00
Jake Norbut b50381d386 adding an unset enum state to CMEMics 2018-09-05 12:38:12 -05:00
Jake Norbut 91345cf9d2 renaming cpp_config proto and conf to cme_reference_data 2018-09-05 12:37:25 -05:00
Taha Afzal 19b3ba6615 SPARK-1668 MIC enums added 2018-09-05 12:41:03 -04:00
Taha Afzal 6b68029b6d adding comments to SPARK-1679 protos 2018-09-05 10:03:38 -04:00
Taha Afzal 15c8a8bf28 SPARK-1679 done ! 2018-09-05 09:55:05 -04:00
Taha Afzal 2b2fe02315 SPARK-1679 get_order_summary done 2018-09-04 22:13:25 -04:00
Taha Afzal 1f4c91a344 SPARK-1679 2018-09-04 10:37:45 -04:00
Taha Afzal 92f8f74d8f SPARK-1679 2018-09-04 08:51:50 -04:00
John Colvin ad2869ea6f OrderPropertiesEditor - use id and order-type to match selected orderdef in inlineprops 2018-09-02 13:06:29 -04:00
John Colvin 979506d5be OrderTemplateKey/Header - override ToString 2018-09-02 12:38:13 -04:00
John Colvin bc6e792855 SPARK-1684 ladder/ticket - infrastructure for order definition templates 2018-09-01 21:25:20 -04:00
Eric Harding 5df90f27c8 Update onixs license 2018-08-29 10:47:07 -04:00
Pedro Pinto d571642454 Removed test code from SDK example 2018-08-28 10:21:16 -04:00
Pedro Pinto 0695ddc33e Moved subrepo ptr 2018-08-28 13:24:08 +00:00
Pedro Pinto c97e7675fd Testing SDK 2018-08-28 13:20:56 +00:00
Pedro Pinto 526805680a Updated cmake files 2018-08-24 18:14:24 +00:00
Pedro Pinto 03e3549895 Merged 2018-08-24 13:56:35 -04:00
Pedro Pinto ebbc48771b Merge 2018-08-19 13:26:56 -04:00
Pedro Pinto 2d6fb5c15c cleaned up build 2018-08-19 17:24:40 +00:00
815 changed files with 37043 additions and 15015 deletions

No files matched your search

+4
View File
@@ -24,5 +24,9 @@ project.lock.json
src/server/sparkd/orderman/perf_analysis/data
src/server/sparkd/orderman/perf_analysis/logs/
src/server/sparkd/orderman/perf_analysis/trace_data/
.fake
*.fsx.lock
src/lib/orderman/perf_analysis/logs/latency_data/
trace_data/
/src/lib/orderman/perf_analysis/last_pretty_log.csv
/src/lib/orderman/perf_analysis/log.csv
+3
View File
@@ -30,6 +30,7 @@
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
@@ -50,6 +51,7 @@
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
@@ -74,6 +76,7 @@
"${workspaceRoot}/build/ninja_debug/src",
"${workspaceRoot}/externals/windows/include",
"${workspaceRoot}/externals/linux/include",
"${workspaceRoot}/externals/linux/include/tensorflow/contrib/makefile/gen/proto",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/x86_64-linux-gnu/c++/6",
"${workspaceRoot}/externals/windows/vscode/usr/include/c++/6/backward",
+59 -66
View File
@@ -6,7 +6,7 @@
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/build/ninja_debug/src/server/sparkd/sparkd",
"program": "${workspaceRoot}/build/ninja_debug/server/sparkd/sparkd",
"args": ["run"],
"stopAtEntry": false,
"cwd": "${workspaceRoot}/build/ninja_debug/src/server/sparkd",
@@ -36,13 +36,24 @@
"name": "SDK Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
"args": ["md","GLBX","Future","ZN", "ZNU8"],
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
"args": ["md","GLBX","Future","CL", "CLZ9", "--user=bts", "--pass="],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
},
{
"name": "Excel Launch",
"type": "clr",
"request": "launch",
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
"args": ["${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Tests",
"type": "clr",
@@ -54,7 +65,7 @@
"externalConsole": true,
"requireExactSource": false
},
{
{
"name": "Client Attach",
"type": "clr",
"request": "attach",
@@ -64,10 +75,10 @@
"name": "SparkVM Launch",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
"args": ["run"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"cwd": "/home/bts/spark/build/ninja_debug/server/",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
@@ -97,6 +108,46 @@
"/home/bts/spark/": "${workspaceRoot}"
}
},
{
"name": "TFTest ",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/sdk/examples/tf_test",
"args": [
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/sdk/examples/",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"preLaunchTask": "",
"logging": {
"engineLogging": false
},
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "enable pretty-printer",
"ignoreFailures": true
},
{
"description": "Skip STL",
"text": "skip -gfi /usr/include/c++/7/bits/*.h",
"ignoreFailures": true
}
],
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
}
},
{
"name": "Spreader Tests",
"type": "cppdbg",
@@ -198,7 +249,7 @@
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test", "[md]"],
"args": ["test","AuthToken"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
@@ -264,63 +315,5 @@
"RedirectOutput"
]
},
{
"name": "edge-asp051",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/sparkd",
"args": ["run"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"preLaunchTask": "",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "enable pretty-printer",
"ignoreFailures": true
}
],
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": ["bts@edge-asp051"],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/bts/": "${workspaceRoot}"
}
},
{
"name": "Docker Launch",
"type": "cppdbg",
"request": "launch",
"program": "/bts/build/gcc_debug/src/server/sparkd/sparkd",
"args": ["test"],
"stopAtEntry": false,
"cwd": "/bts/build/gcc_debug/src/server/sparkd",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"preLaunchTask": "",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "enable pretty-printer",
"ignoreFailures": true
}
],
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": ["-p 2000","root@localhost"],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/bts/": "${workspaceRoot}"
}
}
]
}
+8 -2
View File
@@ -13,7 +13,7 @@ set (SPARK_BIN "${EXTERNALS_ROOT}/bin")
set (SPARK_INCLUDE "${EXTERNALS_ROOT}/include")
set (SPARK_LIB "${EXTERNALS_ROOT}/lib/${CMAKE_BUILD_TYPE}")
include_directories (BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib)
include_directories (BEFORE SYSTEM ${SPARK_INCLUDE} ${EXTERNALS_ROOT}/include/zlib ${TF_INCLUDE})
link_directories (${SPARK_LIB})
if (WIN32)
@@ -41,19 +41,25 @@ else ()
-Wno-missing-braces -Wno-ignored-qualifiers \
--std=c++17 -fdiagnostics-color")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -DNDEBUG -O3")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wno-undef")
add_subdirectory(src/server/sparkd)
add_subdirectory(src/lib)
add_subdirectory(src/web)
add_subdirectory(doc/user)
add_subdirectory(src/sdk)
add_subdirectory(src/data_files)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/server
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/sparkd ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/defaults ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/web ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/ai_order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libtensorflow.so ${CMAKE_BINARY_DIR}/server/plugins
COMMENT "Creating server runtime layout"
DEPENDS sparkd data_files_dir)
+44
View File
@@ -25,3 +25,47 @@
***** FillStore
***** SecuritySrc
* Benchmarks
* OrderMan
** OrderServices
*** callbacks
**** standard
- update
- cancel
- crack
- activate
- book
- settlement
- child order state
- timer
**** low level
- exe reports
- cancel rejects
- gateway status
*** services
**** standard
- secdb
- get time
- schedule actions
- order builder
- activate
- update
- cancel
- crack
**** low level
- governor
- send fix
- get time str
- limiter
** Internalize plan
- change order initiated update/cancel/crack/activate/report_state to
use direct method calls
- need to watch out for reentrancy
- need to also report state externally
- also want to separate active from non active orders in map to speed
up look ups
- want to use direct references in orders to avoid lookups
- want to change how time is captured, the trace is too big to copy
around
+82
View File
@@ -0,0 +1,82 @@
# The BTS Development Process #
## Projects ##
Our development work is organized in projects. A project is a
non-trivial effort to specify, develop and validate a related set of
additions to our Spark platform. A project can be assigned to one more
developers and a developer might concurrently be assigned to multiple
projects. Projects typically start out with specification work,
proceed to implementation and finish with validation efforts. At any
given time there will be multiple projects being worked on
concurrently. A project is concluded when the features it implements
are merged into the product and deployed to customers.
## The Details ##
Typically project ideas come from sales interactions, opposition
research and customer feedback, all tough on occasion development or
operational requirements can also suggest valuable project
ideas. Whatever the source of a project idea the final decision of
which projects to pursue is always done by management. Once a project
idea has been selected for implementation the corresponding project
will go through several phases: inception, specification, development,
validation and merge.
* Inception. In this phase the infrastructure for the project is
created. Typically this means selecting a team, creating a project
case in Jira (that is a case assigned to the team@bluetradesys.com
Jira user) and possibly creating a new branch for the project. This
phase is mostly mechanical and the responsibility of Pedro.
* Specification. The specification phase turns a usually vague and
ill-considered idea project idea into a concrete, implementable
specification that attempts to maximize the value/cost ratio of the
project. It is hard to exaggerate the impact of a this phase on the
rest of the project. *Nothing* has more impact on BTS's ability to
quickly deliver value to customers than good specification work. A
specification typically takes the form of a shared google docs using
a mix of text and UI mock-ups to capture both user requirements and
suggested implementation strategies. Taha and Kevin are responsible for this
phase.
* Development. Once a specification is complete, a team of developers
will use a project specific branch to implement the
specification. Periodically, typically once a week, progress on the
project is assessed by Pedro, possibly with the help of others. This
assessment takes the form of code reviews, demos, or just one-on-one
conversations. To ensure development does not go off on tangents,
developers are expected to continuously merge master into the project
branch as well as encouraged to create Pull Requests (PRs) early in
the project implementation, so that their work is kept visible and
integrated with that of the rest of the group. During the
implementation phase developers should reach out to Pedro for UI
reviews, architecture and design questions and specification trade off
concerns.
* Verification. Once code is complete it is the project's team job to
think carefully about the best way to verify that the work is solid
enough to go to production. We do not have a dedicated QA cycle to
flush out bugs so it is important for changes to be fully tested
before being marked ready to merge (RTM). For any non-trivial change
project teams should enlist a 'test buddy' from outside the team to
help review the code and think of test cases that they may not have
considered. Stability from release to release is especially critical
right now as we develop a reputation in the futures
world. Additionally, project teams are encouraged to make liberal use
of other testing techniques such as
- Unit tests
- System tests
- Manual tests
* Merge. Once a project is considered complete by developers, a PR
that includes Pedro should be created (unless, as mentioned above, an
early PR was already created in the development phase). Whether the PR
already existed or not, its description should be changed to start
with the RTM (Ready To Merge) acronym. This marks the project as ready
to be merged into master. Once a week, typically on Mondays, Pedro
will go through every PR marked as "Ready To Merge", build it, check
it out and if everything looks good merge it to master. At the end of
the day the resulting master will be tagged with a new version
number. This version will be go through some additional testing by
Brian and will then be deployed by Karl and Pete to customers
throughout the rest of the week.
+1
View File
@@ -0,0 +1 @@
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;/home/ppinto/Dropbox/bts/spark/doc/user/mkdocs.yml;bts-help-theme/404.html;bts-help-theme/base.html;bts-help-theme/content.html;bts-help-theme/css/base.css;bts-help-theme/css/bts.css;bts-help-theme/css/font-awesome.min.css;bts-help-theme/css/highlight.css;bts-help-theme/css/local.css;bts-help-theme/fonts/fontawesome-webfont.eot;bts-help-theme/fonts/glyphicons-halflings-regular.eot;bts-help-theme/img/favicon.ico;bts-help-theme/js/base.js;bts-help-theme/js/bootstrap-3.0.3.min.js;bts-help-theme/js/highlight.pack.js;bts-help-theme/js/jquery-1.10.2.min.js;bts-help-theme/keyboard-modal.html;bts-help-theme/main.html;bts-help-theme/nav-sub.html;bts-help-theme/nav.html;bts-help-theme/search-modal.html;bts-help-theme/toc.html;docs/Help.md;docs/Launcher.md;docs/Logging-In-to-the-System.md;docs/Market.md;docs/My-Windows.md;docs/Settings.md;docs/Spreader.md;docs/Trading.md;docs/images/image1.png;docs/images/image10.png;docs/images/image11.png;docs/images/image12.png;docs/images/image13.png;docs/images/image14.png;docs/images/image15.png;docs/images/image16.png;docs/images/image17.png;docs/images/image18.png;docs/images/image19.png;docs/images/image2.png;docs/images/image20.png;docs/images/image21.png;docs/images/image22.png;docs/images/image23.png;docs/images/image24.png;docs/images/image25.png;docs/images/image26.png;docs/images/image27.png;docs/images/image28.png;docs/images/image29.png;docs/images/image3.png;docs/images/image30.png;docs/images/image31.png;docs/images/image32.png;docs/images/image33.png;docs/images/image34.png;docs/images/image35.png;docs/images/image36.png;docs/images/image37.png;docs/images/image38.png;docs/images/image39.png;docs/images/image4.png;docs/images/image40.png;docs/images/image41.png;docs/images/image42.png;docs/images/image43.png;docs/images/image44.png;docs/images/image45.png;docs/images/image46.png;docs/images/image47.png;docs/images/image48.png;docs/images/image49.png;docs/images/image5.png;docs/images/image50.png;docs/images/image51.png;docs/images/image52.png;docs/images/image53.png;docs/images/image54.png;docs/images/image55.png;docs/images/image56.png;docs/images/image57.png;docs/images/image58.png;docs/images/image59.png;docs/images/image6.png;docs/images/image60.png;docs/images/image61.png;docs/images/image62.png;docs/images/image63.png;docs/images/image64.png;docs/images/image65.png;docs/images/image66.png;docs/images/image67.png;docs/images/image68.png;docs/images/image69.png;docs/images/image7.png;docs/images/image70.png;docs/images/image71.png;docs/images/image72.png;docs/images/image73.png;docs/images/image74.png;docs/images/image76.png;docs/images/image8.png;docs/images/image9.png;docs/index.md;docs/release_notes.md)
+31
View File
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.5)
project(doc)
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
include(${CMAKE_SOURCE_DIR}/tools/Protobuf.cmake)
# DEPENDENCIES
set(DOC_GLOBS
*.md
*.jpeg
*.png
*.html
*.css
*.js
*.eot
favicon.ico
)
file (GLOB_RECURSE All_Deps RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${DOC_GLOBS})
set (All_Deps ${All_Deps} ${CMAKE_CURRENT_SOURCE_DIR}/build.sh ${CMAKE_CURRENT_SOURCE_DIR}/mkdocs.yml)
update_deps_file("${All_Deps}")
# END DEPENDENCIES
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/web/docs/index.html
COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/build.sh
ARGS ${CMAKE_BINARY_DIR}/web/docs/
COMMENT "Building Manual UI"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${All_Deps})
add_custom_target(doc_manual_bundle ALL DEPENDS ${CMAKE_BINARY_DIR}/web/docs/index.html)
-1
View File
@@ -26,7 +26,6 @@
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600" rel="stylesheet" />
<link rel="stylesheet" href="{{ base_url }}/css/local.css?1529096332" type="text/css" />
<!--link rel="stylesheet" href="https://www.bluetradingsystems.com/templates/bluetradesys/scripts/colorbox-master/example5/colorbox.css" type="text/css" /-->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
@@ -1,18 +0,0 @@
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url(../fonts/Source-Sans-Pro-300.ttf) format('truetype');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(../fonts/Source-Sans-Pro-400.ttf) format('truetype');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
src: local('Source Sans Pro SemiBold'), local('SourceSansPro-SemiBold'), url(../fonts/Source-Sans-Pro-600.ttf) format('truetype');
}
+2 -3
View File
@@ -30,9 +30,8 @@
{% if nav_item.active%}
<ul>
{%- for toc_item in page.toc %}
<li class="main {% if toc_item.active %}active{% endif %}"><a href="{{ toc_item.url|url }}">{{ toc_item.title }}</a></li>
{%- for toc_item in toc_item.children %}
<li><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>
{%- for toc_subitem in toc_item.children %}
<li><a href="{{ toc_subitem.url }}">{{ toc_subitem.title }}</a></li>
{%- endfor %}
{%- endfor %}
</ul>
+10 -1
View File
@@ -1,2 +1,11 @@
#!/bin/bash
python -m mkdocs build -d $1
tmpfile=$(mktemp /tmp/quiet_build_log.XXXX)
python -m mkdocs build -d $1 >$tmpfile 2>&1
if [ $? -eq 0 ]
then
rm $tmpfile
exit 0
else
cat $tmpfile
exit 1
fi
+4
View File
@@ -1,5 +1,9 @@
#Help
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/yGQfr_puT3w" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
##Report a Problem
If there is any issue at all, you can click here
and it’ll bring up a “Report a Problem” window, which you can type
+6 -2
View File
@@ -1,5 +1,9 @@
#Module Launcher
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/OxVqQBKFIDw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- The module launcher is the first item that will come up after
logging in to Spark.
- This is your central control for all modules
@@ -9,7 +13,7 @@
![](images/image38.png)
- If you right click anywhere on the launcher you can modify
- If you right-click anywhere on the launcher you can modify
various display settings including:
- Vertical layout, instead of horizontal
- Showing the text of the individual modules
@@ -25,5 +29,5 @@
Some common features within BTS Spark
- All Windows are tabbed so they can be combined or separated to organize your workspace
- Some Spark windows have a menu on the left hand side where you can launch related modules or change settings. You can see the menu item names either by hovering your mouse over the icon, or clicking on the right edge of the menu (a blue bar will appear).
- Some Spark windows have a menu on the left-hand side where you can launch related modules or change settings. You can see the menu item names either by hovering your mouse over the icon or clicking on the right edge of the menu (a blue bar will appear).
-  Some table columns have filtering capabilities, where if you hover over the column header, it will display a filter icon, which enables you to filter the table based on the information in that column.
+1 -1
View File
@@ -2,7 +2,7 @@
The shortcut icon will appear on your desktop like below:
![](images/image64.png =45)
![](images/image64.png)
Simply double click or single click and hit enter to launch the
login screen:
+16 -8
View File
@@ -9,6 +9,10 @@
##Market View
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Y54S1uscT_g" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- This provides a grid of outright, spread, and synthetic products
showing real-time market data all in one place
- You can create multiple market view grids and customize the
@@ -29,7 +33,7 @@
and all securities will be displayed.  This will show up in the
selected securities window as “&lt;symbol&gt; future at
&lt;exchange&gt;”
- Synthetic’s can be displayed by clicking on exchange “BTS”, then
- Synthetics can be displayed by clicking on exchange “BTS”, then
synthetic, symbol, and finally under the securities window.
- Below is an example of an outright, spread, all futures, and
synthetic chosen:
@@ -98,10 +102,10 @@
either the Bid or Ask price and volume.
- Highlighting a single security and clicking on the order ticket
![](images/image23.png) icon
on the left hand browser window of the market view
on the left-hand browser window of the market view
- Launch a ladder for 1 security
- You can double click on any security in the market view, besides
- You can double-click on any security in the market view, besides
the Bid Qty, Bid Price, Ask Price, or Ask Qty columns to launch a
ladder for that Security.
@@ -110,7 +114,7 @@
- If you hold down the Ctrl key and select multiple securities, you
can then click on the ladder icon
![](images/image69.png) on
the left hand side to bring up the selected ladders all at
the left-hand side to bring up the selected ladders all at
once.
- Launching a Fills window for 1 or many securities
@@ -118,7 +122,7 @@
- This is done by either highlighting 1 or many securities and then
clicking on the Fills icon
![](images/image20.png) on
the left hand side to bring up a fill window for the selected
the left-hand side to bring up a fill window for the selected
securities.  
- Please note - If there haven’t been any fills for the day in that
@@ -129,14 +133,14 @@
- This is accomplished by highlighting 1 or many securities and then
clicking on the time and sales icon
![](images/image49.png) on
the left hand side
the left-hand side
- Launching the synthetic window to create a synthetic product
- This is accomplished by highlighting either 1 or 2 securities and
clicking on the Synthetic icon
![](images/image34.png) on
the lower left hand side
the lower left-hand side
- Please note - If only 1 security is chosen, a create synthetic
window will appear, and you will be required to add another leg from
@@ -144,7 +148,11 @@
##Time and Sales
- Provides a display of real time fills as they happen for 1 or many
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/nEkSGacjt0s" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- Provides a display of real-time fills as they happen for 1 or many
securities
- You can create multiple time and sale windows and customize the
security selection to fit your needs
+6 -2
View File
@@ -1,5 +1,9 @@
#My Windows
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/pjm1JJ9Q3jM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
This is the housing of the windows that you have manually named and
creates a place where you can quickly and easily launch windows
which you’ve customized.
@@ -18,9 +22,9 @@ section of the module launcher.
![](images/image15.png)
Right clicking on a tab that has already had its default title modified
Right-clicking on a tab that has already had its default title modified
will show the following which will allow you to rename the modified
title or remove it from the my windows, which will then restore the
title or remove it from the My Windows, which will then restore the
default standard title.
![](images/image37.png)
+4
View File
@@ -1,6 +1,10 @@
#Settings
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/yGQfr_puT3w" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
##Workspaces
This is where your workspaces can be saved or switched
+34 -14
View File
@@ -1,14 +1,35 @@
#Spreader
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/XEZpuPAgMC0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/EVFK2yNQFS4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/d5ywlHen6yo" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Mmw8PNJA0Dw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/QK-bBSH6Rc0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- Launching the spreader from the module launcher will bring up the
synthetic management browser which appears like below:
![](images/image35.png)
- From this window it’ll show any synthetics that you have already
- From this window, it will show any synthetics that you have already
created as well as various other options of both managing the
synthetics and other tasks all from the left hand side pane within
synthetics and other tasks all from the left-hand side pane within
the window, including:
- Adding a new synthetic ![](images/image8.png)
@@ -30,7 +51,7 @@
- From the main module launcher, you can go to spreader, then
synthetics, then click add
- Highlighting one or more securities in a market view and clicking
on create synthetic
on Create Synthetic
- Cloning an existing security
- An empty synthetic browser will look like below without any legs
@@ -43,10 +64,10 @@
- Upon adding legs, you can then go through each of the parameters to
customize how you would like the spread to be worked in the
market
- We’ll start at the top of create synthetic window and work our way
- We will start at the top of the Create Synthetic window and work our way
down:
- At the very top of the create synthetic window you will have to
- At the very top of the Create Synthetic window you will have to
name your synthetic product.  By default, it will auto-populate
based on the symbols you have chosen:
@@ -73,16 +94,15 @@
exchange
- Furthermore, if you are ever unsure of what a parameter’s function
is and how it is controlled you can hover over the parameter’s title
and a “?” will be shown.  Clicking on this will expand built in
and a “?” will be shown.  Clicking on this will expand built-in
details of how that parameter functions.  Below is an example of the
throttle details displayed:
![](images/image56.png)
- Main parameters - these are the top 4 rows in the create synthetic
- Main parameters - these are the top 4 rows in the Create Synthetic
window which all synthetics will be required to have set.
 Everything below these are custom to a traders needs, however the
top 4 are the main foundational aspects used for the calculation of
 Everything below these can be customized to a trader's needs, however, the top 4 are the main foundational aspects used for the calculation of
the synthetic:
![](images/image10.png)
@@ -95,7 +115,7 @@
be priced, by default, it’ll be based on the same as the quantity of
the synthetic produced, but you can change it to modify the balance
of the price, giving more weighting to one leg over another.
- Enable quoting - This parameter is in the form of a check box,
- Enable quoting - This parameter is in the form of a checkbox,
where if the box is checked the leg will be quoted versus not
checked, it will not be shown in the market.  If it is not actively
quoted in the market, the leg will still be used for calculation of
@@ -107,7 +127,7 @@
 If your Hedge Ratio is 3 on the NQ leg, then you’ll work the ES
order based on where you could fill 6 (2 (Qty of NQ) \* 3 (Hedge
Ratio)).
- The next 6 parameters are all completely custom to a traders needs
- The next 6 parameters are all completely custom to a trader's needs
and can further define how a synthetic is worked.  As previously
mentioned, if you hover over any of these parameters a “?” will be
displayed and click on such will display further details on how this
@@ -118,7 +138,7 @@
- Quote
- Throttle - This can be enabled or disabled based on if the box is
checked per leg.  It’s main function is to reduce re-quoting updates
checked per leg.  Its main function is to reduce re-quoting updates
by defining a region on either side of the working quote order,
inside which quote updates will not be sent:
@@ -148,8 +168,8 @@
![](images/image70.png)
- Time Based Cross - Basically the exact same as ticks away cross,
however this is based on time (in ms) on when to automatically cross
- Time Based Cross - Basically the same as Ticks Away Cross,
however, this is based on time (in ms) on when to automatically cross
(fill) the market if they get too far away from the opposite inside
market.  If a legged hedge order’s price gets X amount of “ticks”
away from the opposite inside market, it is re-priced to cross the
+57 -50
View File
@@ -9,7 +9,11 @@
##Order Book
- This provides list of all orders a user has placed, by default it
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/ygMFkjBBGeU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- This provides a list of all orders a user has placed, by default it
will display only working orders
- You can create multiple order books filtered however you would
like
@@ -44,39 +48,39 @@
- Anytime
- Today
- Since Open
- A specific user defined period of time
- A specific user-defined period of time
![](images/image25.png)
- The left hand box within an order book provides users with further
- The left-hand box within an order book provides users with further
functionality to quickly perform various tasks with live orders as
well as launch other modules.  It’s appears like the below:
well as launch other modules.  It appears like the below:
![](images/image4.png)
- ![](images/image48.png)][Fills -
- ![](images/image48.png)Fills -
clicking on this will launch a fills window
- ![](images/image57.png)][ Modify -
- ![](images/image57.png) Modify -
clicking on this will launch an order ticket for a live order
highlighted in the Order Book.
- ![](images/image73.png)][Cancel
- ![](images/image73.png)Cancel
selected - clicking on this will cancel the selected orders you have
highlighted in the order book.  You can choose more than one by
holding hold the Ctrl key when click on each row of orders
- ![](images/image74.png)][Cancel
holding hold the Ctrl key when clicking on each row of orders
- ![](images/image74.png)Cancel
buys - clicking on this will cancel all the buy orders you have in
the order book
- ![](images/image33.png)][Cancel
- ![](images/image33.png)Cancel
sells - clicking on this will cancel all the sell orders you have in
the order book
- ![](images/image2.png)][Cancel
- ![](images/image2.png)Cancel
all - clicking on this will cancel all working orders you have the
order book
##Fills
- This provides list of all fills a user has had for a given time
period, by default it will open up with “since open” time frame
- This provides a list of all fills a user has had for a given time
period, by default it will open up with “since open” timeframe
selected showing you fills on the day thus far
- You can create multiple fill windows, filtered however you
like
@@ -85,8 +89,12 @@
##P&amp;L
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/xj6cEQ6By34" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- This provides a running ledger of a users account(s) profit and
loss broken down per symbol
loss, broken down per symbol
- You can create multiple P&L windows, filtered however you would
like
- All P&L windows are brought up as tabs and can be combined with all
@@ -101,6 +109,10 @@
##Ladder
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Q0qsyc1nvYo" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- The ladder was designed to be the most logically produced ladder on
the market, engineered and designed by professional traders for
professional traders
@@ -112,8 +124,8 @@
![](images/image36.png)
- The ladder consist of 2 major parts, the Ladder itself and the
information and control section on the left hand side
- The ladder consists of 2 major parts, the Ladder itself and the
information and control section on the left-hand side
- The information section on the left contains the following: &nbsp; ![](images/image1.png)
- Day - showing the current price of the security on the day
@@ -126,17 +138,17 @@
- The control section right underneath the information section on the
left hand side offers the following:
left-hand side offers the following:
![](images/image62.png)
- Account in which the orders will flow into when trading this
- The account in which the orders will flow into when trading this
security
- Length of time the order will remain in market, either DAY or
- Length of time the order will remain in the market, either DAY or
GTC
- Order type, by default orders will go in as Limit, however all of
- Order type, by default, orders will go in as Limit, however all of
the following are offered out of the box.  The top 5 are all fairly
standard order types, however the bottom 5 are all specialized
standard order types, however, the bottom 5 are all specialized
algos.  They are only offered by BTS and require specialized
parameters.  Please see below for each order type:
@@ -147,8 +159,8 @@
- Limit
- This is a buy or sell order designed to take place on the exact
price level you have entered the order at.
- This is a buy or sell order designed to take place on the
price level where you entered the order.
- Stop
@@ -168,10 +180,10 @@
- Iceberg
- This is a buy or sell order where the order is divided into a
visible or “show” portion which is seen at the exchange.  ][The
visible or “show” portion which is seen at the exchange.  The
rest of the order is hidden and will remain hidden until the show
quantity is filled, essentially reloading the order back to the show
quantity.  You will need to ][define what type of order that will
quantity.  You will need to define what type of order that will
be used for the child order to be sent.
![](images/image47.png)
@@ -198,7 +210,7 @@
         
![](images/image50.png)
- Tick
- Lean
- This is a specialized algo order which allows the user to
essentially watch the opposite-inside market size and cross when
@@ -207,16 +219,9 @@
buying into the asks.  This is often referred to as “paying up” as
well
![](images/image22.png)
![](images/image22.png?v2)
- Lean
- This is a specialized algo order which allows the user to enter an
order which will watch the opposite-inside market size and cross
when that size goes below the threshold defined in the
parameters
![](images/image45.png)
- Join
@@ -225,8 +230,7 @@
market flips and has at least threshold size as defined.
- “Flip” refers to the event where the best offer becomes the best
bid or the market trades away from you, i.e. the line moves up in
the GUI.  This is all true in the reverse way as well if
selling.
the GUI.  The reverse is true if selling.
![](images/image41.png)
@@ -248,7 +252,7 @@
- You can manually type in a quantity here as well
- Furthermore, if you have a value in the volume bar, you can
increase this size by right clicking and the value will increase
increase this size by right-clicking and the value will increase
incrementally by 1.  The same is true if you would like to decrease
this value by left click and the value will decrease.
- By having the auto clear box checked, it will bring the value back
@@ -259,12 +263,12 @@
-  The bottom section of the control area of a ladder has 4 aspects
to it
- Cancel Buys - which will cancel all buys currently working on the
- Cancel Buys, which will cancel all buys currently working on the
ladder
- Cancel Sells - which will cancel all sells currently working on the
- Cancel Sells, which will cancel all sells currently working on the
ladder
- Cancel All - which will cancel all orders on the ladder
- Settings - this will launch the ladder settings window which
- Cancel All, which will cancel all orders on the ladder
- Settings, which will launch the ladder settings window which
appears like below
![](images/image71.png)
@@ -276,27 +280,26 @@
- There are 5 main columns
- Orders - which display current orders working in the market
- Bid - which displays the current bid volumes at each price
- Orders, which display current orders working in the market
- Bid, which displays the current bid volumes at each price
level
- Price - which displays the price levels of the symbol shown
- Ask - which displays the current ask volumes at each price
- Price, which displays the price levels of the symbol shown
- Ask, which displays the current ask volumes at each price
level
- Last Trade - which displays where the price level and volume of
- Last Trade, which displays the price level and volume
where the last trade occurred
- Functionality of the ladder
- To enter an order directly by clicking in the ladder, left click on
either the bid or ask volume with a volume greater than 0 on the
predefined value box on the left hand section
predefined value box on the left-hand section
- A badge will be displayed within the orders column of the value you
have chosen and the quantity filled as displayed below
![](images/image46.png)
- To change the price level you wish to work the order, you can right
click and hold on the badge to move it up or down the ladder for the
- To change the price level you wish to work the order, you can right-click and hold on the badge to move it up or down the ladder for the
desired price
- To cancel the order, left click on the badge
- Furthermore, you can click down on the scroll wheel of your mouse
@@ -305,6 +308,10 @@
## Order Ticket
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/bdC5pZ6T35I" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
- The order ticket was designed to provide another means other than
the ladder to enter orders into the market
- You can bring up an order ticket directly from the module launcher,
@@ -314,7 +321,7 @@
![](images/image27.png)
- On the top left you have the price for the order as well as the
- On the top left, you have the price for the order as well as the
volume
- Beneath are the predefined volumes as well as a clear button to
clear out the volume
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+1 -35
View File
@@ -1,37 +1,3 @@
#Getting Started
If you are new to BTS Spark we recommend starting with these pages to learn the basics.
Click on the headings on the left to expand their contents. If you need specific information quickly use the Search button in the top right of this page.
If you need specific information quickly use the Search button in the top right of the navigation of use the table of contents at the left.
<ol>
<li><a href="Logging-In-to-the-System">Login</a></li>
<li><a href="Launcher">Module Launcher</a></li>
<li><a href="Market">Market</a>
<ul>
<li><a href="Market/#market-view">Market View</a></li>
<li><a href="Market/#time-and-sales">Time and Sales</a></li>
</ul>
</li>
<li><a href="Trading">Trading</a>
<ul>
<li><a href="Trading/#order-book">Order Book</a></li>
<li><a href="Trading/#fills">Fills</a></li>
<li><a href="Trading/#pwzxhzdk0l">P&amp;L</a></li>
<li><a href="Trading/#ladder">Ladder</a></li>
<li><a href="Trading/#order-ticket">Order Ticket</a></li>
</ul>
</li>
<li><a href="Spreader">Spreader</a></li>
<li><a href="My-Windows">My Windows</a></li>
<li><a href="Settings">Settings</a>
<ul><li><a href="Settings/#workspaces">Workspaces</a></li></ul>
</li>
<li><a href="Help">Help</a>
<ul>
<li><a href="Help/#report-a-problem">Report a Problem</a></li>
<li><a href="Help/#about-spark">About Spark</a></li>
<li><a href="Help/#activity-log">Activity Log</a></li>
</ul>
</li>
</ol>
+273
View File
@@ -0,0 +1,273 @@
# Release Notes
## 1.5.0
### User Visible Changes
#### User Interface Improvements
- Removed security key field from most grids
- Removed trade key field from time and sales grid
- Fixed the "Since Open" time filter to use the exchange open time rather than 8:30 AM
- Fixed the floating order book so that rows are no longer
highlighted after an order is modified
#### SDK
- Improved SDK documentation and usability
### Internal Changes
- Improved robustness of limit system test
- Extensive market data refactoring allow for security definition updates
## 1.4.11
### New Features
- Floating Order Book
- The floating order book provides a quick view of orders occupying a price level in the ladder
- Launch the Floating Order Book by middle-clicking a ladder badge
- Cancel orders by clicking on the red X in the Cancel column
- Quantity and Price modification
- Clicking on a row will select it, providing the option for changing your price and quantity
- Enter in a new value or use the up/down buttons to set a desired value
- Press the green checkbox to finalize your change and update the order
### SDK
- Improved SDK documentation and usability
### User Interface Improvements
- The following spreader quoting parameters have been deprecated:
- Hidden parameter
- Price Type setting of the Conditional parameter
### Bug Fixes
- Fixed a limit enforcement problem that could lead to 0 sized orders being sent to the exchange
## 1.4.10
### Bug Fixes
- Removed duplicate entries in performance log
## 1.4.9
### SDK
- Added ticker events to the order API
### Performance Improvements
- Significantly improved hedging speed
### Bug Fixes
- Fixed a visual oddity in the order book UI when using High DPI displays
## 1.4.8
### Marketdata
- Additional trade details for CME ticker
## 1.4.7
### Bug Fixes
- Corrected FIX tag 1028, ManualOrderIndicator, when orders are triggered by algos
## 1.4.6
### Ladder Usability Improvements
- The ladder can now be zoomed in/out according to a user's preference
- The zoom slider can be enabled from Ladder Form -> Settings button -> Left Pane tab -> check Zoom Slider checkbox
- The zoom functionality can also be performed via hotkey and mouse wheel combinations
- Zoom In = CTRL + Mouse wheel up
- Zoom Out = CTRL + Mouse wheel down
- Reset zoom = CTRL + Mouse wheel click or double click on the zoom slider
### Exchange simulator
- Improved the exchange simulator to track real market data more closely
### Bug Fixes
- Fixed a UI crash in some cases when attempting to sort the order book by time
## 1.4.5
- Fixed entitlement validation for ICS spreads
- Tuned performance logging
## 1.4.4
### Performance Improvements
- Reduced performance logging activity
## 1.4.3
### Ladder Usability Improvements
- Controls found in Left Pane of the ladder can now be hidden via the checkboxes found under Ladder Form -> Settings button -> Left Pane tab
- The Left Pane is now scrollable with the mouse wheel when there isn't enough vertical space to accomodate the entire form
- The Left Pane can now grown and shrunk horizontally via the splitter between the left pane and the main ladder area
### Performance Improvements
- Requoting and hedging latency has been reduced by up to 30%
### Software Development Kit (SDK) Improvements
- The API now enforces authentication
### Bug Fixes
- The Market View form now honors filters when new securities are added
## 1.4.2
### User Interface Improvements
- When creating a synthetic from the Market View window, securities are now added to the Create Synthetic dialog in the same sort order as they appear in grid
### Bug Fixes
- Shell no longer crashes when removing a security from the synthetics window
- It is now possible to create a new synthetic using the same name as a previously deleted synthetic
### SDK Improvements
- Improved Client and Server SDK clarity and documentation
### Performance Improvements
- Synthetic calculation speed was improved by 400%.
### Risk / Entitlement System
- Risk Admin UI
- Spark users now have separate Trading, RiskAdmin, and/or MDAdmin permissions
- Spark users can now only add other users or accounts if permissioned to do so.
- Market Data Entitlement
- Automated market data reporting
## 1.4.1
### Tabs
- Tabs now shrink and expand to make better use of horizontal space
### Bug Fixes
- Fixed a bug that could cause excessive CPU usage when a connection broke
- Fixed a layout bug in hedging/requoting performance reports
- Fixed account name mismatching bug for old fills
### Absolute Position limits
- Positions can now be netted or absolute for purposes of limit enforcing
## 1.4.0
### Speed Enhancements
- Improved requoting and hedging performance
- Improved order book form performance
### Server Side SDK
- Added a beta version of the server side SDK
### Front End
- Added status bars to all UI grids
### Reporting
- Added reporting of hedging and requoting performance
## 1.3.8
### Speed Enhancements
- Further speed enhancements
### Conditional (new quoting parameter)
- Reduces re-quoting updates by only quoting if N ticks away from the opposite inside market.
- Quotes are canceled when it gets further away.
- "Price Type" dictates if this logic applies to the quoting market or to the synthetic spread market.
## 1.3.7
### Net Change pricing
- The ability to price synthetics based off of net change
## 1.3.5
### Speed Enhancements
- Further speed enhancements
### Separation of implied and direct market data
- Viewing within a market view will now show 3 columns
- Direct
- Implied
- Merged
- Ability to configure the market data size in a ladder as the following
- Direct
- Implied
- Merged
### Synthetic creation option to choose Merged or Direct
- The ability to choose Merged or Direct per leg when creating a synthetic
### Volume at price
- Within a ladder, you can now see a volume at price graph
- This is configurable from a ladder's settings as well
### Customized settings in all windows
- All windows now contain a settings page where grid settings (font color, size, etc) are applied
- These settings can be saved on a per-window basis or as a Default for newly-launched windows
## 1.3.1 Through 1.3.4
### Speed Enhancements
- Further speed enhancements
## 1.2
### Market View
- Synthetic future leg working orders are now shown
### Order Book
- Many visual facets of the Order Book grid can now be customized
- The Order Book settings page can be accessed from the gears icon in the Order Book toolbar or from the right-click menu
- The Expand/Collapse All button has been separated into two different buttons on the toolbar
### Sound Alerts
- Sounds alerts can now be configured for various order events
- Configure sounds under the Sound/Notification Triggers on the Order Book settings page
### Fills
- Newly launched fill windows will filter out start-of-day records by default
### PnL
- Prices are now formatted with penny precision
- Day PnL has been added to the Totals footer at the bottom of the window
### Ladder
- Order badges now have been improved to communicate a variety of order state
- Background color now indicates order side
- Child orders will display a one-character abbreviation to their parent order enclosed in a rectangle; the rectangle will have a fill color matching the parent order
- Border color around badge will match the associated algo color
- Exchange orders will present with a solid dot in the badge's top-right corner
- When multiple orders reside at the same level, a solid triangle will be present in badge's bottom-right corner; all other badge decorations will not be visible in this state
- Hedge orders will present with a vertical line inside the badge's left border
- Trigger orders, once triggered, will no longer be shown in the ladder
- Session High and Low visual indicators have been added to the Price column
- Session range is now indicated in the Price column
- A button reflecting position has been added above the quantity field
- This button reflects the position for the currently selected account
- Pressing the button will copy the position size to the quantity field
- Order quantity button size values are now customizable
- Default size preset values can now be changed, as well as added or removed
- This can be configured per ladder instance (i.e. per security) by making use of the Custom tab
- Synthetic ladders that were saved as part of a workspace are now restored on startup
### Synthetics
- Hedge Ratio parameter has been renamed to Lean Qty Factor
- Spreader now includes a Hidden mode parameter
- A quoting leg can be hidden, ie an order will not be sent out until the market crosses the computed quoting price. When that happens, the quote is sent out as an IOC order.
### Algos
- Trigger orders now can contain a Trigger Exit parameter based on time or price; once the exit condition has been met the Trigger order will cancel
- IOC/FAK order type added
- Cover order type added
- Canceling a synthetic order no longer cancels any working hedge orders
### General
- A manual page has been added to the launcher Help menu
- Various help buttons and tooltips were added throughout the application
- Various cash per point values updated
- Time-based grid filters now have tick up/down buttons
- Numerous performance improvements and stability enhancements
- Automated position and Order Book maintenance during daily recycle
- Individual fills will be compacted into the Start of Day position records
- Orders from the day before the recycle will be deleted
##1.1
###1.1.22
- Order Book: Orders in Error status can now be recreated
- Order Book: Orders containing child orders will now be collapsed by default.
- Order Book: Expand / Collapse All button added to the right-click menu and window action pane, denoted by ▲ icon
- Order Book: Symbol column added to allow for filtering based on group symbol
- Time filters now have a quick Set Last Hour hyperlink which will set the Period Begin filter to (Now - 1 hour)
###1.1.20
- Update CME market data handler for price precision extension.
https://www.cmegroup.com/confluence/display/EPICSANDBOX/Price+Precision+Extension
###1.1.8
- Enabled wild cards in risk management UI
- Fixed spreader order updates
###1.1.7
- Fixed spread leg price fill
- Fixed HO cash per point value
###1.1.6
- Fixed occasional crash when filtering fills
- Fixed several cash per point multipliers
- Colored time of sales row with aggressor side
- Iceberg orders now cancel (rather than an error) when a child is canceled
###1.1.0
- Added per user self match protection, location and self match protection FIX tag settings
- Implemented a new "Trigger Order" algo
- Corrected tick formatting for a number of products
- Added the ability to recreate canceled orders
- Added leg fills to facilitate futures based PnL
- PnL report now has PnL columns calculated based on your Start of Day, current day, and total position
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
- Corrected cash per point values, used in pnl calculations, for a number of products
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
-61
View File
@@ -1,61 +0,0 @@
# 1.2
### Market View
- Synthetic future leg working orders are now shown
### Order Book
- Many visual facets of the Order Book grid can now be customized
- The Order Book settings page can be access from the gears icon in the Order Book toolbar or from the right-click menu
- The Expand/Collapse All button has been separated into two different buttons on the toolbar
### Sound Alerts
- Sounds alerts can now be configured for various order events
- Configure sounds under the Sound/Notification Triggers on the Order Book settings page
### Fills
- Newly launched fill windows will filter out start-of-day records by default
### PnL
- Prices are now formatted with penny precision
- Day PnL has been added to the Totals footer at the bottom of the window
### Ladder
- Order badges now have been improved to communicate a variety of order state
- Background color now indicates order side
- Child orders will display a one-character abbreviation to their parent order enclosed in a rectangle; the rectangle will have a fill color matching the parent order
- Border color around badge will match the associated algo color
- Exchange orders will present with a solid dot in the badge's top-right corner
- When multiple orders reside at the same level, a solid triangle will be present in badge's bottom-right corner; all other badge decorations will not be visible in this state
- Hedge orders will present with a vertical line inside the badge's left border
- Trigger orders, once triggered, will no longer be shown in the ladder
- Session High and Low visual indicators have been added to the Price column
- Session range is now indicated in the Price column
- A button reflecting position has been added above the quantity field
- This button reflects the position for the currently selected account
- Pressing the button will copy the position size to the quantity field
- Order quantity button size values are now customizable
- Default size preset values can now be changed, as well as added or removed
- This can be configured per ladder instance (i.e. per security) by making use of the Custom tab
- Synthetic ladders that were saved as part of a workspace are now restored on startup
### Synthetics
- Hedge Ratio parameter has been renamed to Lean Qty Factor
- Spreader now includes a Hidden mode parameter
- A quoting leg can be hidden, ie an order will not sent out until the market crosses the computed quoting price. When that happens, the quote is sent out as an IOC order.
### Algos
- Trigger orders now can contain a Trigger Exit parameter based on time or price; once the exit condition has been met the Trigger order will cancel
- IOC/FAK order type added
- Cover order type added
- Cancelling a synthetic order no longer cancels any working hedge orders
### General
- A manual page has been added to the launcher Help menu
- Various help buttons and tooltips were added throughout the application
- Various cash per point values updated
- Time-based grid filters now have tick up/down buttons
- Numerous performance improvements and stability enhancements
- Automated position and orderbook maintenance during daily recycle
- Individual fills be compacted into start of day position records
- Orders from the day before the recycle will be deleted
+10
View File
@@ -1,3 +1,13 @@
1.4.2
- Be sure the system has a bts user before deployment. bts is the superuser, he has all permissions and can see all market data
- After deployment re-permission all existing users for trading, riskadmin, and/or md admin permissions as appropriate
- Also note which users can add other users and accounts and permission them appropriately (Can Add?)
- Permission users for appropriate market data entitlements; this has monetary consequences so ask Kevin if you aren't sure.
- MIC reporting needs to be added to audit Jenkins job (https://bluetradesys.atlassian.net/browse/SPARK-1900)
1.3.5
- Change in market data wire format. Old shell will get only direct market data instead of merged which may not match what the servers is driving from.
1.3.4
- Changed default logging config for Windows. If Logging config is overridden for a system it should be adjusted to override only linux.
-56
View File
@@ -1,56 +0,0 @@
1.3.4
- Significantly reduced client memory use
- Improved OrderBook performance
- Improved workspace load time
1.3.3
- Reduced server memory usage
1.3.2
- Improved OrderBook performance
- Fix some memory leaks when switching workspaces
1.3.1
- Improved server side logging
- Fixed leaking of market data subscriptions on market view form closing
1.3.0
- Significantly improved order execution latency
- Added latency monitoring tools
- Provided core binding controls to system admins so as to reduce latency jitter
1.1.22
- Orderbook: Orders in Error status can now be recreated
- Orderbook: Orders containing child orders will now be collapsed by default.
- Orderbook: Expand / Collapse All button added to the right-click menu and window action pane, denoted by ▲ icon
- Orderbook: Symbol column added to allow for filtering based on group symbol
- Time filters now have a quick Set Last Hour hyperlink which will set the Period Begin filter to (Now - 1 hour)
1.1.20
- Update CME market data handler for price precision extension.
https://www.cmegroup.com/confluence/display/EPICSANDBOX/Price+Precision+Extension
1.1.8
- Enabled wild cards in risk management UI
- Fixed spreader order updates
1.1.7
- Fixed spread leg price fill
- Fixed HO cash per point value
1.1.6
- Fixed occasional crash when filtering fills
- Fixed several cash per point multipliers
- Colored time of sales row with aggressor side
- Iceberg orders now cancel (rathern than error) when a child is canceled
1.1.0
- Added per user self match protection, location and self match protection FIX tag settings
- Implemented a new "Trigger Order" algo
- Corrected tick formatting for a number of products
- Added the hability to recreate canceled orders
- Added leg fills to facilitate futured based PnL
- PnL report now has PnL columns calculated based on your start of day, current day, and total position
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
- Corrected cash per point values, used in pnl calculations, for a number of products
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
+27
View File
@@ -0,0 +1,27 @@
{
"folders": [
{
"path": "."
},
{
"uri": "sftp://bts:BT5u53r@sparkvm:22/home/bts/",
"name": "sparkvm"
}
],
"settings": {
"files.associations": {
"utility": "cpp",
"initializer_list": "cpp",
"list": "cpp",
"type_traits": "cpp",
"vector": "cpp",
"xhash": "cpp",
"xstring": "cpp",
"system_error": "cpp",
"array": "cpp",
"string_view": "cpp",
"xutility": "cpp",
"*.ipp": "cpp"
}
}
}
+77
View File
@@ -0,0 +1,77 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.associations": {
"utility": "cpp",
"initializer_list": "cpp",
"list": "cpp",
"type_traits": "cpp",
"vector": "cpp",
"xhash": "cpp",
"xstring": "cpp",
"system_error": "cpp",
"array": "cpp",
"string_view": "cpp",
"xutility": "cpp",
"*.ipp": "cpp",
"atomic": "cpp",
"hash_map": "cpp",
"hash_set": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"cfenv": "cpp",
"chrono": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"exception": "cpp",
"any": "cpp",
"optional": "cpp",
"fstream": "cpp",
"functional": "cpp",
"future": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"ostream": "cpp",
"numeric": "cpp",
"ratio": "cpp",
"scoped_allocator": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"tuple": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp"
}
}
}
+81 -55
View File
@@ -4,7 +4,7 @@ VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{3335AAF0-489D-359D-8305-9B7A44E2C760}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
@@ -12,15 +12,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sparkcmd", "src\client\spar
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{9AD1A867-93A9-301D-BF77-0113E18E9E39}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{2256015B-E7D5-3614-8F39-814CA68A4444}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
ProjectSection(ProjectDependencies) = postProject
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3} = {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}
{3335AAF0-489D-359D-8305-9B7A44E2C760} = {3335AAF0-489D-359D-8305-9B7A44E2C760}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{ECD78CC5-138F-4809-A764-2358C2AC2E82}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_excel", "src\client\spark_excel\spark_excel.csproj", "{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -57,22 +59,22 @@ Global
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.Build.0 = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|Any CPU.ActiveCfg = Debug|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x64.ActiveCfg = Debug|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x64.Build.0 = Debug|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x86.ActiveCfg = Debug|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|Any CPU.ActiveCfg = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x64.ActiveCfg = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x64.Build.0 = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x86.ActiveCfg = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.Debug|Any CPU.ActiveCfg = Debug|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.Debug|x64.ActiveCfg = Debug|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.Debug|x64.Build.0 = Debug|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.Debug|x86.ActiveCfg = Debug|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.Release|Any CPU.ActiveCfg = Release|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.Release|x64.ActiveCfg = Release|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.Release|x64.Build.0 = Release|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.Release|x86.ActiveCfg = Release|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{3335AAF0-489D-359D-8305-9B7A44E2C760}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|Any CPU.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.Build.0 = Debug|x64
@@ -93,26 +95,30 @@ Global
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x64.Build.0 = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x86.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|Any CPU.ActiveCfg = Debug|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.ActiveCfg = Debug|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.Build.0 = Debug|x64
@@ -133,22 +139,22 @@ Global
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.Build.0 = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|Any CPU.ActiveCfg = Debug|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x64.ActiveCfg = Debug|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x64.Build.0 = Debug|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x86.ActiveCfg = Debug|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|Any CPU.ActiveCfg = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x64.ActiveCfg = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x64.Build.0 = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x86.ActiveCfg = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|Any CPU.ActiveCfg = Debug|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x64.ActiveCfg = Debug|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x64.Build.0 = Debug|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Debug|x86.ActiveCfg = Debug|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|Any CPU.ActiveCfg = Release|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x64.ActiveCfg = Release|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x64.Build.0 = Release|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.Release|x86.ActiveCfg = Release|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{2256015B-E7D5-3614-8F39-814CA68A4444}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|Any CPU.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.Build.0 = Debug|x64
@@ -193,6 +199,26 @@ Global
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.Debug|Any CPU.ActiveCfg = Debug|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.Debug|x64.ActiveCfg = Debug|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.Debug|x64.Build.0 = Debug|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.Debug|x86.ActiveCfg = Debug|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.MinSizeRel|Any CPU.Build.0 = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.MinSizeRel|x64.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.MinSizeRel|x64.Build.0 = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.MinSizeRel|x86.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.MinSizeRel|x86.Build.0 = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.Release|Any CPU.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.Release|x64.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.Release|x64.Build.0 = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.Release|x86.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
{BE9F27C3-B583-47DF-95FA-1F8BBC89DCCB}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+9 -1
View File
@@ -31,6 +31,9 @@
<AvailableItemName Include="Proto3">
<Targets>CompileProto3</Targets>
</AvailableItemName>
<AvailableItemName Include="Proto3Excel">
<Targets>CompileProto3Excel</Targets>
</AvailableItemName>
</ItemGroup>
<PropertyGroup Label="ProtoBuf">
@@ -48,9 +51,14 @@
<OutCS>%(OutBase)%(FileName).cs</OutCS>
<OutCSClient>%(OutBase)%(FileName).client.pb.cs</OutCSClient>
<OutCSServer>%(OutBase)%(FileName).server.pb.cs</OutCSServer>
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
<OutCSImmutable>%(OutBase)%(FileName).immutable.pb.cs</OutCSImmutable>
<CompileOptions></CompileOptions>
</Proto3>
<Proto3Excel>
<OutBase>$(IntermediateOutputPath)\gen\%(relativedir)</OutBase>
<OutCSExcel>%(OutBase)%(FileName).excel.pb.cs</OutCSExcel>
<CompileOptions></CompileOptions>
</Proto3Excel>
</ItemDefinitionGroup>
</Project>
+26
View File
@@ -57,12 +57,38 @@
</ItemGroup>
</Target>
<Target Name="CompileProto3Excel" AfterTargets="GetAssemblyVersion"
Condition="'@(Proto3Excel)'!=''"
Inputs="$(protoc);
%(Proto3Excel.Identity);$(ProtocPluginBase)\plugin.py;
$(ProtocPluginBase)\templates\excel\excel.pb.cs;"
Outputs="%(Proto3Excel.OutCSExcel)">
<MakeDir Directories="%(Proto3Excel.OutBase)" />
<Message Importance="High" Text="Generating excel service %(Proto3Excel.FileName)" />
<Exec Command="
set PATH=$(spark_build_python_path);%path% &amp;
set PYTHONPATH=$(Externals)\python &amp;
$(protoc) ^
%(Proto3Excel.FullPath) ^
--error_format=msvs ^
--proto_path=$(protoinc);$(SourceRoot) ^
--plugin=&quot;protoc-gen-service=$(ProtocPluginBase)\plugin.cmd&quot; ^
--service_out=lang=excel,strip_out_path=1:%(Proto3Excel.OutBase) ^
"
WorkingDirectory="%(relativedir)" />
<ItemGroup>
<Compile Include="%(Proto3Excel.OutCSExcel)" />
</ItemGroup>
</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="%(Proto3Excel.OutCSExcel)" />
</Target>
<Target Name="Test" DependsOnTargets="Build">
+4 -3
View File
@@ -6,6 +6,7 @@ using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using bts.api.core;
namespace mbts.spark {
[Export]
@@ -19,14 +20,14 @@ namespace mbts.spark {
IReadOnlyDictionary<uint, string>
_account_map = new Dictionary<uint, string>(),
_user_map = new Dictionary<uint, string>();
readonly BehaviorSubject<AdminModelWrapper> _wrapper_subject = new BehaviorSubject<AdminModelWrapper>(null);
readonly BehaviorSubject<AdminModelWrapper> _wrapper_subject = new BehaviorSubject<AdminModelWrapper>(new AdminModelWrapper());
IDisposable _dispose;
[ImportingConstructor]
AdminModel(AdminService service, [Import("username")]string user_name) {
_user_name = user_name;
_dispose = service.subscribei_admin_data().Select(update => {
_wrapper_subject.OnNext(new AdminModelWrapper(update.all.FirstOrDefault(), _user_name));
_wrapper_subject.OnNext(new AdminModelWrapper(update.all.TryFirst(), _user_name));
IEnumerable<BTSUserImmutable> active_users =
from admin_data in update.all
from bts_user in admin_data.Users
@@ -36,7 +37,7 @@ namespace mbts.spark {
IEnumerable<BTSAccountImmutable> active_accounts =
from admin_data in update.all
from bts_account in admin_data.Accounts
where bts_account.IsPresent
where bts_account.IsPresent && _wrapper_subject.Value.can_view_account(bts_account.Name)
join account_id in (
from bts_user in active_users
from account_id in bts_user.UserData.AccountPermissions.AccountPermissions.Keys
@@ -12,11 +12,13 @@ namespace bts {
[Export, Export(typeof(IShellDaemon))]
class DeleteSyntheticDaemon : IShellDaemon, IDisposable {
readonly OrdersModel _orders_model;
readonly ErrorReporter _error_reporter;
IDisposable _dispose;
[ImportingConstructor]
public DeleteSyntheticDaemon(IBus bus, OrdersModel orders_model) {
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);
@@ -28,6 +30,7 @@ namespace bts {
async void _delete_synthetic(uint id) {
var (orders, orders_success) = await _orders_model.on_orders.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.Def.SecurityKey == id && o.is_non_terminal())) {
Log.Ui.warn($"Ignoring request to delete synthetic security ({id}) because non-terminal-orders were detected.");
+15 -45
View File
@@ -13,35 +13,34 @@ using System.Linq;
using SecurityKey = System.UInt32;
using System.Collections.Concurrent;
using System.Threading;
using bts.api.user;
namespace bts {
[Export(typeof(IInitializable))]
class MDSource : IObserver<List<SecurityAndHandle>>, IInitializable, IDisposable {
class MDDaemon : bts.md.MDSource, IObserver<List<SecurityAndHandle>>, IInitializable, IDisposable {
readonly Lazy<IBus> _bus;
readonly Lazy<AuthTokenImmutable> _token;
readonly Lazy<SyntheticSecurityService> _ss;
readonly MDConsumer _consumer = new MDConsumer();
readonly Co2RelayControl _relay;
readonly Dictionary<SecurityKey, SecurityMD> _deleted_synthetics = new Dictionary<SecurityKey, SecurityMD>();
long _version = 0;
readonly ConcurrentDictionary<SecurityKey, SecurityMD> _exchange_sec_map = new ConcurrentDictionary<SecurityKey, SecurityMD>();
List<IDisposable> _dispose = new List<IDisposable>();
static uint get_naggle() => mbts.config.Configuration.get<bts.md.RelayOptions>().naggle_ms;
[ImportingConstructor]
public MDSource(Lazy<IBus> bus, Lazy<SyntheticSecurityService> ss) {
public MDDaemon(Lazy<IBus> bus, Lazy<AuthTokenImmutable> token, Lazy<SyntheticSecurityService> ss) : base(get_naggle(), true) {
_bus = bus;
_ss = ss;
_relay = new Co2RelayControl(_consumer);
_relay.preload_securities((co2.MDRequest.Types.Flags)0xff).ignore();
_dispose.Add(_consumer.on_security.Subscribe(this));
_token = token;
_ss = ss;
}
public void initialize() {
override public void initialize() {
_relay.set_token(_token.Value);
_dispose.Add(
_ss.Value.subscribei_metadata()
.Select(upd => upd.all.ToDictionary(i => i.SecurityKey, i => i.Strategy))
.Subscribe(t => {
var deleted_synthetic_keys = new HashSet<uint>(t.Where(kv => kv.Value.Deleted).Select(kv => kv.Key));
// add back 'undeleted' synthetics
foreach (var kvp in _deleted_synthetics) {
foreach (var kvp in _deleted_synthetics.ToList()) {
if (!deleted_synthetic_keys.Contains(kvp.Key)) {
_exchange_sec_map.TryAdd(kvp.Key, kvp.Value);
_deleted_synthetics.Remove(kvp.Key);
@@ -58,6 +57,9 @@ namespace bts {
// Post full dictionary of syntheics here
bus.post(SparkTags.SyntheticstrategiesTag, t);
}));
_dispose.Add( this.on_securities.Subscribe( (md) => {
bus.post(SparkTags.SecuritiesTag, md);
}));
Task.Run(run);
}
@@ -66,37 +68,5 @@ namespace bts {
public int priority => 55;
IBus bus => _bus.Value;
public void OnCompleted() { }
public void OnError(Exception error) => Log.Md.error(error.ToString());
public void OnNext(List<SecurityAndHandle> values) {
if (values.Count > 0) {
foreach (var v in values) {
var smd = new SecurityMD(v.security, v.handle);
_exchange_sec_map.AddOrUpdate(smd.security.Id, smd, (_, __) => smd);
}
_version += 1;
}
}
public void Dispose() => Disposable.dispose(_dispose);
async Task run() {
long run_version = 0;
while (true) {
var exch = _exchange_sec_map;
long current_version = _version;
if (current_version > run_version) {
run_version = current_version;
try {
Log.Md.info($"MDSource publishing {exch.Count} securities");
bus.post(SparkTags.SecuritiesTag, exch);
} catch (Exception e) {
Log.Md.error($"Error in publish_securities: {e}");
}
}
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
}
}
+62 -2
View File
@@ -1,11 +1,13 @@
using bts.api.core;
using bts.metrics;
using mbts;
using mbts.config;
using mbts.ui;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
@@ -18,6 +20,7 @@ namespace bts.perf {
readonly string _version;
readonly string _username;
IDisposable _subs;
Ping _pinger = new Ping();
[ImportingConstructor]
public ShellLatencyDaemon(ShellLatencyService service, [Import("username")] string user) {
@@ -25,7 +28,12 @@ namespace bts.perf {
_version = Configuration.get<bts.config.System>().version;
_username = user;
_hostname = Environment.MachineName;
_subs = _service.subscribei_clock().Subscribe(on_tick);
_forever_ping_host(_pinger);
_subs = Disposable.Create(
_pinger,
_service.subscribei_clock().Subscribe(on_tick)
);
}
void on_tick(Update<TickImmutable, int> update) {
@@ -40,8 +48,60 @@ namespace bts.perf {
}
}
async void _forever_ping_host(Ping pinger) {
var config = Configuration.get<bts.config.ShellPingOptions>();
string server_ip = Configuration.get<bts.config.System>().config_ip;
int delay = config.workloop_milliseconds_interval;
int _current_missed_ping_count = 0;
if (config.shell_ping_is_enabled && pinger != null) {
while (true) {
try {
await Task.Delay(delay);
var response = await pinger.SendPingAsync(server_ip, config.timeout_milliseconds_threshold);
if (response.Status == IPStatus.Success) {
_current_missed_ping_count = 0;
if (response.RoundtripTime > config.logging_milliseconds_threshold) {
Log.Ui.warn($"Ping response not received in a timely manner:\n" +
$"\tAddress:\t{response.Address}\n" +
$"\tRoundTrip:\t{response.RoundtripTime} ms");
}
// potentially reset delay to initial work period (no-op otherwise)
delay = config.workloop_milliseconds_interval;
} else {
_current_missed_ping_count++;
delay = _get_exp_delay(_current_missed_ping_count, config.workloop_milliseconds_interval, config.maximum_backoff_milliseconds);
Log.Ui.warn($"Ping was not successful. Ping response: {response.Status}. Missed ping count: {_current_missed_ping_count}. Backing off for {delay} milliseconds.");
}
} catch (PingException e) {
_current_missed_ping_count++;
delay = _get_exp_delay(_current_missed_ping_count, config.workloop_milliseconds_interval, config.maximum_backoff_milliseconds);
Log.Ui.error($"Ping exception. {e}\nBacking off for {delay} milliseconds.");
}
} // end while
}
// since we're returning milliseconds here up to a 1 minute delay
// we don't need to go crazy with the power, else we will get
// an ArgumentOutOfRangeExecption.
// Int.MaxValue = 2^31, so we need to be below that
// Also, even if we're under the 2^31 int.maxvalue, we don't want to be multed over it
// so that is why we have checked() here.
int _get_exp_delay(int retry_num, int min_interval, int max_delay) {
// add some jitter up to half the max delay so we don't
// have a ping spike from all clients at once
var jitter_amount = new Random().Next(max_delay / 2);
int power_factor = (int)Math.Pow(2, Math.Min(retry_num, 32) - 1);
try {
return Math.Min(checked(min_interval * power_factor + jitter_amount), max_delay + jitter_amount);
} catch (OverflowException) {
return max_delay + jitter_amount;
}
}
}
public void Dispose() {
throw new NotImplementedException();
Disposable.dispose(ref _subs);
}
}
}
+5 -4
View File
@@ -1,5 +1,6 @@
using bts.api.admin;
using bts.api.core;
using bts.api.md.auxiliary;
using bts.api.orderman;
using bts.api.persistence;
using bts.api.position;
@@ -12,10 +13,7 @@ using System.ComponentModel.Composition;
namespace Spark {
public class ShellServices {
[Export]
public static ServiceContext context { get; } = new ServiceContext();
[Export]
public UserService user_service => context.locate<UserService>();
public ServiceContext context { get; } = new ServiceContext();
[Export("settings")]
public PersistenceService settings_service => context.locate<PersistenceService>("settings");
@@ -40,5 +38,8 @@ namespace Spark {
[Export]
public PositionService position_service => context.locate<PositionService>();
[Export]
public MarketDataAuxiliaryService md_auxiliary_service => context.locate<MarketDataAuxiliaryService>();
}
}
+14 -6
View File
@@ -20,7 +20,7 @@ namespace mbts.ui {
static readonly string base_uri = "pack://application:,,,/Images/Icons/";
private static ResourceDictionary _resources = (ResourceDictionary)Application.LoadComponent(new Uri("/Images/Vector/Icons.xaml", UriKind.RelativeOrAbsolute));
private static Uri uri(string filename) {
return new Uri(base_uri + filename);
}
@@ -31,7 +31,9 @@ namespace mbts.ui {
}
private static ImageSource load_resource_and_freeze(string resource_name) {
return ((DrawingImage)_resources[resource_name]).frozen();
lock (_resources) {
return ((DrawingImage)_resources[resource_name]).frozen();
}
}
#pragma warning disable CS0618 // Type or member is obsolete
@@ -94,12 +96,12 @@ namespace mbts.ui {
public static ImageSource CancelBuys { get { return load_resource_and_freeze("CancelBuys"); } }
public static ImageSource CancelSells { get { return load_resource_and_freeze("CancelSells"); } }
public static ImageSource CancelSelected { get { return load_resource_and_freeze("CancelSelected"); } }
public static ImageSource LadderCancelOrders { get { return load_resource_and_freeze("LadderCancelOrders"); } }
public static ImageSource LadderCancelBuys { get { return load_resource_and_freeze("LadderCancelBuys"); } }
public static ImageSource LadderCancelSells { get { return load_resource_and_freeze("LadderCancelSells"); } }
public static ImageSource Synthetics { get { return load_resource_and_freeze("Synthetics"); } }
public static ImageSource Synthetics { get { return load_resource_and_freeze("Synthetics"); } }
public static ImageSource AddSynthetic { get { return load_resource_and_freeze("AddSynthetic"); } }
public static ImageSource RemoveSynthetic { get { return load_resource_and_freeze("RemoveSynthetic"); } }
public static ImageSource ModifySynthetic { get { return load_resource_and_freeze("ModifySynthetic"); } }
@@ -126,7 +128,10 @@ namespace mbts.ui {
public static ImageSource Wrench { get { return load_resource_and_freeze("Wrench"); } }
public static ImageSource RightArrow { get { return (load_resource_and_freeze("RightArrow")); } }
public static ImageSource DownArrow { get { return (load_resource_and_freeze("DownArrow")); } }
public static ImageSource Support => load_resource_and_freeze("SupportIcon");
public static ImageSource Factory { get { return (load_resource_and_freeze("Factory")); } }
public static ImageSource DotsVertical { get { return (load_resource_and_freeze("DotsVertical")); } }
public static ImageSource HandPointingOverDocument => load_resource_and_freeze("HandPointingOverDocument");
public static ImageSource Support => load_resource_and_freeze("SupportIcon");␍
public static ImageSource create_icon(string geometric_path_key) =>
create_icon(geometric_path_key, Brushes.Black);
@@ -149,7 +154,10 @@ namespace mbts.ui {
}
static IEnumerable<string> _get_geometric_path(string geometric_path_key) {
object obj = _resources[geometric_path_key];
object obj;
lock (_resources) {
obj = _resources[geometric_path_key];
}
if (obj is string path) {
if (string.IsNullOrEmpty(path)) {
Log.Api.error($"Resource lookup of '{geometric_path_key}' did not return a valid string.", nameof(geometric_path_key));
+41 -1
View File
@@ -813,8 +813,48 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="Factory" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Geometry="M21.832001,24.424L21.832001,27.912998 26.218018,27.912998 26.218018,24.424z M13.757019,24.424L13.757019,27.912998 18.143005,27.912998 18.143005,24.424z M5.7820129,24.424L5.7820129,27.912998 10.167999,27.912998 10.167999,24.424z M1.8950195,6.3799964L3.8880005,6.3799964 4.1870117,17.745001 8.1750183,17.745001 8.1750183,13.956 16.050018,17.745001 16.149994,17.745001 16.149994,13.956 24.024994,17.745001 24.125,17.745001 24.125,13.956 32,17.745001 31.901001,17.745001 31.901001,31.103001 0,31.103001 0,17.745001 1.5950012,17.745001z M8.5740051,1.3959956C8.5740051,1.3959958 9.1719971,3.2899927 6.6800232,3.5889963 6.6800232,3.5889963 5.085022,3.6879953 4.2870178,4.4859993 4.2870178,4.4859993 3.9880066,4.7849952 3.8880005,4.4859993 3.8880005,4.4859993 3.9880066,3.0899958 6.480011,2.6919931 6.5800171,2.7909998 8.3740234,2.6919931 8.5740051,1.3959956z M5.5830078,0C5.5830078,-1.4470834E-07 6.8789978,1.4949948 4.5859985,2.6919931 4.5859985,2.6919931 3.1900024,3.2899927 2.6920166,4.3859931 2.6920166,4.3859931 2.5920105,4.7849952 2.2929993,4.4859993 2.2929993,4.4859993 1.8950195,3.1899942 4.0880127,1.8939967 4.1870117,1.9939953 5.882019,1.2959973 5.5830078,0z"
/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<!-- Pin
<DrawingImage x:Key="DotsVertical" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Geometry="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"
/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="HandPointingOverDocument" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Geometry="M17.740002,11.999007C17.254002,11.999007,16.859001,12.394008,16.859001,12.879008L16.859001,24.458791 16.860127,24.479386C16.861003,24.607261 16.813003,24.735511 16.716002,24.834012 16.667502,24.883012 16.611378,24.91995 16.551394,24.944762L16.521562,24.955436 16.507633,24.960536 16.506465,24.960837 16.50574,24.961097 16.49194,24.964574 16.459728,24.972863C16.42719,24.979527,16.393502,24.983027,16.359002,24.983027L16.316763,24.980893 16.315716,24.980851 16.307903,24.980445C16.223895,24.971904,16.146014,24.942538,16.07951,24.897597L16.067752,24.887895 16.047276,24.873078 16.015234,24.844558 16.005502,24.836528 16.001236,24.831357 13.737002,22.600011C13.267003,22.134011 12.502003,22.135011 12.031003,22.600011 11.805003,22.82301 11.680003,23.120011 11.680003,23.436011 11.680003,23.751012 11.805003,24.048011 12.031003,24.272012L16.842003,29.031013C19.311003,31.189013 21.546002,31.119014 22.656003,30.886014 23.939002,30.614013 25.015002,29.846013 25.464002,28.880013 25.857001,28.036012 26.301002,27.080012 26.263001,26.195012 26.262002,26.178012 26.268003,26.074012 26.269002,26.056012L26.268003,20.974011C26.268003,20.55601 25.922002,20.215011 25.496001,20.215011 25.335502,20.215011 25.239001,20.246511 25.179877,20.283135L25.144,20.309762 25.144,21.215013C25.144,21.491012 24.919999,21.715013 24.644,21.715013 24.367999,21.715013 24.144,21.491012 24.144,21.215013L24.144,19.278017C24.144,18.793018 23.749,18.399019 23.263,18.399019 22.808312,18.399019 22.432727,18.745308 22.387555,19.18822L22.383004,19.277944 22.383004,19.716007C22.383004,19.992006 22.159003,20.216004 21.883002,20.216004 21.607003,20.216004 21.383002,19.992006 21.383002,19.716007L21.383002,19.278054 21.383002,18.504013C21.383002,18.018016 20.988002,17.623017 20.502002,17.623017 20.016002,17.623017 19.621001,18.018016 19.621001,18.504013L19.621001,20.24402C19.621001,20.52102 19.397001,20.74402 19.121001,20.74402 18.844001,20.74402 18.621001,20.52102 18.621001,20.24402L18.621001,18.504035 18.621001,12.879008C18.621001,12.394008,18.226001,11.999007,17.740002,11.999007z M17.740002,10.999005C18.777001,10.999005,19.621001,11.843007,19.621001,12.879008L19.621001,16.842839 19.687315,16.808824C19.933955,16.689783 20.210345,16.623022 20.502002,16.623022 21.247346,16.623022 21.892987,17.059031 22.197202,17.689331L22.211675,17.721288 22.212884,17.720384C22.513,17.517568 22.874499,17.399021 23.263,17.399021 24.267594,17.399021 25.091062,18.190155 25.141547,19.181478L25.143222,19.247425 25.174827,19.241074C25.276639,19.223799 25.383876,19.21501 25.496001,19.21501 26.473002,19.21501 27.268003,20.00401 27.268003,20.974011L27.268003,26.087012C27.268003,26.098012 27.262002,26.194013 27.262002,26.205012 27.311002,27.281013 26.790002,28.401013 26.371001,29.302012 25.783002,30.566012 24.472001,31.523013 22.863002,31.864013 22.513001,31.938014 22.067002,31.999013 21.547002,31.999013 20.149003,31.999013 18.216002,31.561013 16.161002,29.763013L11.328003,24.983012C10.910003,24.570012 10.680003,24.020011 10.680003,23.436011 10.680003,22.85101 10.910003,22.302011 11.328003,21.88901 12.187003,21.040011 13.583003,21.041011 14.440002,21.88901L15.859002,23.287391 15.859002,12.879008C15.859002,11.843007,16.703002,10.999005,17.740002,10.999005z M6.8659983,4.0500388L19.083997,4.0500388C20.668996,4.0500388,21.957997,5.3460296,21.957997,6.9390478L21.957997,14.653042C21.957997,14.93005 21.734998,15.153042 21.457997,15.153042 21.181996,15.153042 20.957997,14.93005 20.957997,14.653042L20.957997,6.9390478C20.957997,5.8980318,20.116998,5.0500394,19.083997,5.0500394L6.8659983,5.0500394C5.8319979,5.0500394,4.9909983,5.8980318,4.9909984,6.9390478L4.9909984,26.116061C4.9909983,27.157046,5.8319979,28.00504,6.8659983,28.00504L11.696996,28.00504C11.972997,28.00504 12.196996,28.229039 12.196996,28.50504 12.196996,28.781041 11.972997,29.00504 11.696996,29.00504L6.8659983,29.00504C5.2809954,29.00504,3.9909983,27.70905,3.9909983,26.116061L3.9909983,6.9390478C3.9909983,5.3460296,5.2809954,4.0500388,6.8659983,4.0500388z M6.4360008,0L17.467001,0C17.743001,0 17.967001,0.22300029 17.967001,0.50000024 17.967001,0.77600098 17.743001,1.0000012 17.467001,1.0000012L6.4360008,1.0000012C3.4380004,1.0000012,1.0000002,3.4210042,1.0000002,6.3970079L1.0000002,20.617026C1.0000002,20.893025 0.77600026,21.117026 0.50000048,21.117026 0.22400022,21.117026 2.3841858E-07,20.893025 0,20.617026L0,6.3970079C2.3841858E-07,2.8700035,2.8870004,0,6.4360008,0z"
/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<!-- Pin
<Path Data="F1M3,11L3,7 0,7 0,6 1,6 1,1 1,0 6,0 6,1 6,6 7,6 7,7 4,7 4,11 3,11z M2,1L2,6 4,6 4,1 2,1z" />
Unpin
<Path Data="F1M0,3L4,3 4,0 5,0 5,1 10,1 11,1 11,6 10,6 5,6 5,7 4,7 4,4 0,4 0,3z M10,2L5,2 5,4 10,4 10,2z" />
@@ -0,0 +1,33 @@
using bts.api.core;
using bts.api.md.auxiliary;
using mbts.utils;
using System;
using System.ComponentModel.Composition;
using System.Reactive.Concurrency;
namespace mbts.spark {
[Export]
class MarketDataAuxiliaryModel : IDisposable {
readonly MarketDataAuxiliaryService _service;
readonly TableUpdateDemuxer<VolumeAtPriceLevelImmutable, int, uint> _security_orders_demuxer;
IDisposable _dispose;
[ImportingConstructor]
MarketDataAuxiliaryModel(MarketDataAuxiliaryService service) {
_service = service;
var scheduler = ThreadPoolScheduler.Instance;
var interval = TimeSpan.FromMilliseconds(100);
volume_at_price = service.subscribei_volume_at_price().Naggle(interval, scheduler).Table().Cache();
_security_orders_demuxer = new TableUpdateDemuxer<VolumeAtPriceLevelImmutable, int, uint>(o => o.RecordId, v => v.SecurityId);
_dispose = volume_at_price.AddRemoveOnly().Subscribe(_security_orders_demuxer);
}
public IObservable<TableUpdate<VolumeAtPriceLevelImmutable, int>> volume_at_price { get; }
public IObservable<ITableUpdateTransform<VolumeAtPriceLevelImmutable, int>> security_volume_at_price(uint security) =>
_security_orders_demuxer.subscribe(security);
public void Dispose() =>
Disposable.dispose(ref _dispose);
}
}
@@ -2,6 +2,7 @@
using bts.spark;
using mbts;
using mbts.co2;
using mbts.persist;
using mbts.spark;
using mbts.ui;
using System.Collections.Generic;
@@ -17,9 +18,9 @@ namespace bts.shell
class MarketShellModule : IShellModule
{
[ImportingConstructor]
public MarketShellModule(IBus bus, MDConsumer consumer, OrdersModel orders_model, [Import("username")]string user_name) {
_tsf = new TimeAndSalesWindowFactory(this, bus, consumer);
_mvf = new MarketViewWindowFactory(this, bus, orders_model);
public MarketShellModule(IBus bus, MDConsumer consumer, UserSettingsHub settings_hub, OrdersModel orders_model, [Import("username")]string user_name) {
_tsf = new TimeAndSalesWindowFactory(this, bus, consumer, settings_hub);
_mvf = new MarketViewWindowFactory(this, bus, settings_hub, orders_model);
}
TimeAndSalesWindowFactory _tsf;
@@ -5,11 +5,9 @@ using mbts.collections;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using bts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -18,7 +16,6 @@ using System.Linq;
using System.Reactive.Linq;
using System.Windows.Input;
namespace mbts.spark {
partial class MarketViewVM {
public class Row : SecurityMDRow, IDisposable, IEquatable<Row>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
@@ -29,7 +26,6 @@ namespace mbts.spark {
readonly ObservableFilterCollection<OrderRecordImmutable> _working_orders, _working_bids, _working_asks;
readonly string _security_text, _security_key_text;
readonly Property<double> _bid_price, _ask_price, _last_price, _day_change, _settlement, _session_high, _session_low;
readonly Property<uint> _bid_size, _ask_size;
readonly CellProperty<int> _position;
readonly CellProperty<uint> _last_size;
readonly CellProperty<ulong> _volume;
@@ -38,6 +34,9 @@ namespace mbts.spark {
readonly CellProperty<double> _working_best_bid, _working_best_ask;
readonly CellProperty<string> _working_bids_text, _working_asks_text;
readonly MyOrderSummarizer _order_summarizer = new MyOrderSummarizer();
readonly MDDisplayHelper.SizeFormatter _size_formatter = MDDisplayHelper.get_formatter(MarketSizeDisplay.get_default());
Property<uint> _bid_direct_size, _bid_merged_size, _ask_direct_size, _ask_merged_size;
string _bid_merged_size_text, _ask_merged_size_text;
ulong _bbo_ref, _last_ref;
public Row(IBus bus, SecurityMD smd, OrdersModel orders_model) : base(smd) {
@@ -46,37 +45,44 @@ namespace mbts.spark {
_security_text = smd.security.ToString();
_security_key_text = security_key.ToString();
string format_price(double price) => security.ContractSpec.format(price);
_position = new CellProperty<int>(0);
_position = new CellProperty<int>(this, nameof(position), 0);
_bid_price = backing(nameof(bid_price), double.NaN);
_ask_price = backing(nameof(ask_price), double.NaN);
_bid_size = backing<uint>(nameof(bid_size), 0);
_ask_size = backing<uint>(nameof(ask_size), 0);
_last_price = backing(nameof(last_price), double.NaN);
_last_size = new CellProperty<uint>(0);
_last_size = new CellProperty<uint>(this, nameof(last_size), 0);
_day_change = backing(nameof(day_change), double.NaN);
_session_low = backing(nameof(session_low), double.NaN);
_session_high = backing(nameof(session_high), double.NaN);
_settlement = backing(nameof(settlement), double.NaN);
_volume = new CellProperty<ulong>(0);
_inside_bids = new CellProperty<bool>();
_inside_asks = new CellProperty<bool>();
_working_bid_volume = new CellProperty<int>();
_working_ask_volume = new CellProperty<int>();
_volume = new CellProperty<ulong>(this, nameof(volume), 0);
_inside_bids = new CellProperty<bool>(this, nameof(inside_bids));
_inside_asks = new CellProperty<bool>(this, nameof(inside_asks));
_working_bid_volume = new CellProperty<int>(this, nameof(working_bid_volume));
_working_ask_volume = new CellProperty<int>(this, nameof(working_ask_volume));
//
_bid_direct_size = backing(nameof(bid_direct_size), default(uint));
_bid_merged_size = backing(nameof(bid_merged_size), default(uint));
_ask_direct_size = backing(nameof(ask_direct_size), default(uint));
_ask_merged_size = backing(nameof(ask_merged_size), default(uint));
//
_working_orders = new ObservableFilterCollection<OrderRecordImmutable>(_orders, o => o.is_active());
_working_bids = new ObservableFilterCollection<OrderRecordImmutable>(_working_orders, o => o.Def.Side == Side.Buy);
_working_asks = new ObservableFilterCollection<OrderRecordImmutable>(_working_orders, o => o.Def.Side == Side.Sell);
_working_best_bid = new CellProperty<double>(format_price, double.NaN);
_working_best_ask = new CellProperty<double>(format_price, double.NaN);
_working_bids_text = new CellProperty<string>(string.Empty);
_working_asks_text = new CellProperty<string>(string.Empty);
_working_best_bid = new CellProperty<double>(this, nameof(working_best_bid), format_price, double.NaN);
_working_best_ask = new CellProperty<double>(this, nameof(working_best_ask), format_price, double.NaN);
_working_bids_text = new CellProperty<string>(this, nameof(working_bids_text), string.Empty);
_working_asks_text = new CellProperty<string>(this, nameof(working_asks_text), string.Empty);
//
default_command = new DelegateCommand(_show_ladder);
bid_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Bid));
bid_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidSize));
bid_merged_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidMergedSize));
bid_direct_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidDirectSize));
bid_implied_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.BidImpliedSize));
working_bids_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingBids));
ask_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Ask));
ask_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskSize));
ask_merged_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskMergedSize));
ask_direct_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskDirectSize));
ask_implied_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskImpliedSize));
working_asks_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingAsks));
_animation.AnimationsChanged += on_animations_changed;
void on_animations_changed() => RaisePropertyChanged(nameof(animations));
@@ -99,9 +105,15 @@ namespace mbts.spark {
public double last_price => _last_price.Value;
public uint last_size => _last_size.value;
public double bid_price => _bid_price.Value;
public uint bid_size => _bid_size.Value;
public uint bid_direct_size => _bid_direct_size.Value;
public uint bid_implied_size => _bid_merged_size.Value - _bid_direct_size.Value;
public uint bid_merged_size => _bid_merged_size.Value;
public string bid_merged_size_text => _bid_merged_size_text ?? (_bid_merged_size_text = _size_formatter(_bid_merged_size.Value, bid_implied_size));
public double ask_price => _ask_price.Value;
public uint ask_size => _ask_size.Value;
public uint ask_direct_size => _ask_direct_size.Value;
public uint ask_implied_size => _ask_merged_size.Value - _ask_direct_size.Value;
public uint ask_merged_size => _ask_merged_size.Value;
public string ask_merged_size_text => _ask_merged_size_text ?? (_ask_merged_size_text = _size_formatter(_ask_merged_size.Value, ask_implied_size));
public ulong volume => _volume.value;
public double settlement => _settlement.Value;
public double day_change => _day_change.Value;
@@ -115,10 +127,14 @@ namespace mbts.spark {
public double working_best_ask => _working_best_ask.value;
public IList<ui.spark.CellAnimation> animations => _animation.get_animations().ToList();
public ICommand bid_price_click { get; }
public ICommand bid_size_click { get; }
public ICommand bid_merged_size_click { get; }
public ICommand bid_direct_size_click { get; }
public ICommand bid_implied_size_click { get; }
public ICommand working_bids_click { get; }
public ICommand ask_price_click { get; }
public ICommand ask_size_click { get; }
public ICommand ask_merged_size_click { get; }
public ICommand ask_direct_size_click { get; }
public ICommand ask_implied_size_click { get; }
public ICommand working_asks_click { get; }
public ICommand default_command { get; }
@@ -133,9 +149,9 @@ namespace mbts.spark {
bool dirty = false;
if (md.md.get_bbo(ref _bbo_ref, out NativePriceSize b, out NativePriceSize a)) {
_bid_price.set(b.price);
_bid_size.set(b.size);
refresh_size(ref b, _bid_direct_size, _bid_merged_size, ref _bid_merged_size_text, nameof(bid_merged_size_text));
_ask_price.set(a.price);
_ask_size.set(a.size);
refresh_size(ref a, _ask_direct_size, _ask_merged_size, ref _ask_merged_size_text, nameof(ask_merged_size_text));
_inside_bids.on_next_distinct(_bid_price.Value >= _working_best_bid.value, ref dirty);
_inside_asks.on_next_distinct(_ask_price.Value <= _working_best_ask.value, ref dirty);
}
@@ -150,6 +166,16 @@ namespace mbts.spark {
_day_change.set(last_price - settlement);
if (dirty)
invalidated?.Invoke();
void refresh_size(ref NativePriceSize nps, Property<uint> direct, Property<uint> merged, ref string text, string text_prop) {
if (nps.size != merged.Value || nps.direct_size != direct.Value) {
merged.set(nps.size);
direct.set(nps.direct_size);
text = null;
dirty = true;
RaisePropertyChanged(text_prop);
}
}
}
public bool Equals(Row other) =>
@@ -184,20 +210,28 @@ namespace mbts.spark {
var order = _orders_model.factory.order(security);
switch (col) {
case MarketViewView.Columns.Bid:
order.Side = Side.Sell;
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, 0u));
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, 0u));
break;
case MarketViewView.Columns.BidSize:
order.Side = Side.Sell;
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_size));
case MarketViewView.Columns.BidMergedSize:
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_merged_size));
break;
case MarketViewView.Columns.BidDirectSize:
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_direct_size));
break;
case MarketViewView.Columns.BidImpliedSize:
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_implied_size));
break;
case MarketViewView.Columns.Ask:
order.Side = Side.Buy;
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, 0u));
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Buy), ask_price, 0u));
break;
case MarketViewView.Columns.AskSize:
order.Side = Side.Buy;
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_size));
case MarketViewView.Columns.AskMergedSize:
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Buy), ask_price, ask_merged_size));
break;
case MarketViewView.Columns.AskDirectSize:
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), ask_price, ask_direct_size));
break;
case MarketViewView.Columns.AskImpliedSize:
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), ask_price, ask_implied_size));
break;
case MarketViewView.Columns.Last:
_bus.post(SparkTags.ShowOrderTicketTag, (order, last_price, 0u));
@@ -229,7 +263,15 @@ namespace mbts.spark {
_working_best_ask.on_next_distinct(_order_summarizer.best_working_ask, ref dirty);
_inside_bids.on_next_distinct(_order_summarizer.best_working_bid >= bid_price, ref dirty);
_inside_asks.on_next_distinct(_order_summarizer.best_working_ask <= ask_price, ref dirty);
RaisePropertyChanged(nameof(working_bids_text), nameof(working_asks_text));
RaisePropertyChanged(
// Implied might not be ever set, so it probably does not RaisePropertyChanged either
// This might be part of the filter funk?
//nameof(bid_implied_size),
//nameof(ask_implied_size),
nameof(working_bids_text),
nameof(working_asks_text)
);
}
#endregion
@@ -1,10 +1,13 @@
using bts.api;
using mbts.persist;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Windows.Data;
@@ -17,65 +20,79 @@ namespace mbts.spark {
partial class MarketViewVM : SecurityMDGrid<MarketViewVM.Row, SecurityMD> {
readonly IBus _bus;
readonly OrdersModel _orders_model;
IWindowSettingsManager<GridSettings> _settings_manager;
public MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state)
: this(bus, orders_model, state, state.SelectManyInSeries(s => s.security_criteria.subscribe_md(bus))) { }
public MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state, IWindowSettingsManager<GridSettings> settings_manager )
: this(bus, orders_model, state, state.SelectManyInSeries(s => s.security_criteria.subscribe_md(bus)), settings_manager) { }
// securities_connectable should NOT be connected already.
MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state, IConnectableObservable<IEnumerable<SecurityMD>> securities_connectable)
MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state, IConnectableObservable<IEnumerable<SecurityMD>> securities_connectable, IWindowSettingsManager<GridSettings> settings_manager)
: base(RXCollection.Create(securities_connectable, SecurityDataHelper.EqualityComparer.MDComparer), null) {
_bus = bus;
_orders_model = orders_model;
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
_settings_manager = settings_manager;
// Columns
const string size_col_header = "Qty", working_col_header = "Working";
const string merged_size_col_header = "Qty", direct_size_col_header = "Direct", implied_size_col_header = "Implied", working_col_header = "Working";
const double working_orders_col_width = 105;
IMultiValueConverter price_converter = new ui.spark.converters.TickDisplayConverter();
add_col(Columns.Security, "Security", r => r.security.ToString(), nameof(Row.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), 64, volume_filter, DataGridFilterKnownTemplates.ULongMinMax);
add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), DefaultColumnWidths.Volume, volume_filter, DataGridFilterKnownTemplates.ULongMinMax);
add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.LastSize, size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax);
add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true);
add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid);
add_hl_size_col(Columns.BidSize, size_col_header, nameof(Row.bid_size), nameof(Row.bid_size_click), bid_size_filter, BTSColors.MarketData.bid);
add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid_brush);
add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask_brush);
add_hl_size_col(Columns.AskSize, size_col_header, nameof(Row.ask_size), nameof(Row.ask_size_click), ask_size_filter, BTSColors.MarketData.ask);
add_hl_working_orders_col(Columns.WorkingAsks, working_col_header, nameof(Row.working_asks_text), nameof(Row.working_best_ask), nameof(Row.working_asks_click), working_ask_volume_filter, BTSColors.MarketData.ask);
add_col(Columns.Settlement, "Settlement", r => r.format_price(r.settlement), nameof(Row.settlement), DefaultColumnWidths.Price, settlement_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.DayChange, "Change", r => r.format_price(r.day_change), nameof(Row.day_change), DefaultColumnWidths.Price, day_change_filter, DataGridFilterKnownTemplates.DoubleMinMax, true);
add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right);
add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid, System.Windows.HorizontalAlignment.Right);
add_col(Columns.BidImpliedSize, implied_size_col_header, r => r.bid_implied_size.ToString(), nameof(Row.bid_implied_size), DefaultColumnWidths.ImpliedDirectMergedSize, bid_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.BidDirectSize, direct_size_col_header, r => r.bid_direct_size.ToString(), nameof(Row.bid_direct_size), DefaultColumnWidths.ImpliedDirectMergedSize, bid_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
add_hl_merged_size_col(Columns.BidMergedSize, merged_size_col_header, nameof(Row.bid_merged_size), nameof(Row.bid_merged_size_text), nameof(Row.bid_merged_size_click), bid_merged_size_filter, BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid_brush, h_alignment: System.Windows.HorizontalAlignment.Right);
add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask_brush, h_alignment: System.Windows.HorizontalAlignment.Right);
add_hl_merged_size_col(Columns.AskMergedSize, merged_size_col_header, nameof(Row.ask_merged_size), nameof(Row.ask_merged_size_text), nameof(Row.ask_merged_size_click), ask_merged_size_filter, BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.AskDirectSize, direct_size_col_header, r => r.ask_direct_size.ToString(), nameof(Row.ask_direct_size), DefaultColumnWidths.ImpliedDirectMergedSize, ask_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.AskImpliedSize, implied_size_col_header, r => r.ask_implied_size.ToString(), nameof(Row.ask_implied_size), DefaultColumnWidths.ImpliedDirectMergedSize, ask_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
add_hl_working_orders_col(Columns.WorkingAsks, working_col_header, nameof(Row.working_asks_text), nameof(Row.working_best_ask), nameof(Row.working_asks_click), working_ask_volume_filter, BTSColors.MarketData.ask, System.Windows.HorizontalAlignment.Right);
add_col(Columns.Settlement, "Settlement", r => r.format_price(r.settlement), nameof(Row.settlement), DefaultColumnWidths.Settlement, settlement_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.DayChange, "Change", r => r.format_price(r.day_change), nameof(Row.day_change), DefaultColumnWidths.Price, day_change_filter, DataGridFilterKnownTemplates.DoubleMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.SecurityKey, "Security Key", r => r.security_key.ToString(), nameof(Row.security_key), DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
//
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false) =>
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false, Color? font_color = null, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
// it's necessary to have this id populated for grid settings
id = bts.shell.MarketViewWindowFactory.col_id(col_key),
Header = header,
sort_member_path = prop,
Width = width,
padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding
padding = is_animatable ? AnimatableCellPadding : DefaultCellPadding,
font_color_override = font_color,
horizontal_alignment = h_alignment
});
void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background) =>
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price, foreground, background, DefaultSelectionHighlight);
void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background, System.Windows.HorizontalAlignment h_alignment) =>
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.MViewPrice, foreground, background, DefaultSelectionHighlight, h_alignment);
void add_hl_size_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground) =>
add_hl_col(col_key, header, new Binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.Size, foreground, null, null);
void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.ImpliedDirectMergedSize, foreground, null, null, h_alignment);
void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground) =>
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null);
void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null, h_alignment);
void add_hl_col(
Columns col_key, string header, BindingBase binding, string prop, string command_prop,
IDataGridColumnFilter filter, string filter_template, double width, Color foreground, Brush background, Brush background_overlay) =>
IDataGridColumnFilter filter, string filter_template, double width, Color foreground, Brush background, Brush background_overlay, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
columns.Add(new HyperlinkElementColumn<Row, Columns>(
col_key, binding, new Binding(command_prop), filter, filter_template) {
// it's necessary to have this id populated for grid settings
id = bts.shell.MarketViewWindowFactory.col_id(col_key),
Header = header,
sort_member_path = prop,
font_color_override = foreground,
background = background,
background_overlay = background_overlay,
Width = DefaultColumnWidths.Price
Width = DefaultColumnWidths.Price,
horizontal_alignment = h_alignment
});
BindingBase price_binding(string prop) {
@@ -85,17 +102,41 @@ namespace mbts.spark {
return mb;
}
//
// make font-color immutable for Side, Size, (the font-color rule is hard-coded: MarketData.bid for Buy orders, MarketData.ask for Sells)
var col_settings_flags = new Dictionary<string, GridColumnSettingsFeatureFlags> {
{ Columns.Bid.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.Ask.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.BidImpliedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.BidDirectSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.BidMergedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.AskImpliedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.AskDirectSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.AskMergedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.WorkingAsks.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
{ Columns.WorkingBids.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }
};
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, col_settings_flags, row_vms);
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
grid_settings_manager.load_from(settings_manager.current_settings());
//
_resources.AddRange(
state.OnDispatcher().Subscribe(s => {
using (rows_view.DeferRefresh()) {
rows_view.SetSortingCriteria(s.sorting_criteria);
var filters = s.filters;
if (filters != null) {
if (s.grid != null) {
// this means we are not using defaults,
// Note: settings_manager in turn notifies grid_settings_manager of this update
_settings_manager.instance_settings = s.grid;
}
if (s.filters is FilterStates filters) {
security_filter.load_state(filters.security);
bid_price_filter.load_state(filters.bid_price);
bid_size_filter.load_state(filters.bid_size);
bid_merged_size_filter.load_state(filters.bid_merged_size);
bid_direct_size_filter.load_state(filters.bid_direct_size);
bid_implied_size_filter.load_state(filters.bid_implied_size);
ask_price_filter.load_state(filters.ask_price);
ask_size_filter.load_state(filters.ask_size);
ask_merged_size_filter.load_state(filters.ask_merged_size);
ask_direct_size_filter.load_state(filters.ask_direct_size);
ask_implied_size_filter.load_state(filters.ask_implied_size);
last_price_filter.load_state(filters.last_price);
last_size_filter.load_state(filters.last_size);
day_change_filter.load_state(filters.day_change);
@@ -108,23 +149,40 @@ namespace mbts.spark {
volume_filter.load_state(filters.volume);
security_key_filter.load_state(filters.security_key);
}
rows_view.set_sort_descriptions(s.sorting_criterion, columns);
}
}), DataGridFilterHelper.hook_filters(rows_view,
security_filter, bid_price_filter, bid_size_filter, ask_price_filter, ask_size_filter, last_price_filter, last_size_filter, day_change_filter, settlement_filter,
}), DataGridFilterHelper.hook_filters(rows_view, security_filter,
bid_price_filter, bid_merged_size_filter, bid_direct_size_filter, bid_implied_size_filter,
ask_price_filter, ask_merged_size_filter, ask_direct_size_filter, ask_implied_size_filter,
last_price_filter, last_size_filter, day_change_filter, settlement_filter,
session_low_filter, session_high_filter, position_filter, working_bid_volume_filter, working_ask_volume_filter, volume_filter, security_key_filter),
securities_connectable.Connect(),
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
WorkLoop.Start("MarketView", _refresh)
);
}
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
public GridSettings get_grid_save_state() {
if (_settings_manager.instance_settings == null)
return null;
else
return grid_settings_manager.create();
}
#region Filters
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_price_filter { get; } = new DoubleMinMax<Row>(r => r.bid_price, nameof(Row.bid_price));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_size, nameof(Row.bid_size));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_merged_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_merged_size, nameof(Row.bid_merged_size));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_direct_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_direct_size, nameof(Row.bid_direct_size));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_implied_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_implied_size, nameof(Row.bid_implied_size));
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> ask_price_filter { get; } = new DoubleMinMax<Row>(r => r.ask_price, nameof(Row.ask_price));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_size_filter { get; } = new UIntMinMax<Row>(r => r.ask_size, nameof(Row.ask_size));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_merged_size_filter { get; } = new UIntMinMax<Row>(r => r.ask_merged_size, nameof(Row.ask_merged_size));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_direct_size_filter { get; } = new UIntMinMax<Row>(r => r.ask_direct_size, nameof(Row.ask_direct_size));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> ask_implied_size_filter { get; } = new UIntMinMax<Row>(r => r.ask_implied_size, nameof(Row.ask_implied_size));
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> last_price_filter { get; } = new DoubleMinMax<Row>(r => r.last_price, nameof(Row.last_price));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> last_size_filter { get; } = new UIntMinMax<Row>(r => r.last_size, nameof(Row.last_size));
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> day_change_filter { get; } = new DoubleMinMax<Row>(r => r.day_change, nameof(Row.day_change));
@@ -141,9 +199,13 @@ namespace mbts.spark {
return new FilterStates {
security = security_filter.get_state(),
bid_price = bid_price_filter.get_state(),
bid_size = bid_size_filter.get_state(),
bid_merged_size = bid_merged_size_filter.get_state(),
bid_direct_size = bid_direct_size_filter.get_state(),
bid_implied_size = bid_implied_size_filter.get_state(),
ask_price = ask_price_filter.get_state(),
ask_size = ask_size_filter.get_state(),
ask_merged_size = ask_merged_size_filter.get_state(),
ask_direct_size = ask_direct_size_filter.get_state(),
ask_implied_size = ask_implied_size_filter.get_state(),
last_price = last_price_filter.get_state(),
last_size = last_size_filter.get_state(),
day_change = day_change_filter.get_state(),
@@ -16,28 +16,46 @@
</ResourceDictionary>
</UserControl.Resources>
<AdornerDecorator>
<c:DIYGrid
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<c:DIYGrid
x:Name="_grid"
Grid.Row="0"
Columns="{Binding columns}"
CurrentRow="{Binding current_row}"
Rows="{Binding rows_view}"
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
>
<c:DIYGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
</c:DIYGrid.InputBindings>
<c:DIYGrid.CommandBindings>
<CommandBinding
<c:DIYGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
</c:DIYGrid.InputBindings>
<c:DIYGrid.CommandBindings>
<CommandBinding
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
/>
</c:DIYGrid.CommandBindings>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<usp:MarketViewRowView usp:GridAnimation.RowAnimation="{Binding animations}"/>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
</c:DIYGrid.CommandBindings>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<usp:MarketViewRowView usp:GridAnimation.RowAnimation="{Binding animations}"/>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<TextBlock>
<Run Text="Securities:"/>
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -8,7 +8,7 @@ namespace mbts.ui.spark {
/// Interaction logic for MarketSheetsView.xaml
/// </summary>
partial class MarketViewView : UserControl {
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidSize, Ask, AskSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey }
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidMergedSize, BidDirectSize, BidImpliedSize, Ask, AskMergedSize, AskDirectSize, AskImpliedSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey }
public MarketViewView() {
InitializeComponent();
@@ -2,8 +2,11 @@
using bts.api;
using bts.shell;
using bts.spark;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
@@ -16,16 +19,18 @@ namespace mbts.spark {
const string _window_type = "MarketView", _thread_id = "MarketView", _window_type_display_name = "Market View";
readonly OrdersModel _orders_model;
readonly AllowOnce _init_guard = new AllowOnce("MarketView");
readonly IWindowSettingsManager<GridSettings> _settings_manager;
MarketViewVM _vm;
MarketViewView _view;
MarketViewWindowFactory.StartupData _state;
IReadOnlyList<SparkCommand> _commands;
IDisposable _subscriptions;
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state)
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, WindowSettingsManager<GridSettings> settings_manager)
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
_orders_model = orders_model;
_state = state;
_settings_manager = settings_manager;
Icon.Freeze();
}
@@ -34,31 +39,55 @@ namespace mbts.spark {
public override string window_type => _window_type;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public override MarketViewWindowFactory.StartupData SaveState => _state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states());
public override double MinWidth => 1035 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
public override MarketViewWindowFactory.StartupData SaveState =>
_state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.get_grid_save_state(), _vm.rows_view.get_sorting_criteria(_vm.columns));
public override double MinWidth => MarketViewWindowFactory.StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public string thread_id => _thread_id;
public void thread_init() {
_init_guard.check_access();
Log.notnull(_state);
var state_src = Observable.FromEvent<Action<MarketViewWindowFactory.StartupData>, MarketViewWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
_vm = new MarketViewVM(_bus, _orders_model, state_src);
_vm = new MarketViewVM(_bus, _orders_model, state_src, _settings_manager);
_view = new MarketViewView { DataContext = _vm };
var update_securities_cmd = SparkCommands.change_securities_callback(_window_type_display_name);
_commands = new List<SparkCommand> {
SparkCommands.ShowLadders.complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills.complete(_vm.selected_securities_observable(), _ => true,
secs => new FillsWindowFactory.StartupData(new FillsWindowFactory.FilterStates {
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
})
),
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs)),
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria))
SparkCommands.ShowSyntheticCreate.complete(_vm.sorted_selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs)),
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
SparkCommands.window_settings_command(new DialogCommand(() => {
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
_settings_manager,
us => us.market_view_grid,
UserSettings.FileExtensions.GRID,
s => _vm.grid_settings_manager.create_vm(
// we need to get the most up to date GridSettings when launching b/c users may
// have changed some settings on the grid directly, like column widths.
s.with(_vm.columns.Select( c => c.create())),
this.observe_title()
)
);
// TODO: bind to main window's Title
var dialog = new SimpleGridSettingsDialog { Title = "Market View Grid Settings", DataContext = vm };
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
}))
};
_subscriptions = _bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(c =>
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states())));
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.get_filter_states(), _vm.grid_settings_manager.create())));
}
public override void Dispose() {
@@ -67,4 +96,4 @@ namespace mbts.spark {
Commands.OfType<IDisposable>().DisposeAll();
}
}
}
}
@@ -1,31 +1,47 @@
using bts.api;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.MarketViewView;
namespace bts.shell {
class MarketViewWindowFactory : ShellWindowFactory<MarketViewWindowFactory.StartupData> {
readonly IBus _bus;
readonly OrdersModel _orders_model;
readonly UserSettingsHub _settings_hub;
public MarketViewWindowFactory(MarketShellModule module, IBus bus, OrdersModel orders_model) : base(module, "MarketView") {
public MarketViewWindowFactory(MarketShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) : base(module, "MarketView") {
_bus = bus;
_orders_model = orders_model;
_settings_hub = settings_hub;
_dispose = bus.subscribe(SparkTags.ShowMarketViewTag).OnDispatcher().Subscribe(_show);
}
public static string col_id(Columns col) => col.ToString();
public override Task<IShellWindow> load(StartupData state) =>
Task.FromResult(_create(state));
void _show(SecurityCriteria criteria) =>
_bus.post(SparkTags.ShowWindow, _create(new StartupData(criteria, new List<SortingCriteria> { new SortingCriteria(nameof(MarketViewVM.Row.security), true) }, null)));
_bus.post(SparkTags.ShowWindow, _create(StartupData.Default(criteria)));
IShellWindow _create(StartupData state) => new MarketViewWindow(this, _bus, _orders_model, state);
IShellWindow _create(StartupData state) {
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
state.grid,
get: us => us.market_view_grid,
set: (us, gs) => us.with(provided_market_view_gridsettings: gs));
return new MarketViewWindow(this, _bus, _orders_model, state, settings_manager);
}
[ProtoContract]
public class StartupData {
@@ -34,20 +50,89 @@ namespace bts.shell {
[ProtoMember(1)]
public readonly SecurityCriteria security_criteria;
[ProtoMember(2)]
public readonly List<SortingCriteria> sorting_criteria;
List<SortingCriteria> _sorting_criteria;
[ProtoMember(3)]
public readonly FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
StartupData() { }
public StartupData() { }
public StartupData(SecurityCriteria security_criteria)
: this (security_criteria, new List<SortingCriteria>(), null) { }
: this (security_criteria, new FilterStates()) { }
public StartupData(SecurityCriteria security_criteria, List<SortingCriteria> sorting_criteria, FilterStates filters) {
public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List<SortingCriteria> sort = null) {
this.security_criteria = security_criteria;
this.sorting_criteria = sorting_criteria;
this.filters = filters;
this.grid = grid;
_sorting_criteria = sort;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList());
public static StartupData Default(SecurityCriteria security_criteria) =>
new StartupData(security_criteria, new FilterStates(), null);
static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings volume => new GridColumnSettings(col_id(Columns.Volume), DefaultColumnHeaders.Volume, width: DefaultColumnWidths.Volume);
public static GridColumnSettings last => new GridColumnSettings(col_id(Columns.Last), DefaultColumnHeaders.Last, width: DefaultColumnWidths.Price);
public static GridColumnSettings last_size => new GridColumnSettings(col_id(Columns.LastSize), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings position => new GridColumnSettings(col_id(Columns.Position), DefaultColumnHeaders.Pos, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings working_bids => new GridColumnSettings(col_id(Columns.WorkingBids), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Working, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings bid_implied_size => new GridColumnSettings(col_id(Columns.BidImpliedSize), DefaultColumnHeaders.ImpliedSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings bid_direct_size => new GridColumnSettings(col_id(Columns.BidDirectSize), DefaultColumnHeaders.DirectSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings bid_merged_size => new GridColumnSettings(col_id(Columns.BidMergedSize), DefaultColumnHeaders.MergedSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings ask_merged_size => new GridColumnSettings(col_id(Columns.AskMergedSize), DefaultColumnHeaders.MergedSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings ask_direct_size => new GridColumnSettings(col_id(Columns.AskDirectSize), DefaultColumnHeaders.DirectSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings ask_implied_size => new GridColumnSettings(col_id(Columns.AskImpliedSize), DefaultColumnHeaders.ImpliedSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings working_asks => new GridColumnSettings(col_id(Columns.WorkingAsks), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.Working, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings settlement => new GridColumnSettings(col_id(Columns.Settlement), DefaultColumnHeaders.Settlement, width: DefaultColumnWidths.Settlement, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings day_change => new GridColumnSettings(col_id(Columns.DayChange), DefaultColumnHeaders.DayChange, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings session_low => new GridColumnSettings(col_id(Columns.SessionLow), DefaultColumnHeaders.SessionLow, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings session_high => new GridColumnSettings(col_id(Columns.SessionHigh), DefaultColumnHeaders.SessionHigh, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(security.id, true);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return volume;
yield return last;
yield return last_size;
yield return position;
yield return working_bids;
yield return bid_implied_size;
yield return bid_direct_size;
yield return bid_merged_size;
yield return bid;
yield return ask;
yield return ask_merged_size;
yield return ask_direct_size;
yield return ask_implied_size;
yield return working_asks;
yield return settlement;
yield return day_change;
yield return session_low;
yield return session_high;
yield return security_key;
}
}
}
}
[ProtoContract]
@@ -57,11 +142,11 @@ namespace bts.shell {
[ProtoMember(2)]
public DoubleMinMaxState bid_price;
[ProtoMember(3)]
public UIntMinMaxState bid_size;
public UIntMinMaxState bid_merged_size;
[ProtoMember(4)]
public DoubleMinMaxState ask_price;
[ProtoMember(5)]
public UIntMinMaxState ask_size;
public UIntMinMaxState ask_merged_size;
[ProtoMember(6)]
public DoubleMinMaxState last_price;
[ProtoMember(7)]
@@ -84,6 +169,14 @@ namespace bts.shell {
public ULongMinMaxState volume;
[ProtoMember(16)]
public string security_key;
[ProtoMember(17)]
public UIntMinMaxState bid_direct_size;
[ProtoMember(18)]
public UIntMinMaxState bid_implied_size;
[ProtoMember(19)]
public UIntMinMaxState ask_direct_size;
[ProtoMember(20)]
public UIntMinMaxState ask_implied_size;
}
}
}
@@ -19,11 +19,14 @@ namespace mbts.spark {
}
class TimeAndSalesModel : IDisposable {
internal readonly SecurityCriteria security_criteria;
static readonly TimeSpan _poll_interval = TimeSpan.FromSeconds(0.4);
Lazy<IObservable<IReadOnlyList<TradeInfo>>> _trades;
IDisposable _dispose;
public TimeAndSalesModel(IBus bus, MDConsumer consumer, SecurityCriteria security_criteria) {
this.security_criteria = security_criteria;
var curr_channels = new Dictionary<ChannelKey, ChannelConsumer>();
_trades = new Lazy<IObservable<IReadOnlyList<TradeInfo>>>(() => Observable.Interval(_poll_interval).Select(_ => {
var copy = curr_channels;
@@ -94,8 +97,11 @@ namespace mbts.spark {
public IEnumerable<TradeInfo> read_trades() {
var copy = _securities;
foreach (var tm in _channel.read_trades(ref _cursor)) {
if (copy.TryGetValue(tm.Key, out SecurityImmutable sec))
yield return new TradeInfo(sec, tm);
if (tm != null) {
if (copy.TryGetValue(tm.Key, out SecurityImmutable sec)) {
yield return new TradeInfo(sec, new TradeMessageImmutable(tm));
}
}
}
}
}
@@ -3,7 +3,6 @@ using bts.co2;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
using mbts.ui.spark;
using mbts.ui.utility;
using System;
@@ -15,53 +14,75 @@ using System.Windows.Input;
using System.Windows.Media;
using static bts.shell.TimeAndSalesWindowFactory;
using static mbts.ui.controls.DIYGrid;
using mbts.persist;
using mbts.ui.settings;
using System.Reactive.Linq;
namespace mbts.spark {
class TimeAndSalesVM : SecurityGrid<TimeAndSalesVM.Row, TradeInfo> {
readonly IBus _bus;
readonly TimeAndSalesModel _model;
IWindowSettingsManager<GridSettings> _settings_manager;
public TimeAndSalesVM(IBus bus, TimeAndSalesModel model, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
: base(RXCollection.CreateBuffer(model.trades, 1000), sorting_criteria) {
public TimeAndSalesVM(IBus bus, IWindowSettingsManager<GridSettings> settings_manager, TimeAndSalesModel model, StartupData startup_data)
: base(RXCollection.CreateBuffer(model.trades, 1000), Enumerable.Empty<SortingCriteria>()) {
_bus = bus;
_model = model;
_settings_manager = settings_manager;
show_ladder = DelegateCommandParamObserver.Create(current_row_observable, r => _show_ladder_async(r).ignore(), r => r != null);
security_filter = new RegexFilterVM<Row>(r => r.security.ToString(), nameof(Row.security));
if (filters != null) {
if (startup_data.filters is FilterStates filters) {
time_filter.load_state(filters.time);
security_filter.load_state(filters.security);
price_filter.load_state(filters.price);
size_filter.load_state(filters.size);
aggressor_filter.load_state(filters.aggressor_sides);
trade_key_filter.load_state(filters.trade_key);
security_key_filter.load_state(filters.security_key);
}
// Columns
add_column(TimeAndSalesView.Columns.Security, DefaultColumnHeaders.Security, DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.Time, DefaultColumnHeaders.Time, DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter);
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DIYGrid.DefaultColumnWidths.Aggressor, aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
add_column(TimeAndSalesView.Columns.Security, DefaultColumnHeaders.Security, DefaultColumnWidths.Security, nameof(Row.security), security_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.Time, DefaultColumnHeaders.Time, DefaultColumnWidths.Time, nameof(Row.time), time_filter, DataGridFilterKnownTemplates.TimeFilter);
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DefaultColumnWidths.Aggressor, nameof(Row.aggressor_side), aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DefaultColumnWidths.SecurityKey, nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex);
void add_column(TimeAndSalesView.Columns col_key, string header, double width, string sort_member_path, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
columns.Add(new TimeAndSalesColumn(
r => r[col_key]
col_key
, (r) => _get_aggressor_brush(r)
, filter
, filter_template
)
{ Header = header, Width = width, horizontal_alignment = h_alignment }
{ Header = header, Width = width, sort_member_path = sort_member_path, horizontal_alignment = h_alignment }
);
// be sure to load GridSettings AFTER the columns collection has been populated, the grid settings manager looks columns by id to apply the appropriate settings,
// if the collection is not populated, the column settings will not be applied
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, row_vms);
grid_settings_manager.load_from(settings_manager.current_settings());
_resources.AddRange(
show_ladder,
DataGridFilterHelper.hook_filters(
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, trade_key_filter, security_key_filter
)
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, security_key_filter),
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager)
);
rows_view.set_sort_descriptions(startup_data.sorting_criterion, columns);
}
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
public DelegateCommandParamObserver<Row> show_ladder { get; }
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
public GridSettings get_grid_save_state() {
if (_settings_manager.instance_settings == null)
return null;
else
return grid_settings_manager.create();
}
public StartupData create() =>
new StartupData(_model.security_criteria, _get_filter_states(), get_grid_save_state(), rows_view.get_sorting_criteria(columns));
private Brush _get_aggressor_brush(Row r) {
BTSColor c;
@@ -80,15 +101,13 @@ namespace mbts.spark {
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> size_filter { get; } = new UIntMinMax<Row>(r => r.size, nameof(Row.size));
public IDataGridColumnPersistentFilter<Row, AggressorSide[]> aggressor_filter { get; } = DiscreteValuesFilterVM.Create<Row, AggressorSide>(EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side, nameof(Row.aggressor_side));
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
public IDataGridColumnPersistentFilter<Row, string> trade_key_filter { get; } = new RegexFilterVM<Row>(r => r.trade_key.ToString(), nameof(Row.trade_key));
public FilterStates get_filter_states() =>
FilterStates _get_filter_states() =>
new FilterStates {
time = time_filter.get_state(),
security = security_filter.get_state(),
price = price_filter.get_state(),
size = size_filter.get_state(),
trade_key = trade_key_filter.get_state(),
security_key = security_key_filter.get_state(),
aggressor_sides = aggressor_filter.get_state()
};
@@ -144,9 +163,6 @@ namespace mbts.spark {
public AggressorSide aggressor_side { get; }
public ICommand show_ladder { get; }
ObservableCollection<RowAnimation> DIYGrid.IRowVM.row_animations { get; } = new ObservableCollection<RowAnimation>();
IReadOnlyList<DIYGrid.IGridCellStore> DIYGrid.IRowVM.cell_stores => new DIYGrid.IGridCellStore[0];
public string this[TimeAndSalesView.Columns col] {
get {
switch (col) {
@@ -155,7 +171,6 @@ namespace mbts.spark {
case TimeAndSalesView.Columns.Price: return _price_text;
case TimeAndSalesView.Columns.Size: return _size_text;
case TimeAndSalesView.Columns.Aggressor: return _aggressor_text;
case TimeAndSalesView.Columns.TradeKey: return _trade_key_text;
case TimeAndSalesView.Columns.SecurityKey: return _security_key_text;
default: return null;
}
@@ -18,7 +18,18 @@
</UserControl.Resources>
<AdornerDecorator>
<Grid>
<c:DIYGrid Columns="{Binding columns}" Rows="{Binding rows_view}" CurrentRow="{Binding current_row}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<c:DIYGrid
x:Name="_grid"
Grid.Row="0"
Columns="{Binding columns}"
Rows="{Binding rows_view}"
CurrentRow="{Binding current_row}"
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
>
<c:DIYGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
@@ -36,6 +47,11 @@
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<TextBlock Text=" "/>
</StatusBarItem>
</StatusBar>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -7,7 +7,7 @@ namespace mbts.ui.spark {
/// Interaction logic for TimeAndSalesView.xaml
/// </summary>
partial class TimeAndSalesView : UserControl {
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey, Aggressor }
public enum Columns { Time, Security, Price, Size, OrderType, SecurityKey, Aggressor }
public TimeAndSalesView() =>
InitializeComponent();
@@ -3,8 +3,11 @@ using bts.api;
using bts.shell;
using bts.spark;
using mbts.co2;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
@@ -20,6 +23,7 @@ namespace mbts.spark {
const string _window_type = "TimeAndSales", _window_type_display_name = "Time And Sales";
readonly MDConsumer _consumer;
readonly TimeAndSalesWindowFactory _factory;
readonly IWindowSettingsManager<GridSettings> _settings_manager;
SingleValuePump<TimeAndSalesVM> _vm;
TimeAndSalesModel _model;
StartupData _state;
@@ -27,29 +31,25 @@ namespace mbts.spark {
SparkCommand[] _commands;
IDisposable _subscriptions;
public TimeAndSalesWindow(TimeAndSalesWindowFactory factory, IBus bus, MDConsumer consumer, StartupData state, ImageSource icon)
public TimeAndSalesWindow(TimeAndSalesWindowFactory factory, IBus bus, MDConsumer consumer, StartupData state, ImageSource icon, IWindowSettingsManager<GridSettings> settings_manager)
: base("Time And Sales", bus, factory, SparkCommands.ShowTimeAndSales.icon) {
Log.assert(state != null);
_consumer = consumer;
_factory = factory;
_state = state;
_settings_manager = settings_manager;
Icon.Freeze();
}
public override string window_type => _window_type;
public override IEnumerable<SparkCommand> Commands => _commands;
public override UIElement Content => _view;
public override double MinWidth => DefaultColumnWidths.Security
+ DefaultColumnWidths.Time
+ DefaultColumnWidths.Price
+ DefaultColumnWidths.Size
+ DefaultColumnWidths.Aggressor
+ DefaultColumnWidths.TradeKey
+ DefaultColumnWidths.SecurityKey
public override double MinWidth => StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH;
public override StartupData SaveState => _state = new StartupData(_state.security_criteria, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public override StartupData SaveState => _state = _vm.value.create();
public override void Dispose() =>
_dispose_resources();
@@ -62,11 +62,29 @@ 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[] {
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills.complete(selected_secs_source, always_true, get_fills_startup_data),
SparkCommands.ShowMarketView.complete(selected_secs_source, always_true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria))
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
SparkCommands.window_settings_command(new DialogCommand(() => {
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
_settings_manager,
s => s.time_and_sales_grid,
UserSettings.FileExtensions.GRID,
s => _vm.value.grid_settings_manager.create_vm(
// we need to get the most up to date GridSettings when launching b/c users may
// have changed some settings on the grid directly, like column widths.
s.with(_vm.value.columns.Select( c => c.create())),
this.observe_title()
)
);
// TODO: bind to main window's Title
var dialog = new SimpleGridSettingsDialog { Title = "Time and Sales Grid Settings", DataContext = vm };
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
}))
};
_subscriptions = Disposable.Create(
_bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(update_criteria),
@@ -79,7 +97,7 @@ namespace mbts.spark {
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
});
void update_criteria(SecurityCriteria c) {
_state = new StartupData(c, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
_state = _vm.value.create().with(c);
_initialize();
}
}
@@ -98,7 +116,7 @@ namespace mbts.spark {
Disposable.dispose(ref _model);
_model = new TimeAndSalesModel(_bus, _consumer, _state.security_criteria);
// the previous _vm.value is automatically disposed when _vm.value is set - no need to dispose it here
_vm.value = new TimeAndSalesVM(_bus, _model, _state.sorting_criteria, _state.filters);
_vm.value = new TimeAndSalesVM(_bus, _settings_manager, _model, _state);
if (_view == null)
_view = new TimeAndSalesView();
_view.DataContext = _vm.value;
@@ -2,24 +2,31 @@
using bts.spark;
using mbts;
using mbts.co2;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.TimeAndSalesView;
namespace bts.shell {
class TimeAndSalesWindowFactory : ShellWindowFactory<TimeAndSalesWindowFactory.StartupData> {
readonly IBus _bus;
readonly MDConsumer _consumer;
readonly UserSettingsHub _settings_hub;
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
public TimeAndSalesWindowFactory(IShellModule module, IBus bus, MDConsumer consumer) : base(module, "TimeAndSales") {
public TimeAndSalesWindowFactory(IShellModule module, IBus bus, MDConsumer consumer, UserSettingsHub settings_hub) : base(module, "TimeAndSales") {
_bus = bus;
_consumer = consumer;
_settings_hub = settings_hub;
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
@@ -27,8 +34,11 @@ namespace bts.shell {
);
}
public static string col_id(Columns col) => col.ToString();
public override Task<IShellWindow> load(StartupData state) =>
Task.FromResult<IShellWindow>(new TimeAndSalesWindow(this, _bus, _consumer, state, SparkCommands.PickTimeAndSales.icon));
Task.FromResult<IShellWindow>(new TimeAndSalesWindow(this, _bus, _consumer, state, SparkCommands.PickTimeAndSales.icon,
new WindowSettingsManager<GridSettings>(_settings_hub, state.grid, s => s.time_and_sales_grid, (s, g) => s.with(time_and_sales:g))));
public async void show(SecurityCriteria criteria) {
var w = await load(new StartupData(criteria));
@@ -40,19 +50,58 @@ namespace bts.shell {
[ProtoMember(1)]
public readonly SecurityCriteria security_criteria;
[ProtoMember(2)]
public readonly List<SortingCriteria> sorting_criteria;
List<SortingCriteria> _sorting_criterion;
[ProtoMember(3)]
public FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
StartupData() { }//for ptorobuf
public StartupData() { }
public StartupData(SecurityCriteria security_criteria)
: this (security_criteria, new List<SortingCriteria>(), null) { }
public StartupData(SecurityCriteria security_criteria) :
this(security_criteria, new FilterStates(), null, null) { }
public StartupData(SecurityCriteria security_criteria, List<SortingCriteria> sorting_criteria, FilterStates filters) {
public StartupData(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List<SortingCriteria> sorting_criterion = null) {
this.security_criteria = security_criteria;
this.sorting_criteria = sorting_criteria;
this.filters = filters;
this.grid = grid;
_sorting_criterion = sorting_criterion;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public List<SortingCriteria> sorting_criterion => _sorting_criterion ?? (_sorting_criterion = DefaultColumns.default_sorting_criterion.ToList());
public static StartupData Default(SecurityCriteria security_criteria) =>
new StartupData(security_criteria, new FilterStates(), null, DefaultColumns.default_sorting_criterion.ToList());
public StartupData with(SecurityCriteria security_criteria) =>
new StartupData(security_criteria, filters, grid);
public static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings time => new GridColumnSettings(col_id(Columns.Time), DefaultColumnHeaders.Time, width: DefaultColumnWidths.Time);
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings size => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings aggressor => new GridColumnSettings(col_id(Columns.Aggressor), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible:false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(time.id, false);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return time;
yield return price;
yield return size;
yield return aggressor;
yield return security_key;
}
}
}
}
@@ -66,8 +115,8 @@ namespace bts.shell {
public DoubleMinMaxState price;
[ProtoMember(4)]
public UIntMinMaxState size;
[ProtoMember(5)]
public string trade_key;
// [ProtoMember(5)]
// ProtoMember 5 cannot be used further
[ProtoMember(6)]
public string security_key;
[ProtoMember(7)]
@@ -46,27 +46,23 @@
Offset="{StaticResource RowHeight}"
RowHeaderWidth="{StaticResource RowHeaderWidth}"
/>
<usp:DataGridParameterCellTemplateConverter x:Key="ParameterCellTemplateConverter">
<DataTemplate x:Key="SimpleValueTextTemplate">
<TextBlock Text="{Binding value}"/>
</DataTemplate>
<usp:DataGridParameterCellTemplateConverter
x:Key="ParameterCellTemplateConverter"
DoubleTemplate="{StaticResource SimpleValueTextTemplate}"
IntTemplate="{StaticResource SimpleValueTextTemplate}"
UIntTemplate="{StaticResource SimpleValueTextTemplate}"
MarketDataTypeTemplate="{StaticResource SimpleValueTextTemplate}"
MarketDataCalculationTemplate="{StaticResource SimpleValueTextTemplate}"
ConditionalPriceTypeTemplate="{StaticResource SimpleValueTextTemplate}"
>
<usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Left" IsChecked="{Binding value}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
<usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
<DataTemplate>
<TextBlock Text="{Binding value}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
<usp:DataGridParameterCellTemplateConverter.IntTemplate>
<DataTemplate>
<TextBlock Text="{Binding value}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.IntTemplate>
<usp:DataGridParameterCellTemplateConverter.UIntTemplate>
<DataTemplate>
<TextBlock Text="{Binding value}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.UIntTemplate>
<usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
<DataTemplate>
<Grid>
@@ -117,6 +113,21 @@
<c:TickUpDownUInt Value="{Binding value}" IsEnabled="{Binding enabled}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.UIntTemplate>
<usp:DataGridParameterCellTemplateConverter.MarketDataTypeTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:LegSpreaderParams.Types.MarketDataType}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.MarketDataTypeTemplate>
<usp:DataGridParameterCellTemplateConverter.MarketDataCalculationTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:LegSpreaderParams.Types.LegMarketDataCalculation}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.MarketDataCalculationTemplate>
<usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:ConditionalQuoting.Types.PriceType}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
<usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
<DataTemplate>
<Grid IsEnabled="{Binding enabled}">
@@ -302,6 +302,9 @@ namespace mbts.ui.spark {
public DataTemplate IntTemplate { get; set; }
public DataTemplate UIntTemplate { get; set; }
public DataTemplate QuantityMeasureTemplate { get; set; }
public DataTemplate MarketDataTypeTemplate { get; set; }
public DataTemplate MarketDataCalculationTemplate { get; set; }
public DataTemplate ConditionalPriceTypeTemplate { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
Type t = (Type)value;
@@ -310,6 +313,9 @@ namespace mbts.ui.spark {
else if (t == typeof(int)) return IntTemplate;
else if (t == typeof(uint)) return UIntTemplate;
else if (t == typeof(QuantityMeasure)) return QuantityMeasureTemplate;
else if (t == typeof(LegSpreaderParams.Types.MarketDataType)) return MarketDataTypeTemplate;
else if (t == typeof(LegSpreaderParams.Types.LegMarketDataCalculation)) return MarketDataCalculationTemplate;
else if (t == typeof(ConditionalQuoting.Types.PriceType)) return ConditionalPriceTypeTemplate;
else return null;
}
@@ -6,6 +6,7 @@ using mbts.ui;
using mbts.ui.spark;
using mbts.ui.utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
@@ -43,8 +44,23 @@ namespace bts.shell {
name + "(1)";
}
async void _show_synthetic_create(SecurityCriteria security_criteria) =>
_show_synthetic_create((await security_criteria.securities(_bus)).ToList());
async void _show_synthetic_create(SecurityCriteria security_criteria) {
var found_securities = await security_criteria.securities(_bus);
var respect_clause_security_ordering_is_possible = security_criteria.clauses.Any(clause => clause.security_keys != null && clause.security_keys.Count > 0);
if (!respect_clause_security_ordering_is_possible) {
_show_synthetic_create(found_securities.Values.ToList());
} else {
List<SecurityImmutable> ordered_security_list = new List<SecurityImmutable>();
foreach (var clause in security_criteria.clauses) {
foreach (var sec_key in clause.security_keys) {
if (found_securities.TryGetValue(sec_key, out SecurityImmutable found_result)) {
ordered_security_list.Add(found_result);
}
}
_show_synthetic_create(ordered_security_list);
}
}
}
void _show_synthetic_create(IReadOnlyCollection<SecurityImmutable> securities) {
if (securities.Count == 1) {
@@ -53,8 +53,8 @@ namespace mbts.spark {
return null;
}
public async Task<uint?> create_synthetic(SecurityImmutable synthetic) {
var result = await _service.create_synthetic_async(synthetic);
public async Task<uint?> create_synthetic(SecurityImmutable? synthetic) {
var result = await _service.create_synthetic_async(synthetic.Value);
return _error.handle_error(result, "Error creating synthetic: {0}", r => r.Error, e => e == CreateResult.Types.Error.Ok, r => new uint?(r.Key), () => null).result;
}
@@ -1,6 +1,7 @@
using bts.api;
using bts.spark;
using mbts;
using mbts.persist;
using mbts.spark;
using mbts.ui;
using mbts.ui.shell;
@@ -18,8 +19,8 @@ namespace bts.shell
LatencyReportWindowFactory _lrwf;
[ImportingConstructor]
public SpreaderShellModule(IBus bus, OrdersModel orders_model) {
_smwf = new SyntheticManagementWindowFactory(this, bus, orders_model);
public SpreaderShellModule(IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) {
_smwf = new SyntheticManagementWindowFactory(this, bus, settings_hub, orders_model);
_lrwf = new LatencyReportWindowFactory(this, bus);
}
@@ -103,6 +103,11 @@ namespace bts.shell {
OutsideTicks = 3,
Priority = RulePriority
};
public static ConditionalQuoting ConditionalQuoting => new ConditionalQuoting
{
NTicks = 2,
PriceType = ConditionalQuoting.Types.PriceType.Leg
};
public static QuoteInsideMarketDistanceWithTravel QuoteInsideMarketDistanceWithTravel => new QuoteInsideMarketDistanceWithTravel { Priority = RulePriority };
public static QuoteInsideMarketDistance QuoteInsideMarketDistance => new QuoteInsideMarketDistance { Priority = RulePriority };
#endregion
@@ -1,8 +1,12 @@
<UserControl x:Class="mbts.ui.spark.SyntheticManagementView" x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls">
<UserControl
x:Class="mbts.ui.spark.SyntheticManagementView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:usp="clr-namespace:mbts.ui.spark"
xmlns:c="clr-namespace:mbts.ui.controls"
Background="{StaticResource CommonBackground}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
@@ -10,71 +14,46 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<c:DataGridEx
x:Name="grid" BorderThickness="0" IsReadOnly="True" CanUserSortColumns="True"
GridLinesVisibility="None" ItemsSource="{Binding rows_view}"
CurrentItem="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="u:AttachedCommands.DoubleClick" Value="{Binding show_ladder}"/>
<Setter Property="u:AttachedCommands.Enter" Value="{Binding show_ladder}"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<!--<DataGridTextColumn
Header="Create Time" Binding="{Binding create_time}"
c:DataGridFilterProperties.Filter="{Binding DataContext.create_time_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.create_time_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource TimeFilterTemplate}"
/>-->
<DataGridTextColumn
Header="Name" Binding="{Binding name}"
c:DataGridFilterProperties.Filter="{Binding DataContext.name_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.name_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.name_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
/>
<DataGridTextColumn
Header="Description" Binding="{Binding description}"
c:DataGridFilterProperties.Filter="{Binding DataContext.description_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.description_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.description_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
/>
<DataGridTextColumn
Header="# Legs" Binding="{Binding n_legs}"
c:DataGridFilterProperties.Filter="{Binding DataContext.n_legs_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.n_legs_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.n_legs_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"
/>
<DataGridTemplateColumn
Header="# Working Orders"
c:DataGridFilterProperties.Filter="{Binding DataContext.n_working_orders_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.n_working_orders_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.n_working_orders_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<c:DIYGrid
x:Name="_grid"
Grid.Row="0"
Columns="{Binding columns}"
CurrentRow="{Binding current_row}"
Rows="{Binding rows_view}"
>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding show_order_book}">
<TextBlock Text="{Binding n_working_orders}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
Header="Security Key" Binding="{Binding security_key}" Width="*"
c:DataGridFilterProperties.Filter="{Binding DataContext.security_key_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_key_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_key_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
/>
</DataGrid.Columns>
</c:DataGridEx>
</Grid>
<c:DIYGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
</c:DIYGrid.InputBindings>
<c:DIYGrid.CommandBindings>
<CommandBinding
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
/>
</c:DIYGrid.CommandBindings>
<c:DIYGrid.RowTemplate>
<DataTemplate>
<usp:SyntheticManagementRowView/>
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<TextBlock>
<Run Text="Synthetics:"/>
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -1,12 +1,17 @@
using mbts.spark.filters;
using mbts.spark;
using mbts.ui.controls;
using System.Windows.Controls;
namespace mbts.ui.spark {
/// <summary>
/// Interaction logic for SyntheticManagementView.xaml
/// </summary>
internal partial class SyntheticManagementView : UserControl {
partial class SyntheticManagementView : UserControl {
public enum Columns { Name, Description, n_legs, n_working_orders, SecurityKey}
public SyntheticManagementView() =>
InitializeComponent();
}
class SyntheticManagementRowView : DIYGrid.HighPerformanceRow<SyntheticManagementDialogVM.Row, SyntheticManagementView.Columns> { }
}
@@ -2,8 +2,11 @@
using bts.api;
using bts.shell;
using bts.spark;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
@@ -13,27 +16,33 @@ using System.Windows;
using static bts.shell.SyntheticManagementWindowFactory;
namespace mbts.spark {
class SyntheticManagementWindow : SparkWindow<StartupData> {
class SyntheticManagementWindow : SparkWindow<StartupData>, ISlowWindow {
const string _window_type = "SyntheticManagement";
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
StartupData _state;
OrdersModel _orders_model;
SyntheticManagementView _view;
IDisposable _subscriptions;
IReadOnlyList<SparkCommand> _commands;
readonly IWindowSettingsManager<GridSettings> _settings_manager;
public static SyntheticManagementWindow create(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state) {
var window = new SyntheticManagementWindow(factory, bus, orders_model);
window.initialize(state);
return window;
}
SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model)
public SyntheticManagementWindow(SyntheticManagementWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, WindowSettingsManager<GridSettings> settings_manager)
: base("Synthetic Management", bus, factory, SparkCommands.ShowSyntheticManagement.icon) {
_orders_model = orders_model;
_state = state;
_settings_manager = settings_manager;
Icon.Freeze();
}
public void thread_init() {
Log.notnull(_state);
var vm_connectable_builder = new ConnectableSeriesBuilder<SyntheticManagementDialogVM>(_vm.observable);
var current_row_source = vm_connectable_builder.create(vm => vm.current_row_observable);
var current_row_connectable_builder = new ConnectableSeriesBuilder<SyntheticManagementDialogVM.Row>(current_row_source);
Commands = new SparkCommand[] {
_commands = new SparkCommand[] {
SparkCommand.create("Add", Icons.AddSynthetic, SparkTags.ShowSyntheticCreateTag).complete(
() => new SecurityCriteria(), () => true
), SparkCommand.create("Remove", Icons.RemoveSynthetic, SparkTags.SyntheticDeleteTag).complete(
@@ -46,12 +55,29 @@ namespace mbts.spark {
current_row_connectable_builder.create(observe_row_strategy),
rs => rs.HasValue, select_row_strategy_tuple
), SparkCommands.ShowLadder.complete(
vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault
vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ConvertUtils.ValueOrDefault
), SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData {
filters = new OrderBookWindowFactory.FilterStates {
security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security))
}
})
}
), SparkCommands.window_settings_command(new DialogCommand(() => {
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
_settings_manager,
us => us.synthetic_management_grid,
UserSettings.FileExtensions.GRID,
s => _vm.value.grid_settings_manager.create_vm(
// we need to get the most up to date GridSettings when launching b/c users may
// have changed some settings on the grid directly, like column widths.
s.with(_vm.value.columns.Select( c => c.create())),
this.observe_title()
)
);
// TODO: bind to main window's Title
var dialog = new SimpleGridSettingsDialog { Title = "Synthetic Management Grid Settings", DataContext = vm };
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
}))
};
_subscriptions = Disposable.Create(
@@ -69,21 +95,21 @@ namespace mbts.spark {
uint select_security_id(RowModificationParams? rmp) => rmp.HasValue ? rmp.Value.security.Id : 0;
(SecurityImmutable, ExecutionStrategyImmutable) select_row_strategy_tuple(RowStrategy? rs) => rs.HasValue ? (rs.Value.security, rs.Value.strategy) : default;
bool can_execute(RowModificationParams? rmp) => rmp.HasValue && !rmp.Value.has_working_orders;
}
public void initialize(StartupData state) {
Log.notnull(state);
_state = state;
_vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, state.sorting_criteria, state.filters ?? new FilterStates());
_vm.value = new SyntheticManagementDialogVM(_bus, _orders_model, _settings_manager, _state.sorting_criteria, _state.filters ?? new FilterStates());
if (_view == null) _view = new SyntheticManagementView { DataContext = _vm.value };
else _view.DataContext = _vm.value;
}
public override string window_type => _window_type;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands { get; }
public override StartupData SaveState => _state = new StartupData(_vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
public override double MinWidth => 700;
public override IEnumerable<SparkCommand> Commands => _commands;
public override StartupData SaveState => _state = _vm.value.create();
public override double MinWidth =>
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public override void Dispose() {
Disposable.dispose(ref _vm);
@@ -1,30 +1,46 @@
using bts.api;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading.Tasks;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.SyntheticManagementView;
namespace bts.shell {
class SyntheticManagementWindowFactory : ShellWindowFactory<SyntheticManagementWindowFactory.StartupData> {
readonly IBus _bus;
readonly OrdersModel _orders_model;
readonly UserSettingsHub _settings_hub;
public SyntheticManagementWindowFactory(SpreaderShellModule module, IBus bus, OrdersModel orders_model) : base(module, "SyntheticManagement") {
public SyntheticManagementWindowFactory(SpreaderShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model) : base(module, "SyntheticManagement") {
_bus = bus;
_orders_model = orders_model;
_settings_hub = settings_hub;
_dispose = bus.subscribe(SparkTags.ShowSyntheticManagementTag).OnDispatcher().Subscribe(show);
}
public static string col_id(Columns col) => col.ToString();
public override Task<IShellWindow> load(StartupData state) =>
Task.FromResult<IShellWindow>(SyntheticManagementWindow.create(this, _bus, _orders_model, state));
Task.FromResult<IShellWindow>(_create(state));
public async void show(SparkTags.Nothing nothing) =>
_bus.post(SparkTags.ShowWindow, await load(new StartupData()));
IShellWindow _create(StartupData state) {
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
state.grid,
get: us => us.synthetic_management_grid,
set: (us, gs) => us.with(provided_synthetic_management_gridsettings: gs));
return new SyntheticManagementWindow(this, _bus, _orders_model, state, settings_manager);
}
[ProtoContract]
public class StartupData {
public const int Version = 1;
@@ -33,11 +49,41 @@ namespace bts.shell {
public readonly List<SortingCriteria> sorting_criteria;
[ProtoMember(2)]
public readonly FilterStates filters;
[ProtoMember(3)]
public readonly GridSettings grid;
public StartupData() : this(new List<SortingCriteria>(), null) { }
public StartupData(List<SortingCriteria> sorting_criteria, FilterStates filters) {
public StartupData() : this(new List<SortingCriteria>(), null, null) { }
public StartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, GridSettings grid = default) {
this.sorting_criteria = sorting_criteria;
this.filters = filters;
this.grid = grid;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
static class DefaultColumns {
public static GridColumnSettings name => new GridColumnSettings(col_id(Columns.Name), DefaultColumnHeaders.Name, width: DefaultColumnWidths.Security);
public static GridColumnSettings description => new GridColumnSettings(col_id(Columns.Description), DefaultColumnHeaders.Description, width: DefaultColumnWidths.Description);
public static GridColumnSettings n_legs => new GridColumnSettings(col_id(Columns.n_legs), DefaultColumnHeaders.N_Legs, width: DefaultColumnWidths.N_Legs);
public static GridColumnSettings n_working_orders => new GridColumnSettings(col_id(Columns.n_working_orders), DefaultColumnHeaders.N_Working_Orders, width: DefaultColumnWidths.N_Working_Orders);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(name.id, true);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return name;
yield return description;
yield return n_legs;
yield return n_working_orders;
yield return security_key;
}
}
}
}
@@ -0,0 +1,22 @@
using mbts.ui.spark;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark {
partial class SyntheticEditorVM {
class EnableHedgeModel : RowModelBase<bool> {
public override string display_name { get; } = "Enable Hedging";
public override string help_text { get; } = "Send a hedge for this leg?";
public override string man_page { get; } = string.Empty;
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.EnableHedging;
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) =>
leg_tup.leg_params.EnableHedging = value;
public override bool has_errors(IReadOnlyCollection<LegSpreaderParamsTuple> leg_tups) =>
!leg_tups.Any(lt => lt.leg_params.EnableHedging);
}
}
}
@@ -0,0 +1,18 @@
using mbts.ui.spark;
using LegMarketDataCalculation = bts.spark.LegSpreaderParams.Types.LegMarketDataCalculation;
namespace mbts.spark {
partial class SyntheticEditorVM {
class MarketDataCalculationModel : RowModelBase<LegMarketDataCalculation> {
public override string display_name { get; } = "Market Data Calculation";
public override string help_text { get; }
public override string man_page { get; } = string.Empty;
public override LegMarketDataCalculation get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.LegMdCalc;
public override void set_value(LegSpreaderParamsTuple leg_tup, LegMarketDataCalculation value) =>
leg_tup.leg_params.LegMdCalc = value;
}
}
}
@@ -0,0 +1,18 @@
using mbts.ui.spark;
using MarketDataType = bts.spark.LegSpreaderParams.Types.MarketDataType;
namespace mbts.spark {
partial class SyntheticEditorVM {
class MarketDataTypeModel : RowModelBase<MarketDataType> {
public override string display_name { get; } = "Market Data Type";
public override string help_text { get; }
public override string man_page { get; } = string.Empty;
public override MarketDataType get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.MarketDataType;
public override void set_value(LegSpreaderParamsTuple leg_tup, MarketDataType value) =>
leg_tup.leg_params.MarketDataType = value;
}
}
}
@@ -0,0 +1,18 @@
using mbts.ui.spark;
using System;
namespace mbts.spark {
partial class SyntheticEditorVM {
class MinLeanQtyModel : RowModelBase<System.UInt32> {
public override string display_name { get; } = "Min Lean Qty";
public override string help_text { get; } = "Minimum Quantity required at this leg to quote";
public override string man_page { get; } = string.Empty;
public override System.UInt32 get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.MinLeanQty;
public override void set_value(LegSpreaderParamsTuple leg_tup, System.UInt32 value) =>
leg_tup.leg_params.QuoteParams.MinLeanQty = value;
}
}
}
@@ -0,0 +1,39 @@
using bts.shell;
using mbts.ui.spark;
namespace mbts.spark {
partial class SyntheticEditorVM {
class ConditionalQuoteEnabledModel : RowModelBase<bool> {
readonly ConditionalQuotingTicksModel _ticks;
readonly ConditionalQuotingPriceTypeModel _price_type;
public ConditionalQuoteEnabledModel(ConditionalQuotingTicksModel ticks, ConditionalQuotingPriceTypeModel price_type)
{
_ticks = ticks;
_price_type = price_type;
}
public override string display_name { get; } = string.Empty;
public override string help_text { get; } = string.Empty;
public override string man_page { get; } = string.Empty;
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.ConditionalQuoting != null;
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
if (value) {
if (leg_tup.leg_params.QuoteParams.ConditionalQuoting == null) {
leg_tup.leg_params.QuoteParams.ConditionalQuoting = SyntheticDataHelper.Defaults.ConditionalQuoting;
_ticks.apply_value(leg_tup);
_price_type.apply_value(leg_tup);
}
} else {
leg_tup.leg_params.QuoteParams.ConditionalQuoting = null;
}
_ticks.set_enabled(leg_tup, value);
_price_type.set_enabled(leg_tup, value);
}
}
}
}
@@ -0,0 +1,45 @@
using mbts.ui.spark;
using bts.spark;
using PriceType = bts.spark.ConditionalQuoting.Types.PriceType;
using mbts.collections.extensions;
using System.Collections.Generic;
using bts.shell;
namespace mbts.spark
{
partial class SyntheticEditorVM
{
class ConditionalQuotingPriceTypeModel : RowModelBase<PriceType>
{
IDictionary<uint, PriceType> _value_store = new Dictionary<uint, PriceType>();
public override string display_name { get; } = "Price Type";
public override string help_text { get; } = string.Empty;
public override string man_page { get; } = string.Empty;
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup).PriceType = _value_from_store(leg_tup);
public override PriceType get_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup) is ConditionalQuoting cond ?
cond.PriceType :
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, PriceType value)
{
_value_store[leg_tup.SecurityKey] = value;
if (_get_value(leg_tup) is ConditionalQuoting cond)
cond.PriceType = value;
}
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) => _get_value(leg_tup) != null; //TAHA_SPREADER
ConditionalQuoting _get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
PriceType _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.PriceType);
}
}
}
@@ -0,0 +1,41 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class ConditionalQuotingTicksModel : RowModelBase<int> {
IDictionary<uint, int> _value_store = new Dictionary<uint, int>();
public override string display_name { get; } = "Ticks";
public override string help_text { get; } = string.Empty;
public override string man_page { get; } = string.Empty;
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup).NTicks = _value_from_store(leg_tup);
public override int get_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup) is ConditionalQuoting cond ?
cond.NTicks :
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, int value) {
_value_store[leg_tup.SecurityKey] = value;
if (_get_value(leg_tup) is ConditionalQuoting cond)
cond.NTicks = value;
}
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup) != null;
ConditionalQuoting _get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
int _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.NTicks);
}
}
}
@@ -103,6 +103,13 @@ namespace mbts.spark {
"Reduces re-quoting updates by defining a region on either side " +
"of the working quoting order, inside which quote updates will not be sent.");
public static ParameterSection Conditional =>
new ParameterSection("Conditional", true,
"Reduces re-quoting updates by only quoting if N ticks away from the opposite " +
"inside market. Quotes are cancelled when it gets further away. 'Price Type' " +
"dictates if this logic applies to the quoting market or to the synthetic spread " +
"market");
public static ParameterSection InsideMarketThrottleThresholds =>
new ParameterSection("Inside Market Throttle Thresholds", true,
"Reduces re-quoting for quotes that are moving toward the inside market. " +
@@ -2,7 +2,6 @@
using bts.shell;
using bts.spark;
using bts.utils;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.spark;
@@ -110,7 +109,7 @@ namespace mbts.spark {
_validate_leg_count();
};
async Task commit_async(){
async Task commit_async() {
RequestUpdate?.Invoke();
bool valid = true;
if (string.IsNullOrWhiteSpace(name)) {
@@ -125,8 +124,7 @@ namespace mbts.spark {
valid = false;
}
if (valid) {
_synthetic.Synthetic.Legs.Clear();
_synthetic.Synthetic.Legs.AddRange(leg_tuples.Select(l => l.leg).OrderBy(l => l.Security, SecurityHelper.Comparer.Instance));
_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;
@@ -180,6 +178,9 @@ namespace mbts.spark {
var quote_throttle_outside_ticks = new QuoteThrottleOutsideTicksModel();
var quote_throttle_inside_ticks = new QuoteThrottleInsideTicksModel();
var conditional_quoting_ticks = new ConditionalQuotingTicksModel();
var conditional_quoting_price_type = new ConditionalQuotingPriceTypeModel();
var quote_inside_market_throttle_ticks = new QuoteInsideMarketThrottleTicksModel();
var quote_inside_market_throttle_min_move = new QuoteInsideMarketThrottleMinMoveModel();
@@ -201,11 +202,18 @@ namespace mbts.spark {
_quantity_model,
_price_mult_model,
new IsQuoteModel(),
//new EnableHedgeModel(), //TAHA_SPREADER
new HedgeRatioModel(),
new QuoteHiddenModel(),
//new MinLeanQtyModel(), //TAHA_SPREADER
//new QuoteHiddenModel(),
new MarketDataTypeModel(),
new MarketDataCalculationModel(),
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
quote_throttle_inside_ticks,
quote_throttle_outside_ticks,
new ConditionalQuoteEnabledModel(conditional_quoting_ticks, conditional_quoting_price_type),
conditional_quoting_ticks,
//conditional_quoting_price_type,
//new QuoteInsideMarketThrottleEnabledModel(quote_inside_market_throttle_ticks, quote_inside_market_throttle_min_move),
//quote_inside_market_throttle_ticks,
//quote_inside_market_throttle_min_move,
@@ -286,15 +294,14 @@ namespace mbts.spark {
base.Dispose(disposing);
}
static string _deduce_symbol(SecurityImmutable security) {
if (!string.IsNullOrWhiteSpace(security.Symbol)) return security.Symbol;
if (security.Synthetic.Legs.Count() > 0) return security.Synthetic.Legs.First().Security.Symbol;
static string _deduce_symbol(SecurityImmutable? security) {
if (!string.IsNullOrWhiteSpace(security?.Symbol)) return security?.Symbol;
if (security?.Synthetic.Legs.Count() > 0) return security?.Synthetic.Legs.First().Security.Symbol;
return string.Empty;
}
static IEnumerable<LegSpreaderParamsTuple> _get_legs(Security synthetic, ExecutionStrategyImmutable strategy) =>
synthetic.Synthetic.Legs
.OrderBy(l => l.Security, SecurityHelper.Comparer.Instance)
.Select(l => new LegSpreaderParamsTuple(l, strategy.LegParamsMap[l.Security.Id].Clone()));
void _close_new_leg_popup() {
@@ -337,4 +344,4 @@ namespace mbts.spark {
else clear_error(nameof(leg_count));
}
}
}
}
@@ -1,22 +1,31 @@
using bts.api;
using bts.shell;
using bts.spark;
using mbts.persist;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Data;
using System.Windows.Input;
using static bts.shell.SyntheticManagementWindowFactory;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.SyntheticManagementView;
namespace mbts.spark {
class SyntheticManagementDialogVM : SecurityMDGrid<SyntheticManagementDialogVM.Row, (ExecutionStrategyImmutable? strategy, SecurityMD md)> {
readonly IBus _bus;
readonly OrdersModel _orders_model;
IWindowSettingsManager<GridSettings> _settings_manager;
public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model) : this(bus, orders_model, null, null) { }
public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IReadOnlyCollection<SortingCriteria> sorting, FilterStates filters)
public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IWindowSettingsManager<GridSettings> settings_manager) : this(bus, orders_model, settings_manager, null, null) { }
public SyntheticManagementDialogVM(IBus bus, OrdersModel orders_model, IWindowSettingsManager<GridSettings> settings_manager, IReadOnlyCollection<SortingCriteria> sorting, FilterStates filters)
: base(orders_model.spreader.strategy_tuple.Select(map => map.Values), sorting) {
_bus = bus;
_orders_model = orders_model;
@@ -28,7 +37,41 @@ namespace mbts.spark {
n_working_orders_filter.load_state(filters.n_working_orders);
security_key_filter.load_state(filters.security_key);
}
_dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter);
_settings_manager = settings_manager;
// columns
add_col(Columns.Name, DefaultColumnHeaders.Name, r => r.name, nameof(Row.name), name_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Security);
add_col(Columns.Description, DefaultColumnHeaders.Description, r => r.description, nameof(Row.description), description_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.Description);
add_col(Columns.n_legs, DefaultColumnHeaders.N_Legs, r => r.n_legs.ToString(), nameof(Row.n_legs), n_legs_filter, DataGridFilterKnownTemplates.IntMinMax, DefaultColumnWidths.N_Legs);
columns.Add(new HyperlinkElementColumn<Row, Columns>(
Columns.n_working_orders,
new Binding(nameof(Row.n_working_orders)),
new Binding(nameof(Row.show_order_book)),
n_working_orders_filter,
DataGridFilterKnownTemplates.UIntMinMax) {
id = col_id(Columns.n_working_orders),
Header = DefaultColumnHeaders.N_Working_Orders,
Width = DefaultColumnWidths.N_Working_Orders
}
);
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
//
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, IDataGridColumnFilter filter, string filter_template, double width) =>
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
id = col_id(col_key),
Header = header,
sort_member_path = prop,
Width = width
});
//
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, row_vms);
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
grid_settings_manager.load_from(settings_manager.current_settings());
_resources.AddRange(
DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter),
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager)
);
}
protected override Row row_factory((ExecutionStrategyImmutable? strategy, SecurityMD md) row_model) =>
@@ -36,6 +79,20 @@ namespace mbts.spark {
protected override void on_row_removed(Row row) => row.Dispose();
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
public GridSettings get_grid_save_state() {
if (_settings_manager.instance_settings == null)
return null;
else
return grid_settings_manager.create();
}
public StartupData create() =>
new StartupData(rows_view.get_sorting_criteria(columns), _get_filter_states(), get_grid_save_state());
#region Filters
public IDataGridColumnPersistentFilter<Row, TimeFilterTuple> create_time_filter { get; } = new TimeFilterVM<Row>(r => r.create_time, nameof(Row.create_time));
public IDataGridColumnPersistentFilter<Row, string> name_filter { get; } = new RegexFilterVM<Row>(r => r.name, nameof(Row.name));
@@ -44,7 +101,7 @@ namespace mbts.spark {
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> n_working_orders_filter { get; } = new UIntMinMax<Row>(r => r.n_working_orders, nameof(Row.n_legs));
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
public FilterStates get_filter_states() =>
FilterStates _get_filter_states() =>
new FilterStates {
description = description_filter.get_state(),
create_time = create_time_filter.get_state(),
@@ -55,7 +112,7 @@ namespace mbts.spark {
};
#endregion
public class Row : SecurityMDRow, IEquatable<Row> {
public class Row : SecurityMDRow, IEquatable<Row>, IRowVM, IActionableRowVM {
public readonly ExecutionStrategyImmutable? strategy;
readonly IBus _bus;
@@ -85,6 +142,17 @@ namespace mbts.spark {
public uint n_working_orders => _n_working_orders.Value;
public ICommand show_ladder { get; }
public ICommand show_order_book { get; }
public ICommand default_command => show_ladder;
public ObservableCollection<RowAnimation> row_animations => new ObservableCollection<RowAnimation>();
public IReadOnlyList<IGridCellStore> cell_stores => new IGridCellStore[0];
public event Action invalidated;
public void raise_invalidated() => invalidated?.Invoke();
public bool Equals(Row other) =>
other != null &&
@@ -96,6 +164,8 @@ namespace mbts.spark {
public override int GetHashCode() =>
security_key.GetHashCode();
void _show_ladder() =>
_bus.post(SparkTags.ShowLadderTag, md);
@@ -53,7 +53,7 @@ namespace bts.shell {
Configuration.get<bts.config.System>().config_port);
string temp = uristr;
if (uristr.StartsWith("bts://m/")) {
temp = uristr.Replace("bts://m/", http_base + "doc/manual/");
temp = uristr.Replace("bts://m/", http_base + "docs/");
}
else {
temp = uristr.Replace("bts://", http_base);
@@ -38,10 +38,10 @@ namespace bts.shell {
//SparkCommands.ShowAuditLogs,
//SparkCommands.ShowDiagnostics,
SparkCommands.ShowReportIssue.complete("Created from launcher"),
SparkCommands.ShowAboutSpark,
// SparkCommands.ShowReleaaseNotes,
SparkCommands.ShowActivityLog,
SparkCommands.ShowManual,
SparkCommands.ShowAboutSpark,
};
public Task<IShellWindow> create(Stream load_stream) {
@@ -1,6 +1,7 @@
using bts.api;
using bts.api.orderman;
using bts.spark;
using mbts.persist;
using mbts.spark.filters;
using mbts.spark.utilities;
using mbts.ui;
@@ -13,6 +14,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Windows.Data;
using static bts.api.orderman.Fill.Types;
using static bts.shell.FillsWindowFactory;
@@ -23,13 +25,16 @@ using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
namespace mbts.spark {
class FillsVM : SecurityGrid<FillsVM.Row, FillTuple> {
readonly OrdersModel _model;
public FillsVM(IBus bus, OrdersModel model, StartupData state)
: this(bus, model, _get_observable(bus, model, state), state.sorting_criteria, state.filters) { }
IWindowSettingsManager<GridSettings> _settings_manager;
FillsVM(IBus bus, OrdersModel model, IObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
: base(RXCollection.CreateIncrementalUpdate(fills_source), sorting_criteria) {
public FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IWindowSettingsManager<GridSettings> settings_manager)
: this(bus, model, state_obs, _get_observable(bus, model, state_obs), settings_manager) { }
FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IConnectableObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> fills_source, IWindowSettingsManager<GridSettings> settings_manager)
: base(RXCollection.CreateIncrementalUpdate(fills_source), null) {
_model = model;
_settings_manager = settings_manager;
account_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_accounts().Select(d => d.Values), r => r.account, nameof(Row.account));
user_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_users().Select(d => d.Values), r => r.user, nameof(Row.user));
security_filter = DiscreteValuesFilterVM.Create<Row, SecurityImmutable, SecurityCriteria>(
@@ -44,30 +49,6 @@ namespace mbts.spark {
SecurityDataHelper.EqualityComparer.IDComparer,
Comparer<SecurityImmutable>.Default
);
if (filters != null) {
fill_time_filter.load_state(filters.fill_time);
time_in_force_filter.load_state(filters.time_in_force);
order_type_filter.load_state(filters.order_type);
account_filter.load_state(filters.account);
user_filter.load_state(filters.user);
exchange_filter.load_state(filters.exchange);
security_filter.load_state(filters.security);
side_filter.load_state(filters.side);
price_filter.load_state(filters.price);
size_filter.load_state(filters.size);
fill_kind_filter.load_state(filters.fill_kind);
fill_key_filter.load_state(filters.fill_key);
order_key_filter.load_state(filters.order_key);
security_key_filter.load_state(filters.security_key);
} else {
fill_time_filter.load_state(
new TimeFilterTuple(ui.controls.TimeFilterMode.CMEOpen,
TimeTextTuple.FromDateTime(DateTime.Today),
TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1))
)
);
fill_kind_filter.load_state(EnumHelper.get_values_except(FillKind.Sod).ToArray());
}
// columns
add_col(Columns.Security, DefaultColumnHeaders.Security, r => r.security.ToString(), nameof(Row.security), security_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Security);
add_col(Columns.FillTime, DefaultColumnHeaders.FillTime, r => r.fill_time.ToString("M/d h:mm:ss tt"), nameof(Row.fill_time), fill_time_filter, DataGridFilterKnownTemplates.TimeFilter, DefaultColumnWidths.Time);
@@ -75,30 +56,83 @@ namespace mbts.spark {
add_col(Columns.Account, DefaultColumnHeaders.Account, r => r.account, nameof(Row.account), account_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Account);
add_col(Columns.Exchange, DefaultColumnHeaders.Exchange, r => r.exchange.ToString(), nameof(Row.exchange), exchange_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Exchange);
add_col(Columns.TimeInForce, DefaultColumnHeaders.TimeInForce, r => r.time_in_force.ToString(), nameof(Row.time_in_force), time_in_force_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.TimeInForce);
columns.Add(new OrderTypeColumn<Columns>(Columns.OrderType, new Binding(nameof(Row.order_type)), new Binding(nameof(Row.order_metadata)), order_type_filter) { sort_member_path = nameof(Row.order_type) });
columns.Add(new SideColumn<Row, Columns>(Columns.Side, r => r.side, side_filter) { sort_member_path = nameof(Row.side) });
columns.Add(new OrderTypeColumn<Columns>(Columns.OrderType, new Binding(nameof(Row.order_type)), new Binding(nameof(Row.order_metadata)), order_type_filter) { id = col_id(Columns.OrderType), sort_member_path = nameof(Row.order_type) });
columns.Add(new SideColumn<Row, Columns>(Columns.Side, r => r.side, side_filter) { id = col_id(Columns.Side), sort_member_path = nameof(Row.side) });
add_col(Columns.Size, DefaultColumnHeaders.Size, r => r.size.ToString(), nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.Size);
add_col(Columns.Price, DefaultColumnHeaders.Price, r => r.format_price(r.price), nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price);
add_col(Columns.FillKind, "Fill Kind", r => r.fill_kind.ToString(), nameof(Row.fill_kind), fill_kind_filter, DataGridFilterKnownTemplates.DiscreteValues, 70);
add_col(Columns.FillKind, DefaultColumnHeaders.FillKind, r => r.fill_kind.ToString(), nameof(Row.fill_kind), fill_kind_filter, DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.FillKind);
add_col(Columns.FillKey, DefaultColumnHeaders.FillKey, r => r.fill_key.ToString(), nameof(Row.fill_key), fill_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.FillKey);
add_col(Columns.OrderKey, DefaultColumnHeaders.OrderKey, r => r.order_key.ToString(), nameof(Row.order_key), order_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.OrderKey);
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
//
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, IDataGridColumnFilter filter, string filter_template, double width) =>
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
id = col_id(col_key),
Header = header,
sort_member_path = prop,
Width = width
});
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, row_vms);
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
grid_settings_manager.load_from(settings_manager.current_settings());
//
_resources.Add(DataGridFilterHelper.hook_filters(rows_view,
fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter,
security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter
));
_resources.AddRange(
state_obs.OnDispatcher().Subscribe(s =>
{
using (rows_view.DeferRefresh()) {
if (s.grid != null) {
// this means we are not using defaults,
// Note: settings_manager in turn notifies grid_settings_manager of this update
_settings_manager.instance_settings = s.grid;
}
if (s.filters is FilterStates filters) {
fill_time_filter.load_state(filters.fill_time);
time_in_force_filter.load_state(filters.time_in_force);
order_type_filter.load_state(filters.order_type);
account_filter.load_state(filters.account);
user_filter.load_state(filters.user);
exchange_filter.load_state(filters.exchange);
security_filter.load_state(filters.security);
side_filter.load_state(filters.side);
price_filter.load_state(filters.price);
size_filter.load_state(filters.size);
fill_kind_filter.load_state(filters.fill_kind);
fill_key_filter.load_state(filters.fill_key);
order_key_filter.load_state(filters.order_key);
security_key_filter.load_state(filters.security_key);
} else {
fill_time_filter.load_state(
new TimeFilterTuple(ui.controls.TimeFilterMode.CMEOpen,
TimeTextTuple.FromDateTime(DateTime.Today),
TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1))
)
);
fill_kind_filter.load_state(EnumHelper.get_values_except(FillKind.Sod).ToArray());
}
rows_view.set_sort_descriptions(s.sorting_criterion, columns);
}
}),
DataGridFilterHelper.hook_filters(rows_view,
fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter,
security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter
),
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
fills_source.Connect()
);
}
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
public GridSettings get_grid_save_state() {
if (_settings_manager.instance_settings == null)
return null;
else
return grid_settings_manager.create();
}
#region Filters
public IDataGridColumnPersistentFilter<Row, TimeFilterTuple> fill_time_filter { get; } = new TimeFilterVM<Row>(r => r.fill_time, nameof(Row.fill_time));
public IDataGridColumnPersistentFilter<Row, TimeInForce[]> time_in_force_filter { get; } = DiscreteValuesFilterVM.Create<Row, TimeInForce>(EnumHelper.get_values<TimeInForce>().ToLArray(), r => r.time_in_force, nameof(Row.time_in_force));
@@ -144,12 +178,15 @@ namespace mbts.spark {
} else return model.on_fill_tuples;
}
static IConnectableObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> _get_observable(IBus bus, OrdersModel model, IObservable<StartupData> state) =>
state.SelectManyInSeries(s => _get_observable(bus, model, s));
public class Row : SecurityRow, IEquatable<Row>, IRowVM {
readonly OrdersModel _model;
readonly FillImmutable _fill;
readonly OrderRecordImmutable _order;
public Row(FillTuple fill_tuple, OrdersModel model) : base(fill_tuple.security_md.HasValue ? fill_tuple.security_md.Value.security : SecurityDataHelper.Defaults.Security) {
public Row(FillTuple fill_tuple, OrdersModel model) : base(fill_tuple.security_md.HasValue ? fill_tuple.security_md.Value.security : SecurityDataHelper.Defaults.SecurityImmutable) {
_fill = fill_tuple.fill;
_order = fill_tuple.order ?? OrderDataHelper.Defaults.OrderRecordImmutable;
_model = model;
@@ -170,9 +207,6 @@ namespace mbts.spark {
public string fill_key => _fill.FillKey;
public string order_key => _order.Key.ToString();
ObservableCollection<RowAnimation> IRowVM.row_animations { get; } = new ObservableCollection<RowAnimation>();
IReadOnlyList<IGridCellStore> IRowVM.cell_stores => new IGridCellStore[0];
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();
@@ -19,8 +19,13 @@
</UserControl.Resources>
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<c:DIYGrid
x:Name="_grid"
Grid.Row="0"
Columns="{Binding columns}"
CurrentRow="{Binding current_row}"
Rows="{Binding rows_view}"
@@ -42,6 +47,16 @@
</DataTemplate>
</c:DIYGrid.RowTemplate>
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<TextBlock>
<Run Text="Fills:"/>
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -2,9 +2,16 @@
using bts.api;
using bts.shell;
using bts.spark;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using StartupData = bts.shell.FillsWindowFactory.StartupData;
@@ -13,29 +20,76 @@ namespace mbts.spark {
const string _window_type = "Fills", _thread_id = "Fills";
readonly AllowOnce _init_guard = new AllowOnce("Fills");
readonly OrdersModel _orders_model;
readonly IWindowSettingsManager<GridSettings> _settings_manager;
FillsVM _vm;
FillsView _view;
StartupData _state;
IReadOnlyList<SparkCommand> _commands;
IDisposable _subscriptions;
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state)
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, WindowSettingsManager<GridSettings> settings_manager)
: base("Fills", bus, factory, SparkCommands.ShowFills.icon) {
_orders_model = orders_model;
_state = state;
_settings_manager = settings_manager;
}
event Action<FillsWindowFactory.StartupData> _state_changed;
public override string window_type => _window_type;
public override UIElement Content => _view;
public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.GetSortingCriteria(), _vm.get_filters());
public override double MinWidth => 1050 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
public override IEnumerable<SparkCommand> Commands => _commands;
public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.rows_view.get_sorting_criteria(_vm.columns), _vm.get_filters(), _vm.get_grid_save_state());
public override double MinWidth =>
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public string thread_id => _thread_id;
public void thread_init() {
_init_guard.check_access();
Log.notnull(_state);
_vm = new FillsVM(_bus, _orders_model, _state);
var state_src = Observable.FromEvent<Action<FillsWindowFactory.StartupData>, FillsWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
_vm = new FillsVM(_bus, _orders_model, state_src, _settings_manager);
_view = new FillsView { DataContext = _vm };
_commands = new List<SparkCommand> {
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
SparkCommands.window_settings_command(new DialogCommand(() => {
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
_settings_manager,
us => us.fills_grid,
UserSettings.FileExtensions.GRID,
s => _vm.grid_settings_manager.create_vm(
// we need to get the most up to date GridSettings when launching b/c users may
// have changed some settings on the grid directly, like column widths.
s.with(_vm.columns.Select( c => c.create())),
this.observe_title()
)
);
// TODO: bind to main window's Title
var dialog = new SimpleGridSettingsDialog { Title = $"{_window_type} Grid Settings", DataContext = vm };
vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
}))
};
_subscriptions = Disposable.Create(() =>
_state_changed?.Invoke(
_state = new StartupData(_state.orders,
_vm.rows_view.get_sorting_criteria(_vm.columns),
_vm.get_filters(),
_vm.grid_settings_manager.create()
)
)
);
}
public override void Dispose() => Disposable.dispose(ref _vm);
public override void Dispose() {
Disposable.dispose(ref _vm);
Commands.OfType<IDisposable>().DisposeAll();
}
}
}
@@ -1,49 +1,118 @@
using bts.api;
using bts.api.orderman;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.FillsView;
namespace bts.shell {
class FillsWindowFactory : ShellWindowFactory<FillsWindowFactory.StartupData> {
readonly IBus _bus;
readonly OrdersModel _orders_model;
readonly UserSettingsHub _settings_hub;
public FillsWindowFactory(TradingShellModule module, IBus bus, OrdersModel orders_model)
public FillsWindowFactory(TradingShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model)
: base(module, "Fills") {
_bus = bus;
_orders_model = orders_model;
_settings_hub = settings_hub;
_dispose = bus.subscribe(SparkTags.ShowFillsTag).OnDispatcher().Subscribe(show);
}
public static string col_id(Columns col) => col.ToString();
public override Task<IShellWindow> load(StartupData state) =>
Task.FromResult<IShellWindow>(new FillsWindow(this, _bus, _orders_model, state));
Task.FromResult(_create(state));
public async void show(StartupData state) =>
_bus.post(SparkTags.ShowWindow, await load(state));
IShellWindow _create(StartupData state) {
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
state.grid,
get: us => us.fills_grid,
set: (us, gs) => us.with(provided_fills_gridsettings: gs));
return new FillsWindow(this, _bus, _orders_model, state, settings_manager);
}
[ProtoContract]
public class StartupData {
public const int Version = 1;
public StartupData() { }
public StartupData(uint[] orders) : this(orders, null, null) { }
public StartupData(FilterStates filters) : this(null, null, filters) { }
public StartupData(uint[] orders, List<SortingCriteria> sorting_criteria, FilterStates filters) {
this.orders = orders;
this.sorting_criteria = sorting_criteria;
this.filters = filters;
}
[ProtoMember(1)]
public uint[] orders;
[ProtoMember(2)]
public List<SortingCriteria> sorting_criteria;
List<SortingCriteria> _sorting_criteria;
[ProtoMember(3)]
public FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
public StartupData() { }
public StartupData(uint[] orders) : this(orders, null, null) { }
public StartupData(FilterStates filters) : this(null, null, filters) { }
public StartupData(uint[] orders, List<SortingCriteria> sorting_criteria, FilterStates filters, GridSettings grid = default) {
this.orders = orders;
this._sorting_criteria = sorting_criteria;
this.filters = filters;
this.grid = grid;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList());
public static StartupData Default() =>
new StartupData(null, null, new FilterStates(), null);
static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings fill_time => new GridColumnSettings(col_id(Columns.FillTime), DefaultColumnHeaders.FillTime, width: DefaultColumnWidths.Time);
public static GridColumnSettings user => new GridColumnSettings(col_id(Columns.User), DefaultColumnHeaders.User, width: DefaultColumnWidths.User);
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange);
public static GridColumnSettings time_in_force => new GridColumnSettings(col_id(Columns.TimeInForce), DefaultColumnHeaders.TimeInForce, width: DefaultColumnWidths.TimeInForce);
public static GridColumnSettings order_type => new GridColumnSettings(col_id(Columns.OrderType), DefaultColumnHeaders.OrderType, width: DefaultColumnWidths.OrderType);
public static GridColumnSettings side => new GridColumnSettings(col_id(Columns.Side), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Side);
public static GridColumnSettings quantity => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind);
public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey);
public static GridColumnSettings order_key => new GridColumnSettings(col_id(Columns.OrderKey), DefaultColumnHeaders.OrderKey, width: DefaultColumnWidths.OrderKey);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(fill_time.id, false);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return fill_time;
yield return user;
yield return account;
yield return exchange;
yield return time_in_force;
yield return order_type;
yield return side;
yield return quantity;
yield return price;
yield return fill_kind;
yield return fill_key;
yield return order_key;
yield return security_key;
}
}
}
}
[ProtoContract]
@@ -4,6 +4,7 @@ using mbts.config;
using mbts.persist;
using mbts.spark;
using mbts.ui;
using mbts.ui.spark;
using ProtoBuf;
using System;
using System.Collections.Generic;
@@ -22,15 +23,17 @@ namespace bts.shell {
readonly IWindowManager _wm;
readonly UserSettingsHub _settings_hub;
readonly OrdersModel _orders_model;
readonly MarketDataAuxiliaryModel _md_auxiliary_model;
readonly int _max_ladders_open;
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model)
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model, MarketDataAuxiliaryModel md_auxiliary_model)
: base(owner, "SparkLadder") {
_bus = bus;
_wm = wm;
_settings_hub = settings_hub;
_orders_model = orders_model;
_md_auxiliary_model = md_auxiliary_model;
var knobs = Configuration.get<bts.config.LadderKnobs>();
_max_ladders_open = (int)(knobs.max_open_ladders);
_security_timeout = TimeSpan.FromSeconds(Math.Max(15, knobs.security_timeout_secs));
@@ -56,12 +59,14 @@ namespace bts.shell {
IShellWindow create_ladder_window(SecurityMD md, LadderSettings state_settings) {
var id = Interlocked.Increment(ref _ladder_count) % Environment.ProcessorCount;
var view = new Lazy<UIElement>(() => {
var model = new LadderModel(md, _orders_model);
var model = new LadderModel(md, _orders_model, _md_auxiliary_model);
var settings_manager = new WindowSettingsManager<LadderSettings>(
_settings_hub, state_settings,
_settings_hub, state_settings == null || state_settings.use_defaults ? null : state_settings,
us => sanitize_ladder_settings(us.ladder),
(us, v) => us.with(sanitize_ladder_settings(v)));
var vm = new LadderVM(_bus, model, settings_manager);
var init = state_settings ?? settings_manager.default_settings;
init.use_defaults |= state_settings == null;
var vm = new LadderVM(_bus, model, settings_manager, init);
return new LadderView(vm);
});
string thread = Configuration.get<bts.config.toggles.LaddersOnThreads>().enabled ? $"Ladder{id:00}" : "";
@@ -85,6 +90,12 @@ namespace bts.shell {
s.session_high_line = LadderSettings.Default.session_high_line;
if (s.session_range_fill == null)
s.session_range_fill = LadderSettings.Default.session_range_fill;
if (s.volume_column == null)
s.volume_column = LadderSettings.Default.volume_column;
if (s.size_display == null)
s.size_display = MarketSizeDisplay.get_default();
if (s.size_display.normal == null)
s.size_display = s.size_display.with(MarketSizeDisplay.get_default().normal);
s.row_height = LadderRender.measure_row_height(s);
return s;
}
@@ -0,0 +1,166 @@
<!-- MaxHeight will force only x rows before vertical scrollbar shows up -->
<!-- MinWidth will give enough room beyond the last column so that the the verical scrollbar does not eclipse the column content -->
<UserControl x:Class="mbts.spark.MiniOrderBookView" x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sp="clr-namespace:mbts.spark"
xmlns:uc="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:usp="clr-namespace:mbts.ui.spark"
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:order="clr-namespace:bts.api.orderman;assembly=spark_api"
mc:Ignorable="d"
MaxHeight="240"
MinWidth="875"
d:DesignHeight="600" d:DesignWidth="1080"
FocusManager.IsFocusScope="True"
Background="{StaticResource CommonBackground}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
<DataTemplate x:Key="{x:Static sp:MiniOrderBookView.SubTypeFilterTemplateKey}">
<usp:FilterContentControl
ApplyCommandParameter="{Binding}"
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
ClearCommandParameter="{Binding}"
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
>
<c:ListBoxEx
ItemsSource="{Binding items}"
Selection="{Binding selected_items}"
SelectionMode="Multiple"
ItemContainerStyle="{StaticResource ListBoxItemCheckBoxStyle}"
>
<c:ListBoxEx.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource OrderSubTypeLongDisplayTextConverter}}"/>
</DataTemplate>
</c:ListBoxEx.ItemTemplate>
</c:ListBoxEx>
</usp:FilterContentControl>
</DataTemplate>
<Style x:Key="{x:Static sp:MiniOrderBookView.RowStyleKey}" TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding status}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={uc:Nested uc:BTSColors.Orders}}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="{x:Static sp:MiniOrderBookView.RowTemplateKey}">
<sp:MiniOrderBookRowView/>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<AdornerDecorator>
<Border BorderThickness="4" BorderBrush="LightGray">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<c:DIYGrid
x:Name="_grid"
Grid.Row="0"
Columns="{Binding columns}"
CurrentRow="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Rows="{Binding rows}"
RowStyle="{StaticResource {x:Static sp:MiniOrderBookView.RowStyleKey}}"
RowTemplate="{StaticResource {x:Static sp:MiniOrderBookView.RowTemplateKey}}"
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
RowsHorizontalScrollBarVisibility="Collapsed"
RowHeight="20"
>
<c:DIYGrid.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
<Style x:Key="{x:Static sp:OrderBookView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="Transparent" Height="16" Padding="0,5,5,5" Width="16">
<Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
<Path.RenderTransform>
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="RenderTransform" TargetName="ExpandPath">
<Setter.Value>
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
</Setter.Value>
</Setter>
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ToolTip}">
<Style.Triggers>
<Trigger Property="Content" Value="{x:Static sys:String.Empty}">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="Content" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</c:DIYGrid.Resources>
<c:DIYGrid.InputBindings>
<!--<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/> -->
</c:DIYGrid.InputBindings>
<c:DIYGrid.CommandBindings>
<CommandBinding
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
/>
</c:DIYGrid.CommandBindings>
</c:DIYGrid>
</Grid>
</Border>
</AdornerDecorator>
</UserControl>
@@ -0,0 +1,26 @@
using mbts.ui.controls;
using System.Windows.Controls;
namespace mbts.spark {
internal partial class MiniOrderBookView : UserControl {
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
public const string
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
RowStyleKey = "RowStyle",
RowTemplateKey = "RowTemplateKey",
CVRowStyleKey = "CVRowStyle",
CVRowTemplateKey = "CVRowTemplateKey";
public MiniOrderBookView(OrderBookVM vm) {
InitializeComponent();
DataContext = vm;
}
}
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
public MiniOrderBookRowView() : base() { }
}
}
@@ -1,168 +0,0 @@
using bts.utils;
using mbts.ui;
using mbts.ui.controls;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace mbts.spark {
partial class OrderBookRowVM {
public interface IOrderBookCell : DIYGrid.IGridCell, IComparable { }
public interface IOrderBookCell<T> : IOrderBookCell {
T value { get; }
IOrderBookCellStore<T> store { get; }
}
public interface IOrderBookCellStore : DIYGrid.IGridCellStore {
OrderBookRowVM row { get; }
new ICVOrderBookColumn column { get; }
string prop_name { get; }
new IOrderBookCell current { get; }
//
IOrderBookCellStore select_other(OrderBookRowVM row);
}
public interface IOrderBookCellStore<T> : IOrderBookCellStore, IComparer<T>, IEqualityComparer<T> {
new IOrderBookCell<T> current { get; }
new IOrderBookCellStore<T> select_other(OrderBookRowVM row);
//
T get_value(OrderBookRowVM row);
string format(T value);
}
public static class CellFactory {
public static IOrderBookCellStore<T> create<T>(OrderBookRowVM row, ICVOrderBookColumn column, string prop_name, Func<OrderBookRowVM, T> get_value, Func<OrderBookRowVM, IOrderBookCellStore<T>> select_other, Func<T, string> format = null) =>
new OrderBookCellStore<T>(row, column, prop_name, get_value, select_other, format, new Comparison<T>(Comparer<T>.Default.Compare), EqualityComparer<T>.Default);
public static IOrderBookCellStore<T> create<T>(OrderBookRowVM row, ICVOrderBookColumn column, string prop_name, Func<OrderBookRowVM, T> get_value, Func<OrderBookRowVM, IOrderBookCellStore<T>> select_other, Comparison<T> comparison, Func<T, string> format = null) =>
new OrderBookCellStore<T>(row, column, prop_name, get_value, select_other, format, comparison, EqualityComparer<T>.Default);
public static IOrderBookCellStore<T?> create_nullable<T>(OrderBookRowVM row, ICVOrderBookColumn column, string prop_name, Func<OrderBookRowVM, T?> get_value, Func<OrderBookRowVM, IOrderBookCellStore<T?>> select_other, Func<T?, string> format = null)
where T : struct => new OrderBookCellStore<T?>(row, column, prop_name, get_value, select_other, format, CompareUtils.nullable_comparison<T>(Comparer<T>.Default.Compare), EqualityComparer.CreateNullable<T>(EqualityComparer<T>.Default.Equals));
public static IOrderBookCellStore<T?> create_nullable<T>(OrderBookRowVM row, ICVOrderBookColumn column, string prop_name, Func<OrderBookRowVM, T?> get_value, Func<OrderBookRowVM, IOrderBookCellStore<T?>> select_other, Func<T, T, int> compare, Func<T?, string> format = null)
where T : struct =>
new OrderBookCellStore<T?>(row, column, prop_name, get_value, select_other, format, CompareUtils.nullable_comparison(compare), EqualityComparer.CreateNullable<T>(EqualityComparer<T>.Default.Equals));
class OrderBookCellStore<T> : IOrderBookCellStore<T>, INotifyPropertyChanged {
readonly OrderBookRowVM _row;
readonly ICVOrderBookColumn _column;
readonly string _prop_name;
readonly Func<OrderBookRowVM, T> _get_value;
readonly Func<OrderBookRowVM, IOrderBookCellStore<T>> _select_other;
readonly Func<T, string> _format;
readonly Comparison<T> _comparison;
readonly IEqualityComparer<T> _eq_comparer;
OrderBookCell<T> _current;
// Property changed handler to avoid WPF binding leak
// https://blog.jetbrains.com/dotnet/2014/09/04/fighting-common-wpf-memory-leaks-with-dotmemory/
public event PropertyChangedEventHandler PropertyChanged = (_,__) => { };
public OrderBookCellStore(
OrderBookRowVM row,
ICVOrderBookColumn column,
string prop_name,
Func<OrderBookRowVM, T> get_value,
Func<OrderBookRowVM, IOrderBookCellStore<T>> select_other,
Func<T, string> format,
Comparison<T> comparison,
IEqualityComparer<T> eq_comparer) {
//
_row = row;
_column = column;
_prop_name = prop_name;
_get_value = get_value;
_select_other = select_other;
_format = format ?? new Func<T, string>(v => v?.ToString() ?? string.Empty);
_comparison = comparison;
_eq_comparer = eq_comparer;
_current = new OrderBookCell<T>(this, _get_value(_row));
}
public OrderBookRowVM row => _row;
public ICVOrderBookColumn column => _column;
public string prop_name => _prop_name;
public IOrderBookCell<T> current => _current;
IOrderBookCell IOrderBookCellStore.current => _current;
DIYGrid.IGridColumn DIYGrid.IGridCellStore.column => _column;
DIYGrid.IGridCell DIYGrid.IGridCellStore.current => _current;
public ObservableCollection<CellAnimation> animations { get; } = new ObservableCollection<CellAnimation>();
public int Compare(T x, T y) => _comparison(x, y);
public bool Equals(T x, T y) => _eq_comparer.Equals(x, y);
public string format(T value) => _format(value);
public int GetHashCode(T obj) => _eq_comparer.GetHashCode(obj);
public T get_value(OrderBookRowVM row) => _get_value(row);
public IOrderBookCellStore<T> select_other(OrderBookRowVM row) => _select_other(row);
IOrderBookCellStore IOrderBookCellStore.select_other(OrderBookRowVM row) => select_other(row);
public bool refresh() {
T v = _get_value(_row);
if (!_eq_comparer.Equals(v, _current.value)) {
_current = new OrderBookCell<T>(this, v);
_row.RaisePropertyChanged(_prop_name);
return true;
} else return false;
}
public void regenerate_cell() {
_current = new OrderBookCell<T>(this, _current.value);
_row.RaisePropertyChanged(_prop_name);
}
public void invalidate_text() => regenerate_cell();
}
class OrderBookCell<T> : IOrderBookCell<T> {
string _text;
public OrderBookCell(OrderBookCellStore<T> store, T value) {
this.store = store;
this.value = value;
}
public string text => _text ?? (_text = store.format(value));
public T value { get; }
public IOrderBookCellStore<T> store { get; }
public int CompareTo(OrderBookCell<T> other) {
if (other == null)
return 1;
var lin = lineage(store.row);
var olin = lineage(other.store.row);
int c = 0;
using (var en = lin.GetEnumerator())
using (var oen = olin.GetEnumerator()) {
while (en.MoveNext() && oen.MoveNext()) {
if (en.Current.order_key.value != oen.Current.order_key.value) {
c = store.Compare(
store.select_other(en.Current).current.value,
store.select_other(oen.Current).current.value);
if (c != 0)
return c;
else {
c = en.Current.order_key.value.CompareTo(
oen.Current.order_key.value);
return store.column.SortDirection.HasValue && store.column.SortDirection.Value == ListSortDirection.Ascending ? c : -c;
}
}
}
}
return store.column.SortDirection.HasValue && store.column.SortDirection.Value == ListSortDirection.Ascending ?
lin.Count.CompareTo(olin.Count) :
olin.Count.CompareTo(lin.Count);
}
public int CompareTo(object obj) => CompareTo(obj as OrderBookCell<T>);
public override string ToString() => text;
}
}
}
}
@@ -1,11 +1,14 @@
using bts.api;
using bts;
using bts.api;
using bts.api.orderman;
using bts.utils;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.converters;
using mbts.ui.utility;
using mbts.ui.utility.text;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -18,37 +21,53 @@ using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using static bts.shell.OrderBookWindowFactory;
using static mbts.spark.OrderBookRowVM;
using static mbts.spark.OrderBookView;
using static mbts.ui.controls.DIYGrid;
using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
namespace mbts.spark {
// This interface is used to hide ColumnBase.SortDirection, and offer a SortDirection member that does NOT require a DependencyProperty lookup.
// In OrderBookRowVM, each "cell(property)" has a corresponding TreeComparable property that the column uses for sorting
// - each columns' sort_member_path points to the TreeComparable.
// The TreeComparable class needs to know which direction it is sorting so it can force child-orders to always be beneath their parents.
// - when comparing with their parent, it must return 1 if Ascending, -1 if Descending.
// Since the TreeComparables in each row must access their respective column SortDirections in their CompareTo methods, those DP lookups could add significant overhead in large collections.
// I considered adding ListSortDirection members directly to OrderBookVM, and letting the TreeComparables refer to that property,
// but decided it would be better to let the Columns own it so we wouldn't have to worry about keeping the 2 props in sync.
// TODO: Maybe binding to the SortDirection would be less smelly?
// - initially, I tried to bind to the SortDirection of each column, unfortunately, the binding target was not updated before the ICollectionView sorting took place.
// I decided it was not worth changing the DIYGrid.Sorting function to force the SortDirection binding targets to update immediately, before the ICollectionView sorting.
interface ICVOrderBookColumn : IKeyedColumn<Columns>, IGridColumn<OrderBookRowVM> {
interface ICVOrderBookColumn : IKeyedColumn<Columns>, IGridColumn {
string sort_member_path { get; }
ListSortDirection? SortDirection { get; }
IDataGridColumnFilter<OrderBookRowVM> filter { get; }
ListSortDirection? SortDirection { get; set; }
IDataGridColumnFilter<OrderRecordTuple> filter { get; }
IComparer<OrderBookRowVM> comparer { get; }
}
class OrderBookSortingData : IEquatable<OrderBookSortingData> {
public readonly IReadOnlyList<SortingCriteria> sorting_criterion;
public readonly IComparer<OrderBookRowVM> comparer;
public readonly string desc;
public OrderBookSortingData(IEnumerable<ICVOrderBookColumn> column_sorting_hierarchy) {
var comp_builder = new ComparerBuilder<OrderBookRowVM>();
foreach (var col in column_sorting_hierarchy)
comp_builder.add(col.SortDirection.Value == ListSortDirection.Ascending ? col.comparer : col.comparer.reverse());
if (!column_sorting_hierarchy.Any(col => col.key == Columns.OrderKey)) {
// the resultant comparer must to be 100% deterministic, as SortedList (the building block of the SortedTree) uses a BinarySearch to find/remove items and does NOT check for item-equality.
comp_builder.add(Comparer<OrderBookRowVM>.Create((l, r) => l.order_key.CompareTo(r.order_key)));
}
comparer = comp_builder.to_comparer();
sorting_criterion = column_sorting_hierarchy.Select(col => new SortingCriteria(col.id, col.SortDirection.GetValueOrDefault() == ListSortDirection.Ascending)).ToArray();
desc = string.Join(", ", sorting_criterion);
}
public bool Equals(OrderBookSortingData other) => other != null && EqualityComparer.safe_sequence_equals(sorting_criterion, other.sorting_criterion);
public override bool Equals(object obj) => base.Equals(obj);
public override int GetHashCode() => EqualityComparer.aggregate_hash(sorting_criterion);
public override string ToString() => desc;
}
partial class OrderBookVM {
public class OrderBookColumnCollection : ObservableCollection<ColumnBase>, IValueFactory<FilterStates>, IDisposable {
public class OrderBookColumnCollection : ObservableCollection<ColumnBase>, IValueFactory<(FilterStates filters, IReadOnlyList<SortingCriteria> sorting)>, IDisposable {
readonly List<ICVOrderBookColumn> _active_comparers = new List<ICVOrderBookColumn>();
public OrderBookColumnCollection(IBus bus, OrdersModel model, IObservable<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)> order_securities) {
// security
Add(security_column = new SecurityColumn(
DiscreteValuesFilterVM.Create<OrderBookRowVM, SecurityImmutable, SecurityCriteria>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, SecurityImmutable, SecurityCriteria>(
order_securities,
r => r.security.value.GetValueOrDefault(SecurityDataHelper.Defaults.SecurityImmutable),
ort => ort.security_md.HasValue ? ort.security_md.Value.security : SecurityDataHelper.Defaults.SecurityImmutable,
SecurityCriteria.from_securities,
SecurityCriteria.get_to_securities(bus),
nameof(OrderBookRowVM.security),
@@ -60,172 +79,231 @@ namespace mbts.spark {
Columns.Symbol,
DefaultColumnHeaders.Symbol,
nameof(OrderBookRowVM.symbol),
r => r.symbol.store,
r => r.symbol,
DefaultColumnWidths.Symbol,
DiscreteValuesFilterVM.Create<OrderBookRowVM, string>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, string>(
mbts.co2.UserSymbol.get_all().Select(u => u.symbol).Distinct().ToList(),
r => r.symbol.value,
value_path(nameof(OrderBookRowVM.symbol))
ort => ort.security_md.HasValue ? ort.security_md.Value.security.Symbol : string.Empty,
nameof(OrderBookRowVM.symbol)
),
DataGridFilterKnownTemplates.DiscreteValues));
DataGridFilterKnownTemplates.DiscreteValues,
_wrap_comparer(r => r.symbol)));
// time_created
Add(time_created_column = new TimeCreatedColumn(
new TimeFilterVM<OrderBookRowVM>(r => r.time_created.value, value_path(nameof(OrderBookRowVM.time_created)))));
Add(time_created_column = new SimpleTextColumn<TimeFilterTuple>(
Columns.Time,
"Time",
nameof(OrderBookRowVM.time_created),
r => r.time_created_text,
DefaultColumnWidths.Time,
new TimeFilterVM<OrderRecordTuple>(
ort => ort.order.created(),
nameof(OrderBookRowVM.time_created)),
DataGridFilterKnownTemplates.TimeFilter,
_wrap_comparer(r => r.time_created)));
// last_change
Add(last_change_column = new LastChangeTimeColumn(
new TimeFilterVM<OrderBookRowVM>(r => r.last_change.value, value_path(nameof(OrderBookRowVM.last_change)))));
Add(last_change_column = new SimpleTextColumn<TimeFilterTuple>(
Columns.LastChange,
"Last Change",
nameof(OrderBookRowVM.last_change),
r => r.last_change_text,
DefaultColumnWidths.Time,
new TimeFilterVM<OrderRecordTuple>(
ort => ort.order.last_change(),
nameof(OrderBookRowVM.last_change)),
DataGridFilterKnownTemplates.TimeFilter,
_wrap_comparer(r => r.last_change)));
// user
Add(user_column = new SimpleTextColumn<string[]>(
Columns.User,
DefaultColumnHeaders.User,
nameof(OrderBookRowVM.user),
r => r.user.store,
r => r.user,
DefaultColumnWidths.User,
DiscreteValuesFilterVM.Create<OrderBookRowVM, string>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, string>(
model.factory.subscribe_users().Select(d => d.Values),
r => r.user.value,
value_path(nameof(OrderBookRowVM.user))),
DataGridFilterKnownTemplates.DiscreteValues));
ort => ort.order.user(),
nameof(OrderBookRowVM.user)),
DataGridFilterKnownTemplates.DiscreteValues,
_wrap_comparer(r => r.user)));
// account
Add(account_column = new SimpleTextColumn<string[]>(
Columns.Account,
DefaultColumnHeaders.Account,
nameof(OrderBookRowVM.account),
r => r.account.store,
r => r.account,
DefaultColumnWidths.Account,
DiscreteValuesFilterVM.Create<OrderBookRowVM, string, string[]>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, string, string[]>(
model.factory.subscribe_accounts().Select(d => d.Values),
r => r.account.value,
ort => ort.order.account(),
vals => vals.ToArray(),
vals => Observable.Return(vals),
value_path(nameof(OrderBookRowVM.account))),
DataGridFilterKnownTemplates.DiscreteValues));
nameof(OrderBookRowVM.account)),
DataGridFilterKnownTemplates.DiscreteValues,
_wrap_comparer(r => r.account)));
// exchange
Add(exchange_column = new SimpleTextColumn<Venue[]>(
Columns.Exchange,
DefaultColumnHeaders.Exchange,
nameof(OrderBookRowVM.exchange),
r => r.exchange.store,
r => r.exchange.ToString(),
DefaultColumnWidths.Exchange,
DiscreteValuesFilterVM.Create<OrderBookRowVM, Venue>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, Venue>(
EnumHelper.get_values_except(Venue.Unset).ToList(),
r => r.exchange.value,
value_path(nameof(OrderBookRowVM.exchange))),
DataGridFilterKnownTemplates.EnumValues));
ort => ort.security_md.HasValue ? ort.security_md.Value.security.Exchange : Venue.Unset,
nameof(OrderBookRowVM.exchange)),
DataGridFilterKnownTemplates.EnumValues,
_wrap_comparer(r => r.exchange)));
// time in force
Add(time_in_force_column = new SimpleTextColumn<TimeInForce[]>(
Columns.TimeInForce,
DefaultColumnHeaders.TimeInForce,
nameof(OrderBookRowVM.time_in_force),
r => r.time_in_force.store,
r => r.time_in_force.ToString(),
DefaultColumnWidths.TimeInForce,
DiscreteValuesFilterVM.Create<OrderBookRowVM, TimeInForce>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, TimeInForce>(
EnumHelper.get_values<TimeInForce>().ToList(),
r => r.time_in_force.value,
value_path(nameof(OrderBookRowVM.time_in_force))),
DataGridFilterKnownTemplates.EnumValues));
ort => ort.order.time_in_force(),
nameof(OrderBookRowVM.time_in_force)),
DataGridFilterKnownTemplates.EnumValues,
_wrap_comparer(r => r.time_in_force)));
// order type
Add(order_type_column = new OrderBookOrderTypeColumn(
DiscreteValuesFilterVM.Create<OrderBookRowVM, OrderType>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, OrderType>(
utilities.OrderFactory.all_order_types,
r => r.order_type.value,
value_path(nameof(OrderBookRowVM.order_type)))));
ort => ort.order.order_type(),
nameof(OrderBookRowVM.order_type))));
// order sub type
Add(sub_type_column = new SimpleTextColumn<OrderSubType[]>(
Columns.SubType,
"Sub Type",
nameof(OrderBookRowVM.order_sub_type),
r => r.order_sub_type.store,
r => r.order_sub_type_text,
80,
DiscreteValuesFilterVM.Create<OrderBookRowVM, OrderSubType>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, OrderSubType>(
EnumHelper.get_values<OrderSubType>().ToList(),
r => r.order_sub_type.value,
value_path(nameof(OrderBookRowVM.order_sub_type))),
SubTypeFilterTemplateKey));
ort => ort.order.sub_type(),
nameof(OrderBookRowVM.order_sub_type)),
SubTypeFilterTemplateKey,
_wrap_comparer(r => r.order_sub_type)));
// side
Add(side_column = new SideColumn(
DiscreteValuesFilterVM.Create<OrderBookRowVM, Side>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, Side>(
new[] { Side.Buy, Side.Sell },
r => r.side.value,
value_path(nameof(OrderBookRowVM.side)))));
ort=> ort.order.side(),
nameof(OrderBookRowVM.side))));
// quantity
Add(qty_column = new SizeColumn(
new UIntMinMax<OrderBookRowVM>(r => r.quantity.value, value_path(nameof(OrderBookRowVM.quantity)))));
// price
Add(price_column = new PriceColumn(
new DoubleMinMax<OrderBookRowVM>(r => r.price.value, value_path(nameof(OrderBookRowVM.price)))));
new UIntMinMax<OrderRecordTuple>(ort => ort.order.quantity(), nameof(OrderBookRowVM.quantity))));
// edit quantity column
// not usually part of the main order book, but it's useful to add it
// rather than debugging the popup window
//Add(edit_qty_column = new EditableQtyColumn());
// filled quantity
Add(filled_qty_column = new FilledColumn(
new UIntMinMax<OrderBookRowVM>(r => r.filled_quantity.value, value_path(nameof(OrderBookRowVM.filled_quantity)))));
new UIntMinMax<OrderRecordTuple>(ort => ort.order.filled(), nameof(OrderBookRowVM.filled_quantity))));
// price
Add(price_column = new SimpleTextColumn<DoubleMinMaxState>(
Columns.Price,
"Price",
nameof(OrderBookRowVM.price),
r => r.price_text,
DefaultColumnWidths.Price,
new DoubleMinMax<OrderRecordTuple>(
ort => ort.order.price(),
nameof(OrderBookRowVM.price)),
DataGridFilterKnownTemplates.DoubleMinMax,
_wrap_comparer(r => r.price)));
// edit price column
// not usually part of the main order book, but it's useful to add it
// rather than debugging the popup window
//Add(edit_price_column = new OrderBookVM.EditablePriceColumn());
// avg filled price
Add(avg_price_column = new AvgFilledColumn(
new DoubleMinMax<OrderBookRowVM>(r => r.avg_price.value, value_path(nameof(OrderBookRowVM.avg_price)))));
Add(avg_price_column = new SimpleTextColumn<DoubleMinMaxState>(
Columns.AvgFilled,
"Avg Price",
nameof(OrderBookRowVM.avg_price),
r => r.avg_price_text,
DefaultColumnWidths.Price,
new DoubleMinMax<OrderRecordTuple>(
ort => ort.order.avg_price(),
nameof(OrderBookRowVM.avg_price)),
DataGridFilterKnownTemplates.DoubleMinMax,
_wrap_comparer(r => r.avg_price)));
// last filled price
Add(last_price_column = new LastFilledColumn(
new DoubleMinMax<OrderBookRowVM>(r => r.last_price.value, value_path(nameof(OrderBookRowVM.last_price)))));
Add(last_price_column = new SimpleTextColumn<DoubleMinMaxState>(
Columns.LastPrice,
"Last Filled",
nameof(OrderBookRowVM.last_price),
r => r.last_price_text,
DefaultColumnWidths.Price,
new DoubleMinMax<OrderRecordTuple>(
ort => ort.order.last_price(),
nameof(OrderBookRowVM.last_price)),
DataGridFilterKnownTemplates.DoubleMinMax,
_wrap_comparer(r => r.last_price)));
// status
Add(status_column = new SimpleTextColumn<OrderStatus[]>(
Columns.Status,
"Status",
nameof(OrderBookRowVM.status),
r => r.status.store,
r => r.status.ToString(),
80,
DiscreteValuesFilterVM.Create<OrderBookRowVM, OrderStatus>(
DiscreteValuesFilterVM.Create<OrderRecordTuple, OrderStatus>(
EnumHelper.get_values<OrderStatus>().ToList(),
r => r.status.value,
value_path(nameof(OrderBookRowVM.status))),
DataGridFilterKnownTemplates.EnumValues));
ort => ort.order.status(),
nameof(OrderBookRowVM.status)),
DataGridFilterKnownTemplates.EnumValues,
_wrap_comparer(r => r.status)));
// last message
Add(last_message_column = new OrderBookLastMessageColumn(
new RegexFilterVM<OrderBookRowVM>(
r => r.last_msg.value,
value_path(nameof(OrderBookRowVM.last_msg)))));
new RegexFilterVM<OrderRecordTuple>(
ort => ort.order.last_msg(),
nameof(OrderBookRowVM.last_msg))));
// security key
Add(security_key_column = new SimpleTextColumn<string>(
Columns.SecurityKey,
DefaultColumnHeaders.SecurityKey,
nameof(OrderBookRowVM.security_key),
r => r.security_key.store,
r => r.security_key.ToString(),
DefaultColumnWidths.SecurityKey,
new RegexFilterVM<OrderBookRowVM>(
r => r.security_key.text,
value_path(nameof(OrderBookRowVM.security_key))),
DataGridFilterKnownTemplates.Regex));
new RegexFilterVM<OrderRecordTuple>(
ort => ort.order.security_key().ToString(),
nameof(OrderBookRowVM.security_key)),
DataGridFilterKnownTemplates.Regex,
_wrap_comparer(r => r.security_key)));
// order key
Add(order_key_column = new SimpleTextColumn<string>(
Columns.OrderKey,
DefaultColumnHeaders.OrderKey,
nameof(OrderBookRowVM.order_key),
r => r.order_key.store,
r => r.order_key.ToString(),
DefaultColumnWidths.OrderKey,
new RegexFilterVM<OrderBookRowVM>(
r => r.order_key.text,
value_path(nameof(OrderBookRowVM.order_key))),
DataGridFilterKnownTemplates.Regex));
new RegexFilterVM<OrderRecordTuple>(
ort => ort.order.Key.ToString(),
nameof(OrderBookRowVM.order_key)),
DataGridFilterKnownTemplates.Regex,
_wrap_comparer(r => r.order_key)));
// parent key (this is for diagnostic purposes, remove this column once OrderBook seems stable again)
Add(parent_key_column = new SimpleTextColumn<string>(
Columns.ParentKey,
"Parent Key",
nameof(OrderBookRowVM.parent_order_key),
r => r.parent_order_key.store,
80,
new RegexFilterVM<OrderBookRowVM>(
r => r.parent_order_key.text,
value_path(nameof(OrderBookRowVM.parent_order_key))),
DataGridFilterKnownTemplates.Regex));
string value_path(string cell_name) =>
string.Join(".", cell_name, nameof(OrderBookRowVM.IOrderBookCell<uint>.value)); //uint is an arbitrary 'T', just want to get compile check on 'value' path.
r => r.parent_order_key.ToString(),
DefaultColumnWidths.ParentKey,
new RegexFilterVM<OrderRecordTuple>(
ort => ort.order.ParentKey.ToString(),
nameof(OrderBookRowVM.parent_order_key)),
DataGridFilterKnownTemplates.Regex,
_wrap_comparer(r => r.parent_order_key)));
}
List<IDataGridColumnFilter<OrderBookRowVM>> _active_filters = new List<IDataGridColumnFilter<OrderBookRowVM>>(0);
public event Action filter_changed;
public CancelColumn cancel_column { get; }
public SecurityColumn security_column { get; }
public SimpleTextColumn<string[]> symbol_column { get; }
public TimeCreatedColumn time_created_column { get; }
public LastChangeTimeColumn last_change_column { get; }
public SimpleTextColumn<TimeFilterTuple> time_created_column { get; }
public SimpleTextColumn<TimeFilterTuple> last_change_column { get; }
public SimpleTextColumn<string[]> user_column { get; }
public SimpleTextColumn<string[]> account_column { get; }
public SimpleTextColumn<Venue[]> exchange_column { get; }
@@ -234,19 +312,19 @@ namespace mbts.spark {
public SimpleTextColumn<OrderSubType[]> sub_type_column { get; }
public SideColumn side_column { get; }
public SizeColumn qty_column { get; }
public PriceColumn price_column { get; }
public EditableQtyColumn edit_qty_column { get; }
public SimpleTextColumn<DoubleMinMaxState> price_column { get; }
public EditablePriceColumn edit_price_column { get; }
public FilledColumn filled_qty_column { get; }
public LastFilledColumn last_price_column { get; }
public AvgFilledColumn avg_price_column { get; }
public SimpleTextColumn<DoubleMinMaxState> last_price_column { get; }
public SimpleTextColumn<DoubleMinMaxState> avg_price_column { get; }
public SimpleTextColumn<OrderStatus[]> status_column { get; }
public OrderBookLastMessageColumn last_message_column { get; }
public SimpleTextColumn<string> security_key_column { get; }
public SimpleTextColumn<string> order_key_column { get; }
public SimpleTextColumn<string> parent_key_column { get; }
public bool filter(OrderBookRowVM row) => _active_filters.All(f => f.filter(row));
public FilterStates create() =>
public (FilterStates filters, IReadOnlyList<SortingCriteria> sorting) create() => (
new FilterStates {
order_key_regex = order_key_column.filter.get_state(),
time_created = time_created_column.filter.get_state(),
@@ -267,13 +345,11 @@ namespace mbts.spark {
last_change = last_change_column.filter.get_state(),
security_criteria = security_column.filter.get_state(),
symbols = symbol_column.filter.get_state()
};
},
get_sorting_data().sorting_criterion);
public void load_from(FilterStates fs) {
if (fs != null) {
foreach (var c in this.Cast<ICVOrderBookColumn>())
c.filter.filter_changed -= _set_active_filters;
//
public void load_from((FilterStates filters, IReadOnlyList<SortingCriteria> sorting) state) {
if (state.filters is FilterStates fs) {
security_column.filter.load_state(fs.security_criteria);
symbol_column.filter.load_state(fs.symbols);
time_created_column.filter.load_state(fs.time_created);
@@ -293,155 +369,197 @@ namespace mbts.spark {
status_column.filter.load_state(fs.statuses);
last_message_column.filter.load_state(fs.exchange_message_regex);
last_change_column.filter.load_state(fs.last_change);
//
_set_active_filters();
foreach (var c in this.Cast<ICVOrderBookColumn>())
c.filter.filter_changed += _set_active_filters;
}
_active_comparers.Clear();
foreach (var col in this)
col.SortDirection = null;
if (state.sorting is IReadOnlyList<SortingCriteria> sorting) {
foreach (var sc in sorting) {
this.OfType<ICVOrderBookColumn>().ApplyToFirst(col => col.id.iequals(sc.col_name),
col => {
col.SortDirection = sc.is_ascending ? ListSortDirection.Ascending : ListSortDirection.Descending;
_active_comparers.Add(col);
});
}
}
if (_active_comparers.Count == 0) {
// safeguard against empty sort, fallback to time-created
time_created_column.SortDirection = ListSortDirection.Descending;
_active_comparers.Add(time_created_column);
}
}
public Func<OrderRecordTuple, bool> get_filter() {
var active_filters = this.OfType<ICVOrderBookColumn>().Where(c => c.filter.active.GetValueOrDefault()).Select(c => c.filter).ToArray();
return active_filters.Length == 0 ? null : new Func<OrderRecordTuple, bool>(r => active_filters.All(f => f.filter(r)));
}
public OrderBookSortingData get_sorting_data() {
var desc_parts = new List<string>();
var comp_builder = new ComparerBuilder<OrderBookRowVM>();
// this should be unneccesary, safe_active_comparers is just an extra guard against unexpected results from future code changes
// (get_comparer_data() should NEVER return a null/non-deterministic IComparer, fallback to sorting by Time Created in descending order if necessary)
var safe_active_comparers = _active_comparers.Where(c => c.SortDirection.HasValue).ToList();
if (safe_active_comparers.Count == 0) {
time_created_column.SortDirection = ListSortDirection.Descending;
_active_comparers.Clear();
_active_comparers.Add(time_created_column);
safe_active_comparers = _active_comparers;
}
return new OrderBookSortingData(safe_active_comparers);
}
public void on_sorting(ICVOrderBookColumn col, bool clear_current_sort) {
col.SortDirection = col.SortDirection.HasValue && col.SortDirection.Value == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
if (_active_comparers.Contains(col, EqualityComparer.Create<ICVOrderBookColumn, Columns>(r => r.key))) {
if (clear_current_sort) _reset_active_comparer(col);
} else {
if (clear_current_sort) _reset_active_comparer(col);
else _active_comparers.Add(col);
}
}
public void Dispose() {
foreach (var c in this.Cast<ICVOrderBookColumn>()) {
c.filter.filter_changed -= _set_active_filters;
(c.filter as IDisposable)?.Dispose();
foreach (var col in this.OfType<ICVOrderBookColumn>())
(col.filter as IDisposable)?.Dispose();
Clear();
}
/// <see cref="OrderBookColumnCollection"/> ONLY supports sorting/filtering behavior for columns that implement <see cref="ICVOrderBookColumn"/>.
/// This is because OrderBook, unlike the other windows (as of 10/30/2018), does not use <see cref="ICollectionView"/> as the sorting/filtering model.
protected override void InsertItem(int index, ColumnBase item) {
if (item is ICVOrderBookColumn col) {
try {
_hook(col);
base.InsertItem(index, item);
} catch {
_unhook(col);
throw;
}
} else {
Log.Ui.warn($"{nameof(OrderBookColumnCollection)} added a column that is not of type {nameof(ICVOrderBookColumn)}");
base.InsertItem(index, item);
}
}
void _set_active_filters() {
_active_filters = this
.Cast<ICVOrderBookColumn>()
.Select(c => c.filter)
.Where(f => f.active.GetValueOrDefault())
.ToList();
filter_changed?.Invoke();
protected override void RemoveItem(int index) {
if (Count > index && this[index] is ICVOrderBookColumn col)
_unhook(col);
base.RemoveItem(index);
}
protected override void ClearItems() {
var ob_columns = this.OfType<ICVOrderBookColumn>().ToList();
Log.Ui.info($"{nameof(OrderBookColumnCollection)} is unhooking all columns of type {nameof(ICVOrderBookColumn)} ({ob_columns.Count}/{Count}).");
foreach (var col in ob_columns)
_unhook(col);
base.ClearItems();
}
void _hook(ICVOrderBookColumn new_item) => new_item.filter.filter_changed += _on_column_filter_changed;
void _unhook(ICVOrderBookColumn old_item) => old_item.filter.filter_changed -= _on_column_filter_changed;
void _on_column_filter_changed() => filter_changed?.Invoke();
void _reset_active_comparer(ICVOrderBookColumn column) {
foreach (var col in _active_comparers) {
if (!col.Equals(column)) col.SortDirection = null;
}
_active_comparers.Clear();
_active_comparers.Add(column);
}
}
static IComparer<OrderBookRowVM> _wrap_comparer<T>(Func<OrderBookRowVM, T> get) =>
Comparer<T>.Default.wrap(get);
static IComparer<OrderBookRowVM> _wrap_comparer<T>(Func<OrderBookRowVM, T> get, IComparer<T> comparer) =>
comparer.wrap(get);
public abstract class OrderbookColumnBase<T> : ColumnBase<Columns>, IKeyedColumn<Columns>, IFilterableColumn, ICVOrderBookColumn {
ListSortDirection? _cached_sort_direction;
readonly string _filter_template;
protected readonly Func<OrderBookRowVM, IOrderBookCellStore> _get_cell_store;
protected readonly Func<OrderBookRowVM, string> _get_text;
GlyphContext _root_glyph_context, _child_glyph_context;
protected OrderbookColumnBase(Columns col_key, string header, Func<OrderBookRowVM, IOrderBookCellStore> get_cell_store, string sort_prop, double width, IDataGridColumnPersistentFilter<OrderBookRowVM, T> filter, string filter_template) : base(col_key) {
protected OrderbookColumnBase(Columns col_key, string header, Func<OrderBookRowVM, string> get_text, string sort_prop, double width,
IDataGridColumnPersistentFilter<OrderRecordTuple, T> filter, string filter_template, IComparer<OrderBookRowVM> comparer) : base(col_key) {
id = key.ToString();
Width = width;
Header = header;
_get_cell_store = get_cell_store;
_get_text = get_text;
sort_member_path = sort_prop;
this.filter = filter;
_filter_template = filter_template;
_cached_sort_direction = SortDirection;
this.comparer = comparer;
font_params_changed += refresh_glyph_contexts;
refresh_glyph_contexts();
void refresh_glyph_contexts() {
_root_glyph_context = GlyphContext.create(font_family, RenderUtils.root_font_weight);
_child_glyph_context = GlyphContext.create(font_family, RenderUtils.child_font_weight);
}
}
public IDataGridColumnPersistentFilter<OrderBookRowVM, T> filter { get; }
public IDataGridColumnPersistentFilter<OrderRecordTuple, T> filter { get; }
public IComparer<OrderBookRowVM> comparer { get; }
IDataGridColumnFilter<OrderBookRowVM> ICVOrderBookColumn.filter => filter;
IDataGridColumnFilter<OrderRecordTuple> ICVOrderBookColumn.filter => filter;
IDataGridColumnFilter IFilterableColumn.filter => filter;
string IFilterableColumn.filter_template => _filter_template;
ListSortDirection? ICVOrderBookColumn.SortDirection => _cached_sort_direction;
public IGridCellStore get_cell_store(OrderBookRowVM row) => _get_cell_store(row);
ListSortDirection? ICVOrderBookColumn.SortDirection { get => _cached_sort_direction; set => SortDirection = value; }
protected override void OnSortDirectionChanged(ListSortDirection? new_value) {
_cached_sort_direction = new_value;
base.OnSortDirectionChanged(new_value);
}
protected GlyphContext _get_glyph_context(OrderBookRowVM row) => row.is_child ? _child_glyph_context : _root_glyph_context;
}
public class SimpleTextColumn<TFilterState> : OrderbookColumnBase<TFilterState>, IRenderColumn<OrderBookRowVM> {
public SimpleTextColumn(Columns key, string header, string sort_prop, Func<OrderBookRowVM, IOrderBookCellStore> get_cell_store, double width, IDataGridColumnPersistentFilter<OrderBookRowVM, TFilterState> filter, string filter_template)
: base(key, header, get_cell_store, sort_prop, width, filter, filter_template) { }
public SimpleTextColumn(Columns key, string header, string sort_prop, Func<OrderBookRowVM, string> get_text, double width,
IDataGridColumnPersistentFilter<OrderRecordTuple, TFilterState> filter, string filter_template, IComparer<OrderBookRowVM> comparer)
: base(key, header, get_text, sort_prop, width, filter, filter_template, comparer) { }
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) {
var cell = _get_cell_store(row);
if (!string.IsNullOrEmpty(cell.current.text))
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, cell.current.text, position, RowHeight,
font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, cell.animations);
}
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, _get_glyph_context(row), font_size, _get_text(row), position, row_height,
font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
}
#region Explicit Column Defs
public class SideColumn : OrderbookColumnBase<Side[]>, IRenderColumn<OrderBookRowVM> {
public SideColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, Side[]> filter)
: base(Columns.Side, "B/S", r => r.side.store, nameof(OrderBookRowVM.side), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues) { }
public SideColumn(IDataGridColumnPersistentFilter<OrderRecordTuple, Side[]> filter)
: base(Columns.Side, "B/S", r => r.side.ToString(), nameof(OrderBookRowVM.side), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues, _wrap_comparer(r => r.side)) { }
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.side.text, position, RowHeight,
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.side.store.animations);
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.ToString(), position, row_height,
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
}
public class SizeColumn : OrderbookColumnBase<UIntMinMaxState>, IRenderColumn<OrderBookRowVM> {
public SizeColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, UIntMinMaxState> filter)
: base(Columns.Size, "Qty", r => r.quantity.store, nameof(OrderBookRowVM.quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax) {
public SizeColumn(IDataGridColumnPersistentFilter<OrderRecordTuple, UIntMinMaxState> filter)
: base(Columns.Size, "Qty", r => r.quantity.ToString(), nameof(OrderBookRowVM.quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.quantity)) {
sort_member_path = nameof(OrderBookRowVM.quantity);
}
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.quantity.text, position, RowHeight,
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.quantity.store.animations);
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.ToString(), position, row_height,
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
}
public class FilledColumn : OrderbookColumnBase<UIntMinMaxState>, IRenderColumn<OrderBookRowVM> {
public FilledColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, UIntMinMaxState> filter)
: base(Columns.Filled, "Filled Qty", r => r.filled_quantity.store, nameof(OrderBookRowVM.filled_quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax) {
public FilledColumn(IDataGridColumnPersistentFilter<OrderRecordTuple, UIntMinMaxState> filter)
: base(Columns.Filled, "Filled Qty", r => r.filled_quantity.ToString(), nameof(OrderBookRowVM.filled_quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.filled_quantity)) {
sort_member_path = nameof(OrderBookRowVM.filled_quantity);
}
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.filled_quantity.text, position, RowHeight,
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.filled_quantity.store.animations);
}
public class PriceColumn : OrderbookColumnBase<DoubleMinMaxState>, IRenderColumn<OrderBookRowVM> {
public PriceColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, DoubleMinMaxState> filter)
: base(Columns.Price, "Price", r => r.price.store, nameof(OrderBookRowVM.price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { }
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.price.text,
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.price.store.animations);
}
public class AvgFilledColumn : OrderbookColumnBase<DoubleMinMaxState>, IRenderColumn<OrderBookRowVM> {
public AvgFilledColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, DoubleMinMaxState> filter)
: base(Columns.AvgFilled, "Avg Filled", r => r.avg_price.store, nameof(OrderBookRowVM.avg_price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { }
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) {
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.avg_price.text,
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.avg_price.store.animations);
}
}
public class LastFilledColumn : OrderbookColumnBase<DoubleMinMaxState>, IRenderColumn<OrderBookRowVM> {
public LastFilledColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, DoubleMinMaxState> filter)
: base(Columns.LastPrice, "Last Filled", r => r.last_price.store, nameof(OrderBookRowVM.last_price), DefaultColumnWidths.Price, filter, DataGridFilterKnownTemplates.DoubleMinMax) { }
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.last_price.text,
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.last_price.store.animations);
}
public class TimeCreatedColumn : OrderbookColumnBase<TimeFilterTuple>, IRenderColumn<OrderBookRowVM> {
public TimeCreatedColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, TimeFilterTuple> filter)
: base(Columns.Time, "Time", r => r.time_created.store, nameof(OrderBookRowVM.time_created), DefaultColumnWidths.Time, filter, DataGridFilterKnownTemplates.TimeFilter) { }
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.time_created.text,
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.time_created.store.animations);
}
public class LastChangeTimeColumn : OrderbookColumnBase<TimeFilterTuple>, IRenderColumn<OrderBookRowVM> {
public LastChangeTimeColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, TimeFilterTuple> filter)
: base(Columns.LastChange, "Last Change", r => r.last_change.store, nameof(OrderBookRowVM.last_change), DefaultColumnWidths.Time, filter, DataGridFilterKnownTemplates.TimeFilter) { }
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, RenderUtils.get_glyph_context(row), font_size, row.last_change.text,
position, RowHeight, font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding, row.last_change.store.animations);
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.ToString(), position, row_height,
RenderUtils.get_side_brush(row), null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
}
public class OrderBookOrderTypeColumn : OrderTypeColumn<Columns>, ICVOrderBookColumn {
ListSortDirection? _cached_sort_direction;
public OrderBookOrderTypeColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, OrderType[]> filter)
: base(Columns.OrderType, new Binding($"{nameof(OrderBookRowVM.order_type)}.{nameof(IOrderBookCell<OrderType>.value)}"), new Binding(nameof(OrderBookRowVM.order_metadata)), filter) {
public OrderBookOrderTypeColumn(IDataGridColumnPersistentFilter<OrderRecordTuple, OrderType[]> filter)
: base(Columns.OrderType, new Binding(nameof(OrderBookRowVM.order_type)), new Binding(nameof(OrderBookRowVM.order_metadata)), filter) {
id = key.ToString();
this.filter = filter;
sort_member_path = nameof(OrderBookRowVM.order_type);
@@ -449,12 +567,11 @@ namespace mbts.spark {
_cached_sort_direction = SortDirection;
}
public new IDataGridColumnPersistentFilter<OrderBookRowVM, OrderType[]> filter { get; }
public new IDataGridColumnPersistentFilter<OrderRecordTuple, OrderType[]> filter { get; }
public IComparer<OrderBookRowVM> comparer => _wrap_comparer(r => r.order_type);
IDataGridColumnFilter<OrderBookRowVM> ICVOrderBookColumn.filter => filter;
ListSortDirection? ICVOrderBookColumn.SortDirection => _cached_sort_direction;
public IGridCellStore get_cell_store(OrderBookRowVM row) => row.order_type.store;
IDataGridColumnFilter<OrderRecordTuple> ICVOrderBookColumn.filter => filter;
ListSortDirection? ICVOrderBookColumn.SortDirection { get => _cached_sort_direction; set => SortDirection = value; }
protected override void OnSortDirectionChanged(ListSortDirection? new_value) {
_cached_sort_direction = new_value;
@@ -465,22 +582,21 @@ namespace mbts.spark {
public class OrderBookLastMessageColumn : TextColumn<Columns>, ICVOrderBookColumn {
ListSortDirection? _cached_sort_direction;
public OrderBookLastMessageColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, string> filter)
: base(Columns.LastMessage, new Binding($"{nameof(OrderBookRowVM.last_msg)}.{nameof(OrderBookRowVM.IOrderBookCell<string>.value)}"), filter, DataGridFilterKnownTemplates.Regex) {
public OrderBookLastMessageColumn(IDataGridColumnPersistentFilter<OrderRecordTuple, string> filter)
: base(Columns.LastMessage, new Binding(nameof(OrderBookRowVM.last_msg)), filter, DataGridFilterKnownTemplates.Regex) {
id = key.ToString();
this.filter = filter;
Header = "Last Message";
Width = 80;
Width = DIYGrid.DefaultColumnWidths.LastMessage;
sort_member_path = nameof(OrderBookRowVM.last_msg);
tooltip_binding = new Binding(nameof(OrderBookRowVM.last_msg));
}
public new IDataGridColumnPersistentFilter<OrderBookRowVM, string> filter { get; }
public new IDataGridColumnPersistentFilter<OrderRecordTuple, string> filter { get; }
public IComparer<OrderBookRowVM> comparer => _wrap_comparer(r => r.last_msg);
IDataGridColumnFilter<OrderBookRowVM> ICVOrderBookColumn.filter => filter;
ListSortDirection? ICVOrderBookColumn.SortDirection => _cached_sort_direction;
public IGridCellStore get_cell_store(OrderBookRowVM row) => row.last_msg.store;
IDataGridColumnFilter<OrderRecordTuple> ICVOrderBookColumn.filter => filter;
ListSortDirection? ICVOrderBookColumn.SortDirection { get => _cached_sort_direction; set => SortDirection = value; }
protected override void OnSortDirectionChanged(ListSortDirection? new_value) {
_cached_sort_direction = new_value;
@@ -495,7 +611,7 @@ namespace mbts.spark {
ListSortDirection? _cached_sort_direction;
public SecurityColumn(IDataGridColumnPersistentFilter<OrderBookRowVM, SecurityCriteria> filter) : base(Columns.Security) {
public SecurityColumn(IDataGridColumnPersistentFilter<OrderRecordTuple, SecurityCriteria> filter) : base(Columns.Security) {
id = key.ToString();
this.filter = filter;
Header = DefaultColumnHeaders.Security;
@@ -504,14 +620,13 @@ namespace mbts.spark {
_cached_sort_direction = SortDirection;
}
public IDataGridColumnPersistentFilter<OrderBookRowVM, SecurityCriteria> filter { get; }
public IDataGridColumnPersistentFilter<OrderRecordTuple, SecurityCriteria> filter { get; }
public IComparer<OrderBookRowVM> comparer => _wrap_comparer(r => r.security, SecurityHelper.Comparer.InstanceImmutable.to_nullable());
IDataGridColumnFilter<OrderBookRowVM> ICVOrderBookColumn.filter => filter;
IDataGridColumnFilter<OrderRecordTuple> ICVOrderBookColumn.filter => filter;
IDataGridColumnFilter IFilterableColumn.filter => filter;
string IFilterableColumn.filter_template => DataGridFilterKnownTemplates.DiscreteValues;
ListSortDirection? ICVOrderBookColumn.SortDirection => _cached_sort_direction;
public IGridCellStore get_cell_store(OrderBookRowVM row) => row.security.store;
ListSortDirection? ICVOrderBookColumn.SortDirection { get => _cached_sort_direction; set => SortDirection = value; }
public override FrameworkElement view_factory() {
// expander
@@ -521,8 +636,10 @@ namespace mbts.spark {
toggle_button.SetBinding(ToggleButton.VisibilityProperty, new Binding(nameof(OrderBookRowVM.is_parent)) { Converter = _expander_visiblilty_converter });
toggle_button.SetResourceReference(ToggleButton.StyleProperty, ExpandCollapseToggleStyleKey);
//
var security_view = new TextBlock { FontFamily = font_family, FontSize = font_size, Foreground = font_brush };
security_view.SetBinding(TextBlock.TextProperty, $"{nameof(OrderBookRowVM.security)}.{nameof(IGridCell.text)}");
var security_view = new TextBlock { Foreground = font_brush };
security_view.SetBinding(TextBlock.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
security_view.SetBinding(TextBlock.FontSizeProperty, new Binding(nameof(font_size)) { Source = this });
security_view.SetBinding(TextBlock.TextProperty, nameof(OrderBookRowVM.security_text));
security_view.SetBinding(TextBlock.HorizontalAlignmentProperty, new Binding(nameof(horizontal_alignment)) { Source = this });
security_view.SetBinding(TextBlock.ForegroundProperty, new Binding(nameof(font_brush)) { Source = this });
security_view.SetBinding(TextBlock.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
@@ -534,9 +651,11 @@ namespace mbts.spark {
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
grid.Children.Add(toggle_button);
grid.Children.Add(security_view);
grid.HorizontalAlignment = HorizontalAlignment.Center;
grid.VerticalAlignment = VerticalAlignment.Center;
//
var border = new Border { Child = grid };
CellRender.set_background_animation_bindings(border, $"{nameof(OrderBookRowVM.security)}.{nameof(OrderBookRowVM.security.store)}");
CellRender.set_background_animation_bindings(border, nameof(OrderBookRowVM.security));
return border;
}
@@ -545,10 +664,95 @@ namespace mbts.spark {
base.OnSortDirectionChanged(new_value);
}
}
public class CancelColumn : FrameworkElementColumnBase<Columns> {
readonly BindingBase _command_binding;
public CancelColumn(string command_property) : base(Columns.Cancel) {
id = key.ToString();
Header = DefaultColumnHeaders.Cancel;
_command_binding = new Binding(command_property);
}
public override FrameworkElement view_factory() {
var grid = new Grid() {
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
};
var l1 = new System.Windows.Shapes.Line() {
StrokeThickness = 1,
Stroke = Brushes.White,
X1 = 0.5,
Y1 = 0.5,
X2 = 7,
Y2 = 7
};
var l2 = new System.Windows.Shapes.Line() {
StrokeThickness = 1,
Stroke = Brushes.White,
X1 = 0.5,
Y1 = 7,
X2 = 7,
Y2 = 0.5
};
grid.Children.Add(l1);
grid.Children.Add(l2);
var button = new Button() {
Content = grid,
Background = Brushes.Red,
Foreground = Brushes.White,
Margin = new Thickness(2),
VerticalAlignment = VerticalAlignment.Center,
Height = 14,
Width = 14
};
button.SetBinding(Button.CommandProperty, _command_binding);
return button;
}
}
public class EditableQtyColumn:FrameworkElementColumnBase<Columns> {
public EditableQtyColumn() : base(Columns.EditableQty) {
id = key.ToString();
Header = "Qty";
Width = 64;
}
public override FrameworkElement view_factory() {
var tick_up_down = new TickUpDownNullableUIntCommit();
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.SourceValueProperty, nameof(OrderBookRowVM.prospective_quantity));
tick_up_down.SetBinding(TickUpDownNullableUIntCommit.OnCommitProperty, nameof(OrderBookRowVM.modify_order_quantity));
return tick_up_down;
}
}
public class EditablePriceColumn : FrameworkElementColumnBase<Columns> {
public EditablePriceColumn() : base(Columns.EditablePrice) {
id = key.ToString();
Header = "Price";
Width = 96;
}
public override FrameworkElement view_factory() {
var price_up_down = new PriceEntryCommitControl();
price_up_down.SetBinding(PriceEntryCommitControl.SourceValueProperty, nameof(OrderBookRowVM.prospective_price));
price_up_down.SetBinding(PriceEntryCommitControl.SourceContractSpecProperty, nameof(OrderBookRowVM.contract_spec));
price_up_down.SetBinding(PriceEntryCommitControl.OnCommitProperty, nameof(OrderBookRowVM.modify_order_price));
return price_up_down;
}
}
#endregion
static class RenderUtils {
public static readonly GlyphContext _child_glyph_context = TextUtils.StandardGlyphContext.with(FontWeights.Light);
public static FontWeight
root_font_weight = FontWeights.Normal,
child_font_weight = FontWeights.Light;
public static double font_size = FontSizes.Small;
public static readonly IValueConverter
@@ -567,8 +771,7 @@ namespace mbts.spark {
return mb;
}
public static GlyphContext get_glyph_context(OrderBookRowVM r) => r.is_child ? _child_glyph_context : TextUtils.StandardGlyphContext;
public static Brush get_side_brush(OrderBookRowVM r) => r.side.value == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask;
public static Brush get_side_brush(OrderBookRowVM r) => r.side == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask;
public static BindingBase get_font_weight_binding() => new Binding(nameof(OrderBookRowVM.is_child)) { Converter = _font_weight_converter };
}
}
@@ -0,0 +1,213 @@
using bts;
using bts.api.core;
using bts.collections;
using bts.utils;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
namespace mbts.spark {
class OrderBookRowCollection : ObservableCollection<OrderBookRowVM> {
static readonly IEqualityComparer<INode<OrderBookRowVM>> _node_equality_comparer = EqualityComparer.Create<INode<OrderBookRowVM>, uint>(n => n.value.order_key);
readonly Func<OrderRecordTuple, OrderBookRowVM> _row_factory;
/// Critical: Any and all row mutations <see cref="OrderBookRowVM"/> must be executed through <see cref="SortedTree{T, TKey}.TryReplace(TKey, Func{T, T})"/>.
/// Mutations can change the sort-position of a row, and <see cref="_tree"/> will be broken if its elements aren't properly sorted (it relies heavily on binary-search logic).
SortedTree<OrderBookRowVM, uint> _tree;
IComparer<OrderBookRowVM> _comparer;
Func<OrderRecordTuple, bool> _filter;
bool _is_refresh_pending;
int _deferment_count = 0;
/// When processing changed-items, the <see cref="SortedTree{T, TKey}.TryReplace(TKey, Func{T, T})"/> method is used to update the changed row. It is not expected to fail (return false),
/// but if it does, we don't want to overwhelm the logs with a bunch of useless noise in case it starts happening repeatedly, hence the int fields below.
/// <see cref="_max_replace_misses_logged"/>, <see cref="_n_replace_misses_logged"/>, <see cref="update(IIncrementalTableUpdateTransform{OrderRecordTuple, uint})"/>.
const int _max_replace_misses_logged = 10;
int _n_replace_misses_logged;
public OrderBookRowCollection(Func<OrderRecordTuple, OrderBookRowVM> row_factory, IComparer<OrderBookRowVM> comparer) {
_row_factory = row_factory;
_tree = _mk_tree(_comparer = comparer);
}
public Func<OrderRecordTuple, bool> filter {
get => _filter;
set {
_filter = value;
_refresh_branches(_tree.RootNodes);
reset();
}
}
public IComparer<OrderBookRowVM> comparer {
get => _comparer;
set {
_tree = _tree.Resort(_comparer = value);
reset();
}
}
public int total_row_count { get; private set; }
IEnumerable<OrderBookRowVM> _visible_rows => _tree.Where(r => r.is_visible);
bool _is_refresh_deferred => _deferment_count > 0;
public IEnumerable<INode<OrderBookRowVM>> get_inner_nodes(OrderBookRowVM row) =>
_tree.EnumerateInnerNodes(row);
public IEnumerable<OrderBookRowVM> get_descendent_rows(OrderBookRowVM row) =>
_tree.EnumerateInnerValues(row);
public void collapse_all() {
foreach (var n in _tree.RootNodes) {
collapse_node(n);
}
if (_is_refresh_deferred) {
_is_refresh_pending = true;
} else {
Clear();
foreach (var n in _tree.RootNodes) {
if (n.value.is_visible)
Add(n.value);
}
}
// local methods
void collapse_node(INode<OrderBookRowVM> node) {
node.value.collapse_internal();
foreach (var cn in node.inner) {
cn.value.is_visible = false;
collapse_node(cn);
}
}
}
public void expand_all() {
foreach (var n in _tree.RootNodes) {
expand_node(n);
}
if (_is_refresh_deferred) {
_is_refresh_pending = true;
} else {
Clear();
foreach (var n in _tree.RootNodes) {
if (n.value.is_visible) {
Add(n.value);
foreach (var r in n.inner_values())
Add(r);
}
}
}
// local methods
void expand_node(INode<OrderBookRowVM> node) {
node.value.expand_internal();
foreach (var cn in node.inner) {
cn.value.is_visible = node.value.is_visible;
expand_node(cn);
}
}
}
/// <summary>
/// Refreshes the collection by calling <see cref="GlobalExtensions.sync_with{T}(IList{T}, IEnumerable{T})"/> with all visible rows.
/// </summary>
public void refresh() {
if (_is_refresh_deferred) {
_is_refresh_pending = true;
} else {
_is_refresh_pending = false;
this.sync_with(_visible_rows);
}
}
/// <summary>
/// Resets the collection by calling <see cref="Collection{T}.Clear"/>, then adding all visible rows.
/// </summary>
public void reset(bool force = false) {
if (_is_refresh_deferred && !force) {
_is_refresh_pending = true;
} else {
Clear();
this.AddRange(_visible_rows);
_is_refresh_pending = false;
}
}
public IDisposable defer_refresh(bool force_refresh_after = false) {
// use a counter instead of a simple bool flag to gracefully handle recursive calls to defer_refresh(),
// by only calling refresh() after all IDisposables returned by defer_refresh() have been disposed.
_deferment_count++;
_is_refresh_pending |= force_refresh_after;
return Disposable.Create(() => {
_deferment_count--;
if (_is_refresh_pending)
refresh();
});
}
public void update(IIncrementalTableUpdateTransform<OrderRecordTuple, uint> table) {
IEnumerable<INode<OrderBookRowVM>> impacted_branch_roots;
int n_removed = table.removed.Count();
if (n_removed == total_row_count) {
// full refresh (most likely this is the first update or a server restart)
_tree = _mk_tree(_comparer, table.added.Select(_row_factory));
impacted_branch_roots = _tree.RootNodes;
} else {
foreach (OrderRecordTuple ort in table.removed)
_tree.Remove(ort.order.Key);
foreach (Change<OrderRecordTuple> change in table.changes) {
if (!_tree.TryReplace(change.New.order.Key, r => { r.update(change.New); return r; })) {
// uh oh, replace failed! Let's log this, but not a million times (this seems like the kind of error that, if it happens once, could happen a lot).
if (_n_replace_misses_logged++ < _max_replace_misses_logged)
Log.Ui.error($"{nameof(OrderBookRowCollection)} failed to process Changed {nameof(OrderRecordTuple)} in update (old:{change.Old.key}, new:{change.New.key}). This is the '{_n_replace_misses_logged}' time this has happened in this OrderBook.");
}
}
foreach (OrderRecordTuple ort in table.added)
_tree.Insert(_row_factory(ort));
impacted_branch_roots =
table.added.Concat(table.changed).Select(ort => ort.order.Key).Select(_tree.GetRootNode)
.Concat(
table.removed
.Where(ort => ort.order.has_parent())
.Select(ort => ort.order.ParentKey)
.Distinct() // since these are ParentKeys, there could be duplicates.
.Select(_tree.GetRootNode)
.Where(n => n != null)
).Distinct(_node_equality_comparer);
}
_refresh_branches(impacted_branch_roots);
refresh();
int old_row_count = total_row_count;
total_row_count += table.added.Count() - n_removed;
if (old_row_count != total_row_count)
OnPropertyChanged(new PropertyChangedEventArgs(nameof(total_row_count)));
}
static SortedTree<OrderBookRowVM, uint> _mk_tree(IComparer<OrderBookRowVM> comp, IEnumerable<OrderBookRowVM> init = null) =>
new SortedTree<OrderBookRowVM, uint>(comp, r => r.order_key, r => r.parent_order_key, init);
void _refresh_branches(IEnumerable<INode<OrderBookRowVM>> roots) {
if (filter == null) {
foreach (var n in roots)
refresh_tree_properties(0, true, n);
} else {
foreach (var n in roots)
refresh_tree_properties(0, get_branch_passes_filter(n), n);
}
// local methods
void refresh_tree_properties(int depth_level, bool is_visible, INode<OrderBookRowVM> node) {
node.value.depth_level = depth_level;
node.value.is_visible = is_visible;
using (var en = node.inner.GetEnumerator()) {
if (en.MoveNext()) {
node.value.is_parent = true;
depth_level++;
do refresh_tree_properties(depth_level, node.value.is_visible && node.value.is_expanded, en.Current);
while (en.MoveNext());
}
}
}
bool get_branch_passes_filter(INode<OrderBookRowVM> n) =>
filter(n.value.order_tuple) || n.inner.Any(get_branch_passes_filter);
}
}
}
@@ -6,407 +6,221 @@ using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using static mbts.spark.OrderBookView;
using OrderStatus = bts.api.orderman.OrderStatus;
namespace mbts.spark {
partial class OrderBookRowVM : DisposableViewModelBase, IEquatable<OrderBookRowVM>, DIYGrid.IRowVM<OrderRecordTuple>, DIYGrid.IActionableRowVM {
//public readonly PredicateAnimator<OrderBookRowVM, OrderRecordTuple> animator;
partial class OrderBookRowVM : DisposableViewModelBase, IEquatable<OrderBookRowVM>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
const string _unknown_security_price_format_string = "0.#########";
const int _unknown_security_price_num_fractional_digits = 9;
readonly OrderBookVM _book_vm;
readonly OrdersModel _model;
readonly Property<bool> _visible, _expanded, _passes_filter;
readonly DelegateProperty<int> _depth_level;
readonly HashSet<OrderBookRowVM> _child_rows = new HashSet<OrderBookRowVM>();
readonly ObservantCommand _show_order_modify_cmd;
OrderRecordImmutable _order;
SecurityMD? _security_md;
string _symbol;
OrderBookRowVM _parent;
OrderBookVM.BranchFilterSource _branch_filter_source;
// Core
IOrderBookCellStore<uint> _order_key_store_backing;
IOrderBookCellStore<uint> _parent_order_key_store_backing;
IOrderBookCellStore<uint> _security_key_store_backing;
IOrderBookCellStore<SecurityImmutable?> _security_store_backing;
IOrderBookCellStore<Venue> _exchange_store_backing;
IOrderBookCellStore<string> _symbol_store_backing;
// Def
IOrderBookCellStore<CustomOrderDefinition.CustomOneofCase> _order_type_store_backing;
IOrderBookCellStore<Side> _side_store_backing;
IOrderBookCellStore<string> _user_store_backing;
IOrderBookCellStore<string> _account_store_backing;
IOrderBookCellStore<TimeInForce> _time_in_force_store_backing;
IOrderBookCellStore<DateTime> _time_created_store_backing;
// State
IOrderBookCellStore<DateTime> _last_change_store_backing;
IOrderBookCellStore<OrderStatus> _status_store_backing;
IOrderBookCellStore<double> _price_store_backing;
IOrderBookCellStore<uint> _quantity_store_backing;
IOrderBookCellStore<uint> _filled_quantity_store_backing;
IOrderBookCellStore<double> _last_price_store_backing;
IOrderBookCellStore<double> _avg_price_store_backing;
IOrderBookCellStore<string> _last_msg_store_backing;
IOrderBookCellStore<OrderSubType> _order_sub_type_store_backing;
// Core
IOrderBookCellStore<uint> _order_key_store => _order_key_store_backing ?? (_order_key_store_backing = CellFactory.create(this, _book_vm.columns.order_key_column, nameof(order_key), r => r._order.Key, r => r._order_key_store));
IOrderBookCellStore<uint> _parent_order_key_store => _parent_order_key_store_backing ?? (_parent_order_key_store_backing = CellFactory.create(this, _book_vm.columns.parent_key_column, nameof(parent_order_key), r => r._order.ParentKey, r => r._parent_order_key_store));
IOrderBookCellStore<uint> _security_key_store => _security_key_store_backing ?? (_security_key_store_backing = CellFactory.create(this, _book_vm.columns.security_key_column, nameof(security_key), r => r._order.security_key(), r => r._security_key_store));
IOrderBookCellStore<SecurityImmutable?> _security_store => _security_store_backing ?? (_security_store_backing = CellFactory.create_nullable(this, _book_vm.columns.security_column, nameof(security), r => r._security, r => r._security_store, v => v.display_name()));
IOrderBookCellStore<Venue> _exchange_store => _exchange_store_backing ?? (_exchange_store_backing = CellFactory.create(this, _book_vm.columns.exchange_column, nameof(exchange), r => r._security.HasValue ? _security.Value.Exchange : Venue.Unset, r => r._exchange_store));
IOrderBookCellStore<string> _symbol_store => _symbol_store_backing ?? (_symbol_store_backing = CellFactory.create(this, _book_vm.columns.symbol_column, nameof(symbol), r => r._symbol, r => r._symbol_store, v => !String.IsNullOrEmpty(v) ? v : "unknown symbol"));
// Def
IOrderBookCellStore<CustomOrderDefinition.CustomOneofCase> _order_type_store => _order_type_store_backing ?? (_order_type_store_backing = CellFactory.create(this, _book_vm.columns.order_type_column, nameof(order_type), r => r._order.order_type(), r => r._order_type_store));
IOrderBookCellStore<Side> _side_store => _side_store_backing ?? (_side_store_backing = CellFactory.create(this, _book_vm.columns.side_column, nameof(side), r => r._order.side(), r => r._side_store));
IOrderBookCellStore<string> _user_store => _user_store_backing ?? (_user_store_backing = CellFactory.create(this, _book_vm.columns.user_column, nameof(user), r => _order.user(), r => r._user_store));
IOrderBookCellStore<string> _account_store => _account_store_backing ?? (_account_store_backing = CellFactory.create(this, _book_vm.columns.account_column, nameof(account), r => r._order.account(), r => r._account_store));
IOrderBookCellStore<TimeInForce> _time_in_force_store => _time_in_force_store_backing ?? (_time_in_force_store_backing = CellFactory.create(this, _book_vm.columns.time_in_force_column, nameof(time_in_force), r => r._order.time_in_force(), r => r._time_in_force_store));
IOrderBookCellStore<DateTime> _time_created_store => _time_created_store_backing ?? (_time_created_store_backing = CellFactory.create(this, _book_vm.columns.time_created_column, nameof(time_created), r => r._order.created(), r => r._time_created_store, format_date));
// State
IOrderBookCellStore<DateTime> _last_change_store => _last_change_store_backing ?? (_last_change_store_backing = CellFactory.create(this, _book_vm.columns.last_change_column, nameof(last_change), r => _order.last_change(), r => r._last_change_store, format_date));
IOrderBookCellStore<OrderStatus> _status_store => _status_store_backing ?? (_status_store_backing = CellFactory.create(this, _book_vm.columns.status_column, nameof(status), r => _order.status(), r => r._status_store));
IOrderBookCellStore<double> _price_store => _price_store_backing ?? (_price_store_backing = CellFactory.create(this, _book_vm.columns.price_column, nameof(price), r => _order.price(), r => r._price_store, format_price));
IOrderBookCellStore<uint> _quantity_store => _quantity_store_backing ?? (_quantity_store_backing = CellFactory.create(this, _book_vm.columns.qty_column, nameof(quantity), r => _order.quantity(), r => r._quantity_store));
IOrderBookCellStore<uint> _filled_quantity_store => _filled_quantity_store_backing ?? (_filled_quantity_store_backing = CellFactory.create(this, _book_vm.columns.filled_qty_column, nameof(filled_quantity), r => _order.filled(), r => r._filled_quantity_store));
IOrderBookCellStore<double> _last_price_store => _last_price_store_backing ?? (_last_price_store_backing = CellFactory.create(this, _book_vm.columns.last_price_column, nameof(last_price), r => _order.last_price(), r => r._last_price_store, format_price));
IOrderBookCellStore<double> _avg_price_store => _avg_price_store_backing ?? (_avg_price_store_backing = CellFactory.create(this, _book_vm.columns.avg_price_column, nameof(avg_price), r => _order.avg_price(), r => r._avg_price_store, format_price));
IOrderBookCellStore<string> _last_msg_store => _last_msg_store_backing ?? (_last_msg_store_backing = CellFactory.create(this, _book_vm.columns.last_message_column, nameof(last_msg), r => _order.last_msg(), r => r._last_msg_store));
IOrderBookCellStore<OrderSubType> _order_sub_type_store => _order_sub_type_store_backing ?? (_order_sub_type_store_backing = CellFactory.create(this, _book_vm.columns.sub_type_column, nameof(order_sub_type), r => r._order.sub_type(), r => r._order_sub_type_store, format_sub_type));
readonly OrderBookVM _owner;
IRaiseCanExecuteChangedCommand _show_order_modify_cmd, _toggle_expanded_cmd, _cancel_cmd, _modify_order_quantity, _modify_order_price;
bool _is_parent, _is_expanded, _is_selected;
int _depth_level;
SecurityMD? _security_md => order_tuple.security_md;
// for mini-ob
uint _prospective_quantity;
double _prospective_price;
static string format_date(DateTime v) => $"{v:M/d} {v:h:mm:ss.fff tt}";
string format_price(double v) => _security.HasValue ?
_security.Value.ContractSpec.format(v) :
Math.Round(v, _unknown_security_price_num_fractional_digits, MidpointRounding.AwayFromZero).ToString(_unknown_security_price_format_string);
static string format_sub_type(OrderSubType v) {
public OrderBookRowVM(OrderBookVM owner, OrderRecordTuple order_tuple, bool is_expanded) {
_owner = owner;
this.order_tuple = order_tuple;
is_visible = !is_child;
_is_expanded = is_expanded;
prospective_quantity = this.quantity;
prospective_price = this.price;
this.invalidated += OrderBookRowVM_invalidated;
}
private void OrderBookRowVM_invalidated() {
modify_order_quantity?.TryRaiseCanExecuteChanged();
modify_order_price?.TryRaiseCanExecuteChanged();
}
protected override void Dispose(bool disposing) {
this.invalidated -= OrderBookRowVM_invalidated;
base.Dispose(disposing);
}
public event Action invalidated;
// Core
public OrderRecordTuple order_tuple { get; private set; }
public OrderRecordImmutable order => order_tuple.order;
public OrderMetadata order_metadata => _owner.get_metadata(this);
public uint order_key => order.Key;
public uint parent_order_key => order.ParentKey;
public uint security_key => order.security_key();
public SecurityImmutable? security => _security_md.security();
public ContractSpecificationImmutable? contract_spec => security.spec_or_default();
public string security_text => security.display_name();
public Venue exchange => _security_md.exchange();
public string symbol => _security_md.symbol();
// Def
public CustomOrderDefinition.CustomOneofCase order_type => order.order_type();
public OrderSubType order_sub_type => order.sub_type();
public string order_sub_type_text => _fmt_order_sub_type(order_sub_type);
public Side side => order.side();
public string user => order.user();
public string account => order.account();
public TimeInForce time_in_force => order.time_in_force();
public DateTime time_created => order.created();
public string time_created_text => _fmt_date(time_created);
// State
public DateTime last_change => order.last_change();
public string last_change_text => _fmt_date(last_change);
public OrderStatus status => order.status();
public double price => order.price();
public double last_price => order.last_price();
public double avg_price => order.avg_price();
public string price_text => _fmt_price(price);
public string last_price_text => _fmt_price(last_price);
public string avg_price_text => _fmt_price(avg_price);
public uint quantity => order.quantity();
public uint filled_quantity => order.filled();
public string last_msg => order.last_msg();
// Tree
public bool is_parent { get => _is_parent; set => Set(ref _is_parent, value); }
public bool is_child => order.has_parent();
public bool is_visible { get; set; }
public bool is_expanded {
get => _is_expanded;
set {
if (Set(ref _is_expanded, value))
_owner.on_expanded_changed(this);
}
}
public int depth_level { get => _depth_level; set => Set(ref _depth_level, value); }
public bool is_selected {
get => _is_selected;
set => Set(ref _is_selected, value);
}
public uint prospective_quantity {
get => _prospective_quantity;
set {
if (Set(ref _prospective_quantity, value)) {
RaisePropertyChanged(nameof(is_qty_dirty));
}
}
}
public double prospective_price {
get => _prospective_price;
set {
if (Set(ref _prospective_price, value)) {
RaisePropertyChanged(nameof(is_price_dirty));
}
}
}
public bool is_qty_dirty => this.prospective_quantity != this.quantity;
public bool is_price_dirty => this.prospective_price != this.price;
// Commands
public ICommand toggle_command => _toggle_expanded_cmd ?? (
_toggle_expanded_cmd = new DelegateCommand(() => is_expanded = !is_expanded));
ICommand DIYGrid.IActionableRowVM.default_command => _show_order_modify_cmd ??
(_show_order_modify_cmd = new DelegateCommand(() => _owner.show_order_modify(this), () => status == OrderStatus.Working));
public ICommand cancel_command => _cancel_cmd ??
(_cancel_cmd = new DelegateCommand(() => _owner.cancel_order(this), () => status == OrderStatus.Working));
public ICommand modify_order_quantity => _modify_order_quantity ??
(_modify_order_quantity = new DelegateCommand(
(desired_quantity) => {
_owner.modify_order_quantity(this, (uint)desired_quantity);
_unselect_me();
},
(desired_quantity) =>
(desired_quantity != null && this.status == OrderStatus.Working && (uint)desired_quantity != this.quantity)
|| is_qty_dirty
)
);
public ICommand modify_order_price => _modify_order_price ??
(_modify_order_price = new DelegateCommand(
(desired_price) => {
_owner.modify_order_price(this, (double)desired_price);
_unselect_me();
},
(desired_price) =>
(desired_price != null && this.status == OrderStatus.Working && (double)desired_price != this.price)
|| is_price_dirty
)
);
private void _unselect_me() {
this.is_selected = false;
_owner.current_row = null;
}
// These fns present a way for the orderbook to avoid noisy, intermittent collection changes during "Expand/Collapse All" ops,
// when incremental changes are ignored in favor of a full reset, after 'is_expanded' is updated for all rows.
internal void expand_internal() => Set(ref _is_expanded, true);
internal void collapse_internal() => Set(ref _is_expanded, false);
public void update(OrderRecordTuple order_tuple) {
Log.assert(order_tuple.order.Key == this.order.Key, "OrderBookRowVM: update_order was called with an order with the wrong key.");
bool had_security = _security_md.HasValue;
// _show_order_modify_cmd is lazy (instantiated in the default_command getter); it might be null, don't not force instantiation
bool? could_modify_execute = _show_order_modify_cmd?.CanExecute(null);
OrderRecordImmutable old_order = order;
this.order_tuple = order_tuple;
// most cells are "render-cells," which don't use databindings - the only way to update such cells is to raise the invalidated event
bool invalidate = !(
old_order.last_change() == order.last_change() &&
old_order.filled() == order.filled() &&
old_order.last_price() == order.last_price() &&
old_order.avg_price().Equals(order.avg_price()) &&
old_order.price().Equals(order.price()) &&
old_order.quantity() == order.quantity() &&
old_order.last_msg() == order.last_msg());
// however, the order-type and security cells DO use databindings - if these cells have changed, the PropertyChanged event must be raised regardless of whether or not invalidated is raised
if (old_order.order_type() != order.order_type())
RaisePropertyChanged(nameof(order_type), nameof(order_metadata));
if (old_order.quantity() != order.quantity()) {
RaisePropertyChanged(nameof(quantity));
RaisePropertyChanged(nameof(is_qty_dirty));
}
if (old_order.price() != order.price()) {
RaisePropertyChanged(nameof(price));
RaisePropertyChanged(nameof(is_price_dirty));
}
if (!had_security && _security_md.HasValue) {
RaisePropertyChanged(nameof(security), nameof(security_text));
// need to raise invalidated to refresh the price cells (the security.ContractSpec determines how they are formatted - Ticks!)
invalidate = true;
}
if (old_order.status() != order.status()) {
invalidate = true;
RaisePropertyChanged(nameof(status));
}
if (could_modify_execute.HasValue && could_modify_execute.Value != _show_order_modify_cmd.CanExecute(null))
_show_order_modify_cmd.RaiseCanExecuteChanged();
if (invalidate)
raise_invalidated();
}
public void raise_invalidated() => invalidated?.Invoke();
public bool Equals(OrderBookRowVM other) => other != null && other.order.Key == order.Key;
public override bool Equals(object obj) => Equals(obj as OrderBookRowVM);
public override int GetHashCode() => (int)order.Key;
public override string ToString() => $"{order_key} {last_change} {parent_order_key}";
static string _fmt_date(DateTime v) => $"{v:M/d} {v:h:mm:ss.fff tt}";
static string _fmt_order_sub_type(OrderSubType v) {
switch (v) {
case OrderSubType.HedgingLeg: return "H";
case OrderSubType.QuotingLeg: return "Q";
default: return string.Empty;
}
}
public OrderBookRowVM(IBus bus, OrderBookVM source_vm, OrdersModel model, OrderRecordImmutable record, SecurityMD? s) {
_book_vm = source_vm;
_model = model;
_order = record;
_security_md = s;
_symbol = _security_md.HasValue ? _security_md.Value.security.Symbol : null;
//animator = new PredicateAnimator<OrderBookRowVM,OrderRecordTuple>(this);
_visible = backing(nameof(is_visible), true);
_expanded = backing(nameof(is_expanded), false);
_depth_level = backing(nameof(depth_level), () => {
int i = 0;
var curr = this;
while (curr._parent != null) {
i++;
curr = curr._parent;
}
return i;
});
toggle_command = new DelegateCommand(() => is_expanded = !is_expanded);
_show_order_modify_cmd = new ObservantCommand(() => bus.post(SparkTags.ShowModifyOrderTag, _order.Key));
_show_order_modify_cmd.OnNext(status.value == OrderStatus.Working);
row_passes_filter = _book_vm.columns.filter(this);
_passes_filter = backing(nameof(passes_filter), row_passes_filter);
}
public event Action invalidated;
public event Action<(OrderRecordTuple old_val, OrderRecordTuple new_val)> updated;
public OrderRecordImmutable order => _order;
public OrderMetadata order_metadata => _model.factory.get_metadata(order_type.value);
public OrderBookRowVM parent => _parent;
public OrderRecordTuple current_data => new OrderRecordTuple(_order, _security_md);
// Core
public IOrderBookCell<uint> order_key => _order_key_store.current;
public IOrderBookCell<uint> parent_order_key => _parent_order_key_store.current;
public IOrderBookCell<uint> security_key => _security_key_store.current;
public IOrderBookCell<SecurityImmutable?> security => _security_store.current;
public IOrderBookCell<Venue> exchange => _exchange_store.current;
public IOrderBookCell<string> symbol => _symbol_store.current;
// Def
public IOrderBookCell<CustomOrderDefinition.CustomOneofCase> order_type => _order_type_store.current;
public IOrderBookCell<Side> side => _side_store.current;
public IOrderBookCell<string> user => _user_store.current;
public IOrderBookCell<string> account => _account_store.current;
public IOrderBookCell<TimeInForce> time_in_force => _time_in_force_store.current;
public IOrderBookCell<DateTime> time_created => _time_created_store.current;
// State
public IOrderBookCell<DateTime> last_change => _last_change_store.current;
public IOrderBookCell<OrderStatus> status => _status_store.current;
public IOrderBookCell<double> price => _price_store.current;
public IOrderBookCell<uint> quantity => _quantity_store.current;
public IOrderBookCell<uint> filled_quantity => _filled_quantity_store.current;
public IOrderBookCell<double> last_price => _last_price_store.current;
public IOrderBookCell<double> avg_price => _avg_price_store.current;
public IOrderBookCell<string> last_msg => _last_msg_store.current;
public IOrderBookCell<OrderSubType> order_sub_type => _order_sub_type_store.current;
// Commands
public ICommand toggle_command { get; }
ICommand ui.controls.DIYGrid.IActionableRowVM.default_command => _show_order_modify_cmd;
// Accessories
public bool is_parent => _child_rows.Count > 0;
public bool is_child => _order.has_parent();
public IReadOnlyCollection<OrderBookRowVM> child_orders => _child_rows;
public OrderBookVM.BranchFilterSource branch_filter_source => _branch_filter_source;
public bool passes_filter => _passes_filter.Value;
public bool row_passes_filter { get; private set; }
public bool is_visible {
get => _visible.Value;
set {
_visible.set(value);
if (is_expanded) {
foreach (var child_row in _child_rows)
child_row.is_visible = value;
}
}
}
public bool is_expanded {
get => _expanded.Value;
set {
_expanded.set(value);
foreach (var child_row in child_orders)
child_row.is_visible = value;
}
}
public int depth_level => _depth_level.Value;
SecurityImmutable? _security => _security_md?.security;
// '_dynamic_render_cells' are presented by 'RenderColumns,' and may change value during the lifetime of this row
// Note: by instantiating all these cells inside a CellPropertyGroup, we can update all of them with a single call.
// this reduces the risk of accidentally failing to update one of the cells, esp if we make changes to them down the road.
IEnumerable<IOrderBookCellStore> _dynamic_render_cells {
get {
yield return _last_change_store;
yield return _price_store;
yield return _quantity_store;
yield return _filled_quantity_store;
yield return _avg_price_store;
yield return _last_price_store;
yield return _status_store;
yield return _last_msg_store;
}
}
IEnumerable<IOrderBookCellStore> _security_dependent_cells {
get {
yield return _security_store;
yield return _exchange_store;
yield return _symbol_store;
}
}
IEnumerable<IOrderBookCellStore> _price_cells {
get {
yield return _price_store;
yield return _avg_price_store;
yield return _last_price_store;
yield return _symbol_store;
}
}
public ObservableCollection<RowAnimation> row_animations { get; } = new ObservableCollection<RowAnimation>();
DIYGrid.IGridCellStore[] _cell_stores_backing;
public IReadOnlyList<DIYGrid.IGridCellStore> cell_stores => _cell_stores_backing ?? (_cell_stores_backing = new DIYGrid.IGridCellStore[] {
_security_store,
_security_key_store,
_parent_order_key_store,
_order_key_store,
_symbol_store,
_time_created_store,
_user_store,
_account_store,
_exchange_store,
_time_in_force_store,
_order_type_store,
_order_sub_type_store,
_side_store,
_last_change_store,
_price_store,
_quantity_store,
_filled_quantity_store,
_avg_price_store,
_last_price_store,
_status_store,
_last_msg_store
});
public static Stack<OrderBookRowVM> lineage(OrderBookRowVM node) {
var rv = new Stack<OrderBookRowVM>();
rv.Push(node);
while (node._parent != null)
rv.Push((node = node._parent));
return rv;
}
public void join_branch_filter_source(OrderBookVM.BranchFilterSource new_branch_filter_source) {
if (_branch_filter_source != null) {
_branch_filter_source.passes_filter_changed -= _passes_filter.set;
}
_branch_filter_source = new_branch_filter_source;
_branch_filter_source.set_row_passes_filter(_order.Key, row_passes_filter);
foreach (var child_row in _child_rows)
child_row.join_branch_filter_source(_branch_filter_source);
_passes_filter.set(_branch_filter_source.passes_filter);
_branch_filter_source.passes_filter_changed += _passes_filter.set;
}
/// <summary>
/// Attaches the child-row to this row's branch-filter-source, and recursively refreshes sort/filter properties for the child and it's descendents.
/// </summary>
public void add_child_order(OrderBookRowVM child_row) {
if (_child_rows.Add(child_row) && _child_rows.Count == 1)
RaisePropertyChanged(nameof(is_parent));
child_row._parent = this;
// child orders that arrived in an earlier update use a temporary branch_filter_source
// need to tell _book_vm to remove the now-stale branch_filter_source.
if (child_row._branch_filter_source != null)
_book_vm.remove_branch_filter_root(child_row._order.Key);
// recursively updates 'live_sorting_props,' and 'is_visible' for child_row and descendents
child_row._on_lineage_changed();
}
public void remove_child_order(OrderBookRowVM child_row) {
if (_child_rows.Remove(child_row) && _child_rows.Count == 0)
RaisePropertyChanged(nameof(is_parent));
}
public void remove_parent(OrderBookVM.BranchFilterSource new_branch_filter_source) {
_parent = null;
join_branch_filter_source(new_branch_filter_source);
refresh_row_passes_filter();
foreach (var p in _book_vm.grid_settings_manager.live_sorting_props)
RaisePropertyChanged(p);
_depth_level.refresh();
foreach (OrderBookRowVM child_row in _child_rows)
child_row._on_lineage_changed();
}
public void update_order(OrderRecordImmutable order) {
Log.assert(order.ParentKey == _order.ParentKey, "Parent Keys can change?");
Log.assert(order.Key == _order.Key, "Keys can change?");
var old_data = current_data;
_order = order;
if (_update_partial(_dynamic_render_cells))
raise_invalidated();
_show_order_modify_cmd.OnNext(status.value == OrderStatus.Working);
refresh_row_passes_filter();
updated?.Invoke((old_data, current_data));
}
public void update_security(SecurityMD security_md) {
var old_data = current_data;
var old_spec = _security.spec_or_default();
_security_md = security_md;
_symbol = _security_md.HasValue ? _security_md.Value.security.Symbol : null;
if (!Equals(old_spec, _security.spec_or_default())) {
_update_partial(_security_dependent_cells);
foreach (var cell_store in _price_cells)
cell_store.regenerate_cell();
raise_invalidated();
} else if (_update_partial(_security_dependent_cells)) {
raise_invalidated();
}
refresh_row_passes_filter();
updated?.Invoke((old_data, current_data));
}
public void refresh_row_passes_filter() {
row_passes_filter = _book_vm.columns.filter(this);
if (_branch_filter_source != null) {
_branch_filter_source.set_row_passes_filter(_order.Key, row_passes_filter);
} else {
_passes_filter.set(row_passes_filter);
}
}
public void raise_invalidated() => invalidated?.Invoke();
public bool Equals(OrderBookRowVM other) => other != null && other._order.Key == _order.Key;
public override bool Equals(object obj) => Equals(obj as OrderBookRowVM);
public override int GetHashCode() => (int)_order.Key;
public override string ToString() => $"{order_key} {last_change} {parent_order_key}";
protected override void Dispose(bool disposing) {
// idempotent
if (_branch_filter_source != null) {
_branch_filter_source.passes_filter_changed -= _passes_filter.set;
_branch_filter_source = null;
}
}
static IEnumerable<OrderBookRowVM> _descendents(OrderBookRowVM root) {
foreach (var child in root.child_orders) {
yield return child;
foreach (var grand_child in _descendents(child))
yield return grand_child;
}
}
static IOrderBookCellStore _cell_store_from_column(OrderBookRowVM row, Columns column) {
switch (column) {
case Columns.OrderKey: return row._order_key_store;
case Columns.ParentKey: return row._parent_order_key_store;
case Columns.SecurityKey: return row._security_key_store;
case Columns.Security: return row._security_store;
case Columns.Symbol: return row._symbol_store;
case Columns.Exchange: return row._exchange_store;
case Columns.OrderType: return row._order_type_store;
case Columns.Side: return row._side_store;
case Columns.User: return row._user_store;
case Columns.Account: return row._account_store;
case Columns.TimeInForce: return row._time_in_force_store;
case Columns.Time: return row._time_created_store;
case Columns.LastChange: return row._last_change_store;
case Columns.Status: return row._status_store;
case Columns.Price: return row._price_store;
case Columns.Size: return row._quantity_store;
case Columns.Filled: return row._filled_quantity_store;
case Columns.LastPrice: return row._last_price_store;
case Columns.AvgFilled: return row._avg_price_store;
case Columns.LastMessage: return row._last_msg_store;
case Columns.SubType: return row._order_sub_type_store;
default: throw new ArgumentException(nameof(column));
}
}
// Note: this method assumes that lineage exists (i.e. _parent is not null).
void _on_lineage_changed() {
_depth_level.refresh();
// parent 'branch_filter_source' can be null:
// if the parent is itself an orphan - meaning this is the middle of an update -
// the final branch-filter-root will be determined after all new orders have been added
if (_parent._branch_filter_source != null)
join_branch_filter_source(_parent._branch_filter_source);
_visible.set(_parent.is_visible && _parent.is_expanded);
// refresh the sort by calling regenerate_cell() for each live-sorting-columns corresponding cell-store
foreach (Columns column in _book_vm.live_sorting_columns)
_cell_store_from_column(this, column).regenerate_cell();
// finally, repeat for each child-row
foreach (var child_row in _child_rows)
child_row._on_lineage_changed();
}
bool _update_partial(IEnumerable<IOrderBookCellStore> target_cell_stores) {
bool updated = false;
foreach (var cell_store in target_cell_stores) {
if (cell_store.refresh()) {
updated = true;
if (_book_vm.live_sorting_columns.Contains(cell_store.column.key)) {
// refresh the sort by calling regenerate_cell() for each descendent-row
foreach (OrderBookRowVM dr in _descendents(this))
cell_store.select_other(dr).regenerate_cell();
}
}
}
return updated;
}
string _fmt_price(double v) => security.HasValue ?
security.Value.ContractSpec.format(v) :
Math.Round(v, _unknown_security_price_num_fractional_digits, MidpointRounding.AwayFromZero).ToString(_unknown_security_price_format_string);
}
}
@@ -1,250 +1,218 @@
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.shell;
using mbts.collections;
using bts.spark;
using bts.utils;
using mbts.persist;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
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.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using static bts.shell.OrderBookWindowFactory;
using static mbts.spark.OrderBookView;
using _GridSettingsManager = mbts.ui.GridSettingsManager<mbts.spark.OrderBookRowVM, mbts.spark.OrderRecordTuple, bts.shell.INotificationServiceDataCache, mbts.OrderRecordTuplePredicate, bts.shell.OrderBookWindowFactory.OrderBookGridSettings>;
namespace mbts.spark {
partial class OrderBookVM : DisposableViewModelBase, IHandleRowSelection {
readonly LiveSortingColumKeys _live_sorting_columns;
namespace mbts.spark {
partial class OrderBookVM : ViewModelBase, IDisposable, IHandleRowSelection, IHandleGridSorting, IWillLetYouKnowWhenIDispose {
readonly IBus _bus;
readonly OrdersModel _model;
readonly Property<OrderBookRowVM> _current_row;
readonly OrderBookRowCollection _row_collection;
readonly ObservableCollection<OrderBookRowVM> _selected_rows = new ObservableCollection<OrderBookRowVM>();
readonly OrderBookColumnCollection _columns;
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
readonly List<IDisposable> _resources;
OrderBookStartupData _state;
Dictionary<uint, OrderBookRowVM> _order_rows_cache = new Dictionary<uint, OrderBookRowVM>();
Dictionary<uint, BranchFilterSource> _branch_filter_roots = new Dictionary<uint, BranchFilterSource>();
ObservableCollection<OrderBookRowVM> _order_rows = new ObservableCollection<OrderBookRowVM>();
ObservableCollection<OrderBookRowVM> _selected_rows = new ObservableCollection<OrderBookRowVM>();
SetMap<uint, uint> _parent_to_children = new SetMap<uint, uint>();
SetMap<uint, uint> _security_orders = new SetMap<uint, uint>();
bool _is_row_collection_populated;
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model) {
var order_securities_subject = new Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)>();
columns = new OrderBookColumnCollection(bus, model, order_securities_subject);
rows_view = CollectionViewSource.GetDefaultView(_order_rows);
rows_view.Filter = obj => obj is OrderBookRowVM vm && vm.is_visible && vm.passes_filter;
columns.filter_changed += () => {
foreach (var vm in _order_rows)
vm.refresh_row_passes_filter();
};
var cvls = (ICollectionViewLiveShaping)rows_view;
cvls.IsLiveFiltering = true;
cvls.IsLiveSorting = true;
cvls.LiveFilteringProperties.AddRange(nameof(OrderBookRowVM.is_visible), nameof(OrderBookRowVM.passes_filter));
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager<OrderBookGridSettings> settings_manager, bool rows_are_initially_expanded = false) {
_bus = bus;
_model = model;
_settings_manager = settings_manager;
_columns = new OrderBookColumnCollection(bus, model, model.on_order_securities.Select(u => (u.added.Values.Select(smd => smd.security), u.removed.Values.Select(smd => smd.security))));
// we use an ObservableAppendCollection b/c the mini-ob needs a cancel column at the front
columns = new ObservableAppendCollection<DIYGrid.ColumnBase>(_columns);
_row_collection = new OrderBookRowCollection(ort => new OrderBookRowVM(this, ort, rows_are_initially_expanded), _columns.get_sorting_data().comparer) { filter = _columns.get_filter() };
_row_collection.CollectionChanged += _row_collection_CollectionChanged;
_current_row = backing<OrderBookRowVM>(nameof(current_row));
collapse_all_rows_command = new DelegateCommand(
() => {
foreach (var r in _order_rows.Where(r => r.is_visible && r.passes_filter))
r.is_expanded = false;
});
expand_all_rows_command = new DelegateCommand(
() => {
foreach (var r in _order_rows.Where(r => r.is_visible && r.passes_filter))
r.is_expanded = true;
});
grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, _order_rows, rows_view, columns);
_live_sorting_columns = new LiveSortingColumKeys(grid_settings_manager.live_sorting_props);
SetMap<uint, uint> missing_security_orders = new SetMap<uint, uint>();
IReadOnlyDictionary<uint, SecurityMD> security_dict = new Dictionary<uint, SecurityMD>();
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(on_securities),
model.on_orders.OnDispatcher().Subscribe(on_orders),
_columns.filter_changed += on_filter_changed;
collapse_all_rows_command = new DelegateCommand(_row_collection.collapse_all);
expand_all_rows_command = new DelegateCommand(_row_collection.expand_all);
grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, rows, core_columns);
_resources = new List<IDisposable> {
model.on_order_tuples_table.OnDispatcher().Subscribe(_row_collection.update),
grid_settings_manager,
columns,
order_securities_subject);
void on_orders(TableUpdate<OrderRecordImmutable, uint> table) {
// clear everything (most likely the server restarted)
if (table.removed.Count() == _order_rows.Count && !table.added.Any()) {
_selected_rows.Clear();
_order_rows.DisposeAll();
_order_rows.Clear();
_order_rows_cache.Clear();
_branch_filter_roots.Clear();
_parent_to_children.clear();
_security_orders.clear();
missing_security_orders.clear();
Log.Ui.warn("OrderBook received reset message.");
return;
}
var added_securities = new List<SecurityImmutable>(1);
var removed_securities = new List<SecurityImmutable>(1);
foreach (var o in table.removed) {
if (_order_rows_cache.TryPopValue(o.Key, out OrderBookRowVM row)) {
row.Dispose();
_order_rows.Remove(row);
if (_security_orders.removes_key(o.Def.SecurityKey, o.Key)) {
if (row.security.value.HasValue)
removed_securities.Add(row.security.value.Value);
}
if (o.has_parent()) {
_parent_to_children.remove(o.ParentKey, o.Key);
if (row.parent != null) {
row.parent.remove_child_order(row);
} else {
_branch_filter_roots.Remove(o.Key);
}
} else {
_branch_filter_roots.Remove(o.Key);
}
// the child_rows will most-likely be removed in this update, but we can't be sure,
// until that happens they must serve as tempororay branch-filter-roots
foreach (var child_row in row.child_orders) {
var bfs = new BranchFilterSource();
child_row.remove_parent(bfs);
_branch_filter_roots.Add(child_row.order.Key, bfs);
}
missing_security_orders.remove(o.Key);
}
}
foreach (var ch in table.changes)
_order_rows_cache[ch.New.Key].update_order(ch.New);
var orphans = new Dictionary<uint, OrderBookRowVM>();
foreach (var o in table.added) {
SecurityMD? security_md;
if (security_dict.TryGetValue(o.Def.SecurityKey, out SecurityMD smd)) {
security_md = smd;
if (_security_orders.adds_key(o.Def.SecurityKey, o.Key))
added_securities.Add(security_md.Value.security);
} else {
missing_security_orders.add(o.Def.SecurityKey, o.Key);
security_md = null;
_security_orders.add(o.Def.SecurityKey, o.Key);
}
OrderBookRowVM row = new OrderBookRowVM(bus, this, model, o, security_md);
if (!o.has_parent()) {
// if it doesn't have a parent, it must be a root; create a new BranchFilterSource
var bfs = new BranchFilterSource();
_branch_filter_roots.Add(o.Key, bfs);
row.join_branch_filter_source(bfs);
} else {
_parent_to_children.add(o.ParentKey, o.Key);
if (_order_rows_cache.TryGetValue(o.ParentKey, out OrderBookRowVM pvm)) {
pvm.add_child_order(row);
} else {
// (**) look for orphans' parents again after all new orders have been added to _order_vm_cache
// don't create a temporary BranchFilterSource root just yet. (**)
orphans.Add(o.Key, row);
_order_rows_cache.Add(o.Key, row);
try_add_children(row);
continue;
}
}
_order_rows_cache.Add(o.Key, row);
_order_rows.Add(row);
try_add_children(row);
}
// (**) since all new rows have been added by now, any remaining orphans will be assigned their own branch-filter-roots
// (usually, rows are tempororily added to orphans bc their parent is just behind them in the list of added orders) (**)
foreach (OrderBookRowVM row in orphans.Values) {
var bfs = new BranchFilterSource();
row.join_branch_filter_source(bfs);
_branch_filter_roots.Add(row.order.Key, bfs);
_order_rows.Add(row);
}
if (added_securities.Count > 0 || removed_securities.Count > 0)
order_securities_subject.OnNext((added_securities, removed_securities));
void try_add_children(OrderBookRowVM row) {
if (_parent_to_children.try_lookup(row.order.Key, out ISet<uint> children))
foreach (uint child_key in children) {
OrderBookRowVM child_vm = _order_rows_cache[child_key];
// (**) if children arrived in an earlier update, this will notify them that their parent has arrived (**)
// - their tempororary branch-filter-roots will be removed, and they will join that of their parent's
// - for the live-filter-properties, their corresponding cells will be regenerated and they will be re-inserted beneath their new parent.
row.add_child_order(child_vm);
if (orphans.Remove(child_key))
_order_rows.Add(child_vm);
else {
_order_rows.Remove(child_vm);
_order_rows.Add(child_vm);
}
}
}
Disposable.Create(() => {
_columns.filter_changed -= on_filter_changed;
_row_collection.CollectionChanged -= _row_collection_CollectionChanged;
Disposable.dispose(_row_collection);
}),
_columns
};
if (settings_manager != null) {
_resources.Add(settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager));
}
void on_filter_changed() => _row_collection.filter = _columns.get_filter();
}
// I assume we don't need to worry about orders "losing" or "changing" their security
// once the order vm discovers its security (by id), it will never change.
void on_securities(IReadOnlyDictionary<uint, SecurityMD> securities) {
security_dict = securities;
var added_securities = new List<SecurityImmutable>(); // assume securities won't be removed
foreach (var missing_sec_orders in missing_security_orders.ToList()) {
if (securities.TryGetValue(missing_sec_orders.Key, out SecurityMD smd)) {
missing_security_orders.remove(missing_sec_orders.Key);
added_securities.Add(smd.security);
foreach (var order_key in missing_sec_orders) {
var vm = _order_rows_cache[order_key];
vm.update_security(smd);
}
}
}
if (added_securities.Count > 0)
order_securities_subject.OnNext((added_securities, new List<SecurityImmutable>(0)));
}
private void _row_collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
is_row_collection_populated = _row_collection.Count != 0;
}
public event Action<OrderRecordImmutable?> selected_order_changed;
public event Action<bool> OnDisposed;
public OrderBookColumnCollection columns { get; }
public ISet<Columns> live_sorting_columns => _live_sorting_columns;
public ICollectionView rows_view { get; }
public OrderBookRowVM current_row { get => _current_row.Value; set => _current_row.Value = value ?? _selected_rows.FirstOrDefault(); }
public ObservableAppendCollection<DIYGrid.ColumnBase> columns { get; }
public ObservableCollection<DIYGrid.ColumnBase> core_columns => _columns;
public ObservableCollection<OrderBookRowVM> rows => _row_collection;
public OrderBookRowVM current_row {
get => _current_row.Value;
set {
_current_row.Value = value;
// this is so that when the current_row is set to null from
// the row_vm, we propagate the change up to the OrderBookWindow
// so that the cancel and modify commands can_execute is updated.
// Note that this is really only for if/when the edit_xxx columns
// from the mini-ob are present in regular orderbook, else this is
// a no-op.
selected_order_changed?.Invoke(current_order);
}
}
public OrderRecordImmutable? current_order => current_row?.order;
public IEnumerable<OrderRecordImmutable> selected_orders => _selected_rows.Select(r => r.order);
public GridSettingsManager<OrderBookRowVM, OrderRecordTuple, INotificationServiceDataCache, OrderRecordTuplePredicate> grid_settings_manager { get; }
public _GridSettingsManager grid_settings_manager { get; }
public ICommand collapse_all_rows_command { get; }
public ICommand expand_all_rows_command { get; }
public bool is_row_collection_populated {
get => _is_row_collection_populated;
set => Set(ref _is_row_collection_populated, value);
}
public void row_selection_changed(SelectionChangedEventArgs e) {
if (e.RemovedItems != null) {
foreach (var row_vm in e.RemovedItems.Cast<OrderBookRowVM>()) {
row_vm.is_selected = false;
}
}
if (e.AddedItems != null) {
foreach (var row_vm in e.AddedItems.Cast<OrderBookRowVM>()) {
row_vm.is_selected = true;
}
}
RowSelectionHelper.apply_changes(_selected_rows, e);
selected_order_changed?.Invoke(current_order);
}
public OrderBookStartupData create() {
var (grid, animations) = grid_settings_manager.create();
return new OrderBookStartupData(columns.create(), grid, animations.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications)));
var (filters, sorting) = _columns.create();
var gs = grid_settings_manager.create();
return new OrderBookStartupData(filters, gs.general.with(sorting_criterion: sorting), gs.predicate_animations, use_defaults: _settings_manager.instance_settings == null);
}
public void load_from(OrderBookStartupData state) {
if (state == null) return;
_state = state;
grid_settings_manager.load_from((state.grid, state.predicate_animations));
columns.load_from(state.filters);
var gs = state.grid_settings_wrapper();
if (gs != null) {
grid_settings_manager.load_from(gs);
}
(var old_filter, var old_sort) = _columns.create();
#pragma warning disable CS0612 // Type or member is obsolete
_columns.load_from((state.filters, (state.grid ?? OrderBookStartupData.Default.grid).sorting_criterion));
#pragma warning restore CS0612 // Type or member is obsolete
(var new_filter, var new_sort) = _columns.create();
bool reset_filter = !new_filter.Equals(old_filter);
bool reset_sorting = !new_sort.Equals(old_sort);
if (reset_filter) {
if (reset_sorting) {
// both filter and sort changed; use defer_refresh to avoid refreshing the collection twice
using (_row_collection.defer_refresh()) {
_row_collection.filter = _columns.get_filter();
_row_collection.comparer = _columns.get_sorting_data().comparer;
/// <see cref="OrderBookRowCollection.reset(bool)"/> is an optimization; a way to hedge our bets. Interally it calls Clear(), then AddAll(_visible_rows).
/// Otherwise, <see cref="OrderBookRowCollection.refresh(bool)"/> would be called when we exit/dispose the defer_refresh() scope.
/// The latter method calls sync_with(_visible_rows) internally, which won't perform as well as the former if _visible_rows has changed significantly.
_row_collection.reset(true);
}
} else {
// only filter changed; row_collection will refresh accordingly
_row_collection.filter = _columns.get_filter();
}
} else if (reset_sorting) {
// only sorting changed; row_collection will refresh accordingly
_row_collection.comparer = _columns.get_sorting_data().comparer;
}
}
internal void remove_branch_filter_root(uint key) =>
_branch_filter_roots.Remove(key);
protected override void Dispose(bool disposing) =>
Disposable.dispose(_order_rows);
public void on_sorting(object sender, DIYGridSortingEventArgs e) {
if (e.Column is ICVOrderBookColumn col) {
e.Handled = true;
_columns.on_sorting(col, !KeyboardExtensions.IsShiftDown());
var sorting_data = _columns.get_sorting_data();
Log.Ui.info($"{nameof(OrderBookRowVM)} begin resort: {sorting_data}");
_row_collection.comparer = sorting_data.comparer;
} else {
Log.Ui.warn($"{nameof(OrderBookVM)} failed to process sorting event: Column was not of type {nameof(ICVOrderBookColumn)}");
}
}
static GridSettingsManager<OrderBookRowVM, OrderRecordTuple, INotificationServiceDataCache, OrderRecordTuplePredicate> _create_grid_settings_manager(
IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model,
ObservableCollection<OrderBookRowVM> rows, ICollectionView rows_view, ObservableCollection<DIYGrid.ColumnBase> columns) {
internal void show_order_modify(OrderBookRowVM row) =>
_bus.post(SparkTags.ShowModifyOrderTag, row.order_key);
internal void cancel_order(OrderBookRowVM row) =>
_bus.post(SparkTags.CancelOrders, new List<uint> { row.order_key });
internal void modify_order_quantity(OrderBookRowVM row, uint size) =>
_bus.post(SparkTags.UpdateOrder, (row.order_key, row.price, size));
internal void modify_order_price(OrderBookRowVM row, double price) =>
_bus.post(SparkTags.UpdateOrder, (row.order_key, price, row.quantity));
internal void on_expanded_changed(OrderBookRowVM row) {
using (_row_collection.defer_refresh(true)) {
if (row.is_expanded) {
_on_expanded(row);
} else {
_on_collapsed(row);
}
}
}
void _on_expanded(OrderBookRowVM row) {
foreach (var node in _row_collection.get_inner_nodes(row)) {
node.value.is_visible = true;
if (node.value.is_expanded)
_on_expanded(node.value);
}
}
void _on_collapsed(OrderBookRowVM row) {
foreach (var dr in _row_collection.get_descendent_rows(row))
dr.is_visible = false;
}
public OrderMetadata get_metadata(OrderBookRowVM row) => _model.factory.get_metadata(row.order_type);
public void Dispose() {
Disposable.dispose(_resources);
OnDisposed?.Invoke(true);
}
static _GridSettingsManager _create_grid_settings_manager(
IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model, ObservableCollection<OrderBookRowVM> rows, ObservableCollection<DIYGrid.ColumnBase> columns) {
// make font-weight immutable for all columns (the font-weight rule is hard-coded: Regular for root-orders, Light for all others)
// TODO: Remove the immutable font-weight flags when cell animation is ready
var col_settings_flags = EnumHelper.get_values<Columns>().ToDictionary(
@@ -254,8 +222,8 @@ namespace mbts.spark {
foreach (var c in new[] { Columns.Side, Columns.Size, Columns.Filled }) {
col_settings_flags[c.ToString()] = GridColumnSettingsFeatureFlags.ImmutableFontColor | GridColumnSettingsFeatureFlags.ImmutableFontWeight;
}
return new GridSettingsManager<OrderBookRowVM, OrderRecordTuple, INotificationServiceDataCache, OrderRecordTuplePredicate>(
owner_title, bus, notification_service, model, rows, rows_view, columns,
return new _GridSettingsManager(
owner_title, bus, notification_service, model, rows, columns,
() => {
var pvm = PredicateBuilderVMWrapper.create<OrderRecordTuple, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
OrderRecordTuplePredicateVMFactory.get_oneofs(bus, model), rp => rp.predicate_type_case);
@@ -267,7 +235,10 @@ namespace mbts.spark {
OrderRecordTuplePredicateVMFactory.get_oneofs(bus, model), rp => rp.predicate_type_case);
pvm.selected_predicate_type = _corresponding_predicate_type(EnumHelper.parse_or_default<Columns>(col_id));
return pvm;
}, notification_service.data_cache, col_settings_flags);
},
(gen, anims) => new OrderBookGridSettings(gen, anims.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))),
(notification_service == null) ? null : notification_service.data_cache,
col_settings_flags);
}
static OrderRecordTuplePredicate.PredicateTypeOneofCase _corresponding_predicate_type(Columns col) {
@@ -295,84 +266,5 @@ namespace mbts.spark {
default: return default;
}
}
internal class BranchFilterSource {
readonly HashSet<uint> _orders_pass_filter = new HashSet<uint>();
public event Action<bool> passes_filter_changed;
public bool passes_filter => _orders_pass_filter.Count > 0;
public void set_row_passes_filter(uint key, bool value) {
if (value) {
if (_orders_pass_filter.Add(key) && _orders_pass_filter.Count == 1)
passes_filter_changed?.Invoke(value);
} else if (_orders_pass_filter.Remove(key) && _orders_pass_filter.Count == 0) {
passes_filter_changed?.Invoke(value);
}
}
}
class LiveSortingColumKeys : HashSet<Columns> {
public LiveSortingColumKeys(ObservableCollection<string> live_sorting_props) {
_add_items(live_sorting_props);
live_sorting_props.CollectionChanged += (s, e) => {
switch (e.Action) {
case NotifyCollectionChangedAction.Add: _add_items(e.NewItems); break;
case NotifyCollectionChangedAction.Remove: _remove_items(e.OldItems); break;
case NotifyCollectionChangedAction.Replace:
_remove_items(e.OldItems);
_add_items(e.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
Clear();
_add_items(live_sorting_props);
break;
}
};
}
public static bool try_parse(string prop_name, out Columns result) {
switch (prop_name) {
case nameof(OrderBookRowVM.security): result = Columns.Security; break;
case nameof(OrderBookRowVM.symbol): result = Columns.Symbol; break;
case nameof(OrderBookRowVM.time_created): result = Columns.Time; break;
case nameof(OrderBookRowVM.last_change): result = Columns.LastChange; break;
case nameof(OrderBookRowVM.user): result = Columns.User; break;
case nameof(OrderBookRowVM.account): result = Columns.Account; break;
case nameof(OrderBookRowVM.exchange): result = Columns.Exchange; break;
case nameof(OrderBookRowVM.time_in_force): result = Columns.TimeInForce; break;
case nameof(OrderBookRowVM.order_type): result = Columns.OrderType; break;
case nameof(OrderBookRowVM.order_sub_type): result = Columns.SubType; break;
case nameof(OrderBookRowVM.side): result = Columns.Side; break;
case nameof(OrderBookRowVM.quantity): result = Columns.Size; break;
case nameof(OrderBookRowVM.filled_quantity): result = Columns.Filled; break;
case nameof(OrderBookRowVM.price): result = Columns.Price; break;
case nameof(OrderBookRowVM.avg_price): result = Columns.AvgFilled; break;
case nameof(OrderBookRowVM.last_price): result = Columns.LastPrice; break;
case nameof(OrderBookRowVM.status): result = Columns.Status; break;
case nameof(OrderBookRowVM.last_msg): result = Columns.LastMessage; break;
case nameof(OrderBookRowVM.security_key): result = Columns.SecurityKey; break;
case nameof(OrderBookRowVM.order_key): result = Columns.OrderKey; break;
case nameof(OrderBookRowVM.parent_order_key): result = Columns.ParentKey; break;
default: result = default; return false;
}
return true;
}
void _add_items(IList prop_names) {
foreach (string pn in prop_names) {
if (try_parse(pn, out Columns c))
Add(c);
}
}
void _remove_items(IList prop_names) {
foreach (string pn in prop_names) {
if (try_parse(pn, out Columns c))
Remove(c);
}
}
}
}
}
}
@@ -48,7 +48,7 @@
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding status.value}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
<Condition Binding="{Binding status}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={uc:Nested uc:BTSColors.Orders}}"/>
@@ -62,12 +62,17 @@
</ResourceDictionary>
</UserControl.Resources>
<AdornerDecorator>
<c:DIYGrid
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<c:DIYGrid
x:Name="_grid"
Grid.Row="2"
Grid.Row="0"
Columns="{Binding columns}"
CurrentRow="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Rows="{Binding rows_view}"
Rows="{Binding rows}"
RowStyle="{StaticResource {x:Static sp:OrderBookView.RowStyleKey}}"
RowTemplate="{StaticResource {x:Static sp:OrderBookView.RowTemplateKey}}"
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
@@ -148,5 +153,16 @@
/>
</c:DIYGrid.CommandBindings>
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<TextBlock>
<Run Text="Orders:"/>
<Run Text="{Binding rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding rows.total_row_count, Mode=OneWay}" />
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -5,7 +5,7 @@ namespace mbts.spark {
internal partial class OrderBookView : UserControl {
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey }
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
public const string
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
@@ -14,8 +14,6 @@ namespace mbts.spark {
CVRowStyleKey = "CVRowStyle",
CVRowTemplateKey = "CVRowTemplateKey";
public const double RowHeight = 17;
public OrderBookView(OrderBookVM vm) {
InitializeComponent();
DataContext = vm;
@@ -23,6 +21,6 @@ namespace mbts.spark {
}
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
public OrderBookRowView() : base(OrderBookView.RowHeight) { }
public OrderBookRowView() : base() { }
}
}
@@ -2,18 +2,27 @@
using bts.api.orderman;
using bts.shell;
using bts.spark;
using mbts.persist;
using mbts.ui;
using mbts.ui.converters;
using mbts.ui.spark;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
//using IOrderBookWSM = mbts.persist.IWindowSettingsManager<mbts.spark.GridSettingsWithNotifications<mbts.spark.OrderRecordTuple, mbts.OrderRecordTuplePredicate>>;
using OrderBookGridSettings = bts.shell.OrderBookWindowFactory.OrderBookGridSettings;
using OrderBookGridSettingsVM = mbts.spark.GridSettingsVM<mbts.spark.OrderRecordTuple, mbts.OrderRecordTuplePredicate, bts.shell.OrderBookWindowFactory.OrderBookGridSettings>;
namespace mbts.spark {
internal class OrderBookWindow : SparkWindow<OrderBookWindowFactory.OrderBookStartupData>, ISharedWindow {
const string _window_type = "OrderBook", ORDERBOOK_THREAD_ID = "OrderBook";
const string _window_type = "OrderBook", ORDERBOOK_THREAD_ID = "OrderBook", _default_title = "Order Book";
readonly INotificationService _notification_service;
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
OrderBookWindowFactory.OrderBookStartupData _state;
OrdersModel _model;
OrderBookVM _vm;
@@ -32,12 +41,14 @@ namespace mbts.spark {
, _toggle_expand_all_command
, _show_grid_settings_command;
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service, OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon)
: base("Order Book", bus, factory, SparkCommands.ShowOrders.icon) {
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service,
OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon, IWindowSettingsManager<OrderBookGridSettings> settings_manager)
: base(_default_title, bus, factory, SparkCommands.ShowOrders.icon) {
_model = model;
_factory = factory;
_notification_service = notification_service;
_state = state;
_settings_manager = settings_manager;
}
public override OrderBookWindowFactory.OrderBookStartupData SaveState => _state = _vm.create();
@@ -45,8 +56,11 @@ namespace mbts.spark {
public override string window_type => _window_type;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public override double MinWidth => 1577;
public override double MinWidth =>
OrderBookWindowFactory.OrderBookStartupData.DefaultGrid.general.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public string thread_id => ORDERBOOK_THREAD_ID;
public override void Dispose() {
@@ -55,7 +69,7 @@ namespace mbts.spark {
}
public void thread_init() {
_vm = new OrderBookVM(this.observe_title(), _bus, _notification_service, _model);
_vm = new OrderBookVM(this.observe_title(), _bus, _notification_service, _model, _settings_manager);
_vm.load_from(_state);
_view = new OrderBookView(_vm);
@@ -106,9 +120,32 @@ namespace mbts.spark {
Icons.DownArrow,
_vm.expand_all_rows_command);
_show_grid_settings_command = SparkCommands.window_settings_command(_vm.grid_settings_manager.launch_settings_editor);
_show_grid_settings_command = SparkCommands.window_settings_command(new DialogCommand(() => {
var gs_vm = new WindowSettingsDialogVM<OrderBookGridSettings, OrderBookGridSettingsVM>(
_settings_manager,
us => us.order_book_grid,
UserSettings.FileExtensions.GRID,
s => {
if (s.general is null)
s = OrderBookWindowFactory.OrderBookStartupData.DefaultGrid;
return _vm.grid_settings_manager.create_vm(
// we need to get the most up to date GridSettings when launching b/c users may
// have changed some settings on the grid directly, like column widths.
s.general.with(_vm.core_columns.Select(c => c.create())),
this.observe_title()
);
}
);
// TODO: bind to main window's Title
var dialog = new SimpleGridSettingsDialog { DataContext = gs_vm };
dialog.SetBinding(Window.TitleProperty, new Binding($"{nameof(gs_vm.instance_vm)}.{nameof(OrderBookGridSettingsVM.owner_title)}") {
Converter = ValueConverter.create<string, string>(str => (string.IsNullOrEmpty(str) ? _default_title : str) + " Settings")
});
gs_vm.RequestClose += (s, e) => WindowUtilities.force_close(dialog);
return dialog;
}));
_commands = new SparkCommand[] {
_commands = new SparkCommand[] {
_show_fills_command,
_modify_order_command,
_cancel_selected_orders_command,
@@ -130,11 +167,9 @@ namespace mbts.spark {
}
private void _vm_selected_order_changed(OrderRecordImmutable? e) {
if (e.HasValue) {
_modify_order_command.raiseCanExecuteChanged();
_cancel_selected_orders_command.raiseCanExecuteChanged();
_recreate_selected_orders_command.raiseCanExecuteChanged();
}
}
}
}
@@ -1,8 +1,9 @@
using bts.api;
using bts.api.orderman;
using bts.spark;
using Google.Protobuf.Collections;
using bts.utils;
using mbts;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
@@ -22,19 +23,23 @@ namespace bts.shell {
readonly INotificationService _notification_service;
readonly INotificationServiceDataCache _notification_service_data_cache;
readonly OrdersModel _model;
readonly UserSettingsHub _settings_hub;
public OrderBookWindowFactory(IShellModule owner, IBus bus, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, OrdersModel model) : base(owner, "SparkOrderBookWindow") {
public OrderBookWindowFactory(IShellModule owner, IBus bus, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, OrdersModel model, UserSettingsHub settings_hub) : base(owner, "SparkOrderBookWindow") {
_bus = bus;
_notification_service = notification_service;
_notification_service_data_cache = notification_service_data_cache;
_model = model;
_settings_hub = settings_hub;
_dispose = bus.subscribe(SparkTags.ShowOrderBookTag).OnDispatcher().Subscribe(show);
}
public override Task<IShellWindow> load(OrderBookStartupData state) {
var w = new OrderBookWindow(_model, this, _bus, _notification_service, state, SparkCommands.ShowOrders.icon) as IShellWindow;
return Task.FromResult(w);
var settings_manager = new WindowSettingsManager<OrderBookGridSettings>(_settings_hub, state.grid_settings_wrapper(),
us => us.order_book_grid ?? OrderBookStartupData.DefaultGrid,
(us, s) => us.with(order_book_grid: s));
var w = new OrderBookWindow(_model, this, _bus, _notification_service, state, SparkCommands.ShowOrders.icon, settings_manager.as_non_shared());
return Task.FromResult<IShellWindow>(w);
}
async void show(OrderBookStartupData startup_data) {
@@ -49,27 +54,44 @@ namespace bts.shell {
[ProtoMember(10)]
public FilterStates filters;
[ProtoMember(15)]
[Obsolete]
public GridSettings grid;
[ProtoMember(20)]
[Obsolete]
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
[ProtoMember(25)]
public bool use_defaults;
public OrderBookStartupData() { }
public OrderBookStartupData(FilterStates filters, GridSettings grid = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null) {
public OrderBookStartupData(FilterStates filters, GridSettings grid = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null, bool use_defaults = false) {
this.filters = filters;
this.use_defaults = use_defaults;
#pragma warning disable CS0612 // Type or member is obsolete
this.grid = grid;
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
if (predicate_animations != null)
_predicate_animations.AddRange(predicate_animations);
#pragma warning restore CS0612 // Type or member is obsolete
}
#pragma warning disable CS0612 // Type or member is obsolete
[Obsolete]
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
public OrderBookGridSettings grid_settings_wrapper() => use_defaults ? null : new OrderBookGridSettings(grid, predicate_animations);
#pragma warning restore CS0612 // Type or member is obsolete
public static OrderBookStartupData Default =>
new OrderBookStartupData(
new FilterStates {
statuses = new[] { OrderStatus.Working }
}, GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion));
}, _default_general_grid, use_defaults: true);
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid);
static GridSettings _default_general_grid => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.sorting_criterion);
public static GridSettings default_mini_grid => GridSettings.Default.with(DefaultColumns.mini, DefaultColumns.sorting_criterion);
static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.ToString(), DIYGrid.DefaultColumnHeaders.Security, width: DIYGrid.DefaultColumnWidths.Security);
@@ -85,14 +107,14 @@ namespace bts.shell {
public static GridColumnSettings side => new GridColumnSettings(Columns.Side.ToString(), "B/S", width: DIYGrid.DefaultColumnWidths.Side);
public static GridColumnSettings quantity => new GridColumnSettings(Columns.Size.ToString(), "Qty", width: DIYGrid.DefaultColumnWidths.Size, horizontal_alignment: HorizontalAlignment.Right);
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.ToString(), DIYGrid.DefaultColumnHeaders.Price, width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
public static GridColumnSettings filled_quantity => new GridColumnSettings(Columns.Filled.ToString(), "Filled Qty", width: DIYGrid.DefaultColumnWidths.Size, horizontal_alignment: HorizontalAlignment.Right);
public static GridColumnSettings avg_price => new GridColumnSettings(Columns.AvgFilled.ToString(), "Avg Filled", width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.Price, horizontal_alignment: HorizontalAlignment.Right);
public static GridColumnSettings filled_quantity => new GridColumnSettings(Columns.Filled.ToString(), "Filled Qty", width: DIYGrid.DefaultColumnWidths.Filled, horizontal_alignment: HorizontalAlignment.Right);
public static GridColumnSettings avg_price => new GridColumnSettings(Columns.AvgFilled.ToString(), "Avg Filled", width: DIYGrid.DefaultColumnWidths.AveragePrice, horizontal_alignment: HorizontalAlignment.Right);
public static GridColumnSettings last_price => new GridColumnSettings(Columns.LastPrice.ToString(), "Last Filled", width: DIYGrid.DefaultColumnWidths.LastPrice, horizontal_alignment: HorizontalAlignment.Right);
public static GridColumnSettings status => new GridColumnSettings(Columns.Status.ToString(), "Status", width: 80);
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: 80);
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, width: DIYGrid.DefaultColumnWidths.SecurityKey);
public static GridColumnSettings last_message => new GridColumnSettings(Columns.LastMessage.ToString(), "Last Message", width: DIYGrid.DefaultColumnWidths.LastMessage);
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, is_visible: false, width: DIYGrid.DefaultColumnWidths.SecurityKey);
public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey);
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.OrderKey);
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey);
public static IEnumerable<SortingCriteria> sorting_criterion {
get {
@@ -125,11 +147,27 @@ namespace bts.shell {
yield return parent_key;
}
}
}
private static HashSet<GridColumnSettings> _mini_mode_columns_to_hide = new HashSet<GridColumnSettings> {
time_created, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key
};
public static IEnumerable<GridColumnSettings> mini {
get {
foreach (var col_setting in all) {
if (_mini_mode_columns_to_hide.Contains(col_setting)) {
yield return col_setting.with(is_visible: false);
} else {
yield return col_setting;
}
}
}
}
}
}
[ProtoContract]
public class FilterStates {
public class FilterStates : IEquatable<FilterStates> {
[ProtoMember(1)]
public SecurityCriteria security_criteria;
[ProtoMember(2)]
@@ -170,6 +208,61 @@ namespace bts.shell {
public api.orderman.OrderSubType[] order_sub_types;
[ProtoMember(20)]
public string[] symbols;
public bool Equals(FilterStates other) =>
other != null &&
security_criteria.safe_equals(other.security_criteria) &&
order_key_regex == other.order_key_regex &&
time_created.Equals(other.time_created) &&
EqualityComparer.safe_sequence_equals(users, other.users) &&
EqualityComparer.safe_sequence_equals(accounts, other.accounts) &&
EqualityComparer.safe_sequence_equals(exchanges, other.exchanges) &&
EqualityComparer.safe_sequence_equals(tifs, other.tifs) &&
EqualityComparer.safe_sequence_equals(order_types, other.order_types) &&
EqualityComparer.safe_sequence_equals(sides, other.sides) &&
qty.safe_equals(other.qty) &&
price.safe_equals(other.price) &&
qty_filled.safe_equals(other.qty_filled) &&
avg_price.safe_equals(other.avg_price) &&
last_price.safe_equals(other.last_price) &&
EqualityComparer.safe_sequence_equals(statuses, other.statuses) &&
exchange_message_regex == other.exchange_message_regex &&
last_change.Equals(other.last_change) &&
EqualityComparer.safe_sequence_equals(order_sub_types, other.order_sub_types) &&
EqualityComparer.safe_sequence_equals(symbols, other.symbols);
}
[ProtoContract]
public class OrderBookGridSettings : IGridSettingsWithNotifications<OrderRecordTuple, OrderRecordTuplePredicate>, IEquatable<OrderBookGridSettings> {
[ProtoMember(1)]
public GridSettings general { get; set; }
[ProtoMember(2)]
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
public OrderBookGridSettings() { }
public OrderBookGridSettings(GridSettings general = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null) {
this.general = general;
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
if (predicate_animations != null)
_predicate_animations.AddRange(predicate_animations);
}
public IReadOnlyList<OrderRecordTuplePredicateAnimations> predicate_animations => _predicate_animations ?? (_predicate_animations = new List<OrderRecordTuplePredicateAnimations>());
IEnumerable<IPredicateAnimationsData<OrderRecordTuple, OrderRecordTuplePredicate>> IGridSettingsWithNotifications<OrderRecordTuple, OrderRecordTuplePredicate>.predicate_animations => predicate_animations;
public OrderBookGridSettings with(GridSettings general) =>
new OrderBookGridSettings(general ?? this.general, predicate_animations);
public bool Equals(OrderBookGridSettings other) =>
other != null &&
EqualityComparer.null_check_equals(general, other.general) &&
EqualityComparer.safe_sequence_equals(predicate_animations, other.predicate_animations);
public override bool Equals(object obj) => Equals(obj as OrderBookGridSettings);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(
EqualityComparer.safe_get_hash_code(general),
EqualityComparer.safe_aggregate_hash(predicate_animations));
}
}
}
}
@@ -1,30 +1,38 @@
using bts.api;
using bts.api.orderman;
using bts.spark;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.spark;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using static mbts.spark.PAndLView;
using static mbts.ui.controls.DIYGrid;
namespace bts.shell {
internal class PAndLWindowFactory : ShellWindowFactory<PAndLWindowFactory.PAndLStartupData> {
readonly IBus _bus;
readonly PAndLModel _model;
public PAndLWindowFactory(IShellModule owner, IBus bus, PAndLModel model) : base(owner, "SparkPAndL") {
readonly UserSettingsHub _settings_hub;
public PAndLWindowFactory(IShellModule owner, IBus bus, UserSettingsHub settings_hub, PAndLModel model) : base(owner, "SparkPAndL") {
_bus = bus;
_model = model;
_settings_hub = settings_hub;
_dispose = bus.subscribe(SparkTags.ShowPAndLTag).OnDispatcher().Subscribe(show);
}
public static string col_id(Columns col) => col.ToString();
public override Task<IShellWindow> load(PAndLStartupData state) {
var w = new PAndLWindow(_model, this, _bus, state, SparkCommands.ShowPAndL.icon) as IShellWindow;
return Task.FromResult(w);
return Task.FromResult(_create(state));
}
async void show(PAndLStartupData startup_data) {
@@ -32,6 +40,14 @@ namespace bts.shell {
_bus.post(SparkTags.ShowWindow, w);
}
IShellWindow _create(PAndLStartupData state) {
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
state.grid,
get: us => us.pnl_grid,
set: (us, gs) => us.with(provided_pnl_gridsettings: gs));
return new PAndLWindow(_model, this, _bus, state, SparkCommands.ShowPAndL.icon, settings_manager);
}
[ProtoContract]
public class PAndLStartupData {
@@ -40,21 +56,75 @@ namespace bts.shell {
/// </summary>
public PAndLStartupData() { }
public PAndLStartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, pnl_calc_type calc_type) {
this.sort_criteria = sorting_criteria;
public PAndLStartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, pnl_calc_type calc_type, GridSettings grid = default) {
_sort_criteria = sorting_criteria;
this.filters = filters;
this.calc_type = calc_type;
this.grid = grid;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public IReadOnlyList<SortingCriteria> sorting_criterion => _sort_criteria ?? (_sort_criteria = DefaultColumns.default_sorting_criterion.ToList());
public static PAndLStartupData Default() =>
new PAndLStartupData(null, new FilterStates(), pnl_calc_type.midpoint);
[ProtoMember(5)]
public readonly List<SortingCriteria> sort_criteria;
List<SortingCriteria> _sort_criteria;
[ProtoMember(10)]
public FilterStates filters;
[ProtoMember(15)]
public pnl_calc_type calc_type;
//[ProtoMember(20)]
// ProtoMember 20 cannot be used further
[ProtoMember(25)]
public GridSettings grid;
static class DefaultColumns {
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange);
public static GridColumnSettings symbol => new GridColumnSettings(col_id(Columns.Symbol), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol);
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings security_type => new GridColumnSettings(col_id(Columns.SecurityType), DefaultColumnHeaders.SecurityType, width: DefaultColumnWidths.SecurityType);
public static GridColumnSettings start_net_position => new GridColumnSettings(col_id(Columns.StartNetPos), DefaultColumnHeaders.StartNetPos, width: DefaultColumnWidths.Start_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings day_long_pnl => new GridColumnSettings(col_id(Columns.DayLong), DefaultColumnHeaders.DayLong, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings day_short_pnl => new GridColumnSettings(col_id(Columns.DayShort), DefaultColumnHeaders.DayShort, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings day_pnl => new GridColumnSettings(col_id(Columns.DayPNL), DefaultColumnHeaders.DayPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, width: DefaultColumnWidths.Total_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings pnl_price => new GridColumnSettings(col_id(Columns.PNLPrice), DefaultColumnHeaders.PnLPrice, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(account.id, true);
yield return new SortingCriteria(exchange.id, true);
yield return new SortingCriteria(symbol.id, true);
yield return new SortingCriteria(security.id, true);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return account;
yield return exchange;
yield return symbol;
yield return security;
yield return security_type;
yield return start_net_position;
yield return start_pnl;
yield return day_long_pnl;
yield return day_short_pnl;
yield return day_pnl;
yield return total_net_pos;
yield return total_pnl;
yield return pnl_price;
}
}
}
}
[ProtoContract]
@@ -1,4 +1,5 @@
using bts.api;
using bts.api.md.auxiliary;
using bts.spark;
using mbts.persist;
using mbts.spark;
@@ -19,11 +20,12 @@ namespace bts.shell {
PAndLWindowFactory _plwf;
[ImportingConstructor]
public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache, UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model) {
_lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model);
_obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model);
_fwf = new FillsWindowFactory(this, bus, orders_model);
_plwf = new PAndLWindowFactory(this, bus, pandl_model);
public TradingShellModule(IBus bus, IWindowManager wm, INotificationService notification_service, INotificationServiceDataCache notification_service_data_cache,
UserSettingsHub settings_hub, OrdersModel orders_model, PAndLModel pandl_model, MarketDataAuxiliaryModel md_auxiliary_model) {
_lwf = new LadderWindowFactory(this, bus, wm, settings_hub, orders_model, md_auxiliary_model);
_obwf = new OrderBookWindowFactory(this, bus, notification_service, notification_service_data_cache, orders_model, settings_hub);
_fwf = new FillsWindowFactory(this, bus, settings_hub, orders_model);
_plwf = new PAndLWindowFactory(this, bus, settings_hub, pandl_model);
}
public ImageSource icon => Icons.Trades;
+15 -9
View File
@@ -53,18 +53,22 @@ namespace mbts.ui {
ToastNotificationController _toast_controller;
SoundNotificationPlayer _sound_player;
Dispatcher _dispatcher;
bool _toast_notifications_enabled, _sound_notifications_enabled = NotificationSettings.Default.sound_notification.enabled;
bool
_sound_notifications_enabled = Configuration.get<bts.config.toggles.SoundNotifications>().enabled,
_toast_notifications_enabled = Configuration.get<bts.config.toggles.ToastNotifications>().enabled;
[ImportingConstructor]
public Notifier(IBus bus, UserSettingsHub settings_hub) {
_disposer = new ThreadSafeDisposable(dispose_core);
Thread thread = new Thread(start) {
IsBackground = true,
Priority = ThreadPriority.BelowNormal,
Name = "BTSNotifications"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
if (_sound_notifications_enabled || _toast_notifications_enabled) {
Thread thread = new Thread(start) {
IsBackground = true,
Priority = ThreadPriority.BelowNormal,
Name = "BTSNotifications"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
void start() {
try {
@@ -102,7 +106,9 @@ namespace mbts.ui {
(_dispatcher = Dispatcher.CurrentDispatcher).UnhandledException += _on_dispatcher_UnhandledException;
Dispatcher.Run();
}
} catch (Exception ex) {
}
catch (NAudio.MmException) { }
catch (Exception ex) {
Log.assert(false, $"Exception in {Thread.CurrentThread.Name} thread: {ex}");
_disposer.Dispose();
}
@@ -24,7 +24,10 @@ namespace mbts.ui.spark {
public DataTemplate LeanOrderTemplate { get; set; }
public DataTemplate JoinOrderTemplate { get; set; }
public DataTemplate TopOrderTemplate { get; set; }
public DataTemplate CoverOrderTemplate { get; set; }
public DataTemplate CoverOrderTemplate { get; set; }
public DataTemplate OCOOrderTemplate { get; set; }
public DataTemplate PricerOrderTemplate { get; set; }
public DataTemplate OffsetOrderTemplate { get; set; }
public DataTemplate TriggerOrderTemplate { get; set; }
public DataTemplate IcebergOrderTemplate { get; set; }
public DataTemplate SpreaderTemplate { get; set; }
@@ -42,6 +45,9 @@ namespace mbts.ui.spark {
case CustomOrderDefinition.CustomOneofCase.Join: return JoinOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Top: return TopOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Cover: return CoverOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Oco : return OCOOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Pricer: return PricerOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Offset: return OffsetOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Trigger: return TriggerOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Iceberg: return IcebergOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Spreader: return SpreaderTemplate;
@@ -51,4 +57,5 @@ namespace mbts.ui.spark {
return null;
}
}
}
@@ -1,6 +1,7 @@
using bts;
using bts.api;
using bts.api.orderman;
using bts.utils;
using mbts.ui;
using mbts.ui.shell;
using System;
@@ -11,6 +12,60 @@ using System.Linq;
using System.Reactive.Linq;
namespace mbts.spark {
struct OrderTemplateKey : IEquatable<OrderTemplateKey> {
public readonly CustomOrderDefinition.CustomOneofCase type;
public readonly string name;
public OrderTemplateKey(CustomOrderDefinition.CustomOneofCase type)
: this(type, OrderUtils.order_type_display_name(type)) { }
public OrderTemplateKey(CustomOrderDefinition.CustomOneofCase type, string name) {
this.type = type;
this.name = name;
}
public bool Equals(OrderTemplateKey other) =>
type == other.type &&
name == other.name;
public override bool Equals(object obj) =>
obj is OrderTemplateKey other && Equals(other);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(
type.GetHashCode(),
name == null ? 0 : name.GetHashCode());
public override string ToString() => $"{name}:{type}";
public static bool operator ==(OrderTemplateKey a, OrderTemplateKey b) => a.Equals(b);
public static bool operator !=(OrderTemplateKey a, OrderTemplateKey b) => !a.Equals(b);
}
struct OrderTemplateHeader : IEquatable<OrderTemplateHeader> {
public readonly OrderTemplateKey key;
// Note: display_name is NOT part of EQUALITY
public readonly string display_name;
public OrderTemplateHeader(CustomOrderDefinition.CustomOneofCase type)
: this(new OrderTemplateKey(type), OrderUtils.order_type_display_name(type)) { }
public OrderTemplateHeader(OrderTemplateKey key, string display_name) {
this.key = key;
this.display_name = display_name;
}
public bool Equals(OrderTemplateHeader other) =>
key.Equals(other.key);
public override bool Equals(object obj) =>
obj is OrderTemplateHeader other && Equals(other);
public override int GetHashCode() => key.GetHashCode();
public override string ToString() => $"{display_name} ({key})";
}
class InlineOrderProperties {
public readonly InlineUserProperty<AccountTuple> account;
public readonly InlineUserProperty<TimeInForce> time_in_force;
@@ -39,7 +94,7 @@ namespace mbts.spark {
class OrderPropertiesEditorVM : DisposableViewModelBase {
readonly OrdersModel _model;
readonly Property<SecurityImmutable> _security;
readonly SelectorProperty<AccountTuple> _accounts;
readonly SelectorProperty<AccountTuple, AccountTuple?> _accounts;
readonly SelectorProperty<TimeInForce> _time_in_force;
readonly SelectorProperty<IOrderTypeVM> _order_type;
IDisposable _order_type_param_errors_sub;
@@ -58,14 +113,21 @@ namespace mbts.spark {
nameof(selected_account),
model.factory.subscribe_account_tuples(),
order_properties.Select(ops => ops.account),
() => accounts?.FirstOrDefault() ?? default,
val => (!string.IsNullOrWhiteSpace(val.name) && accounts.Contains(val), "Please select an account"));
a => new AccountTuple?(a),
() => {
foreach (var a in accounts) {
return a;
}
return new AccountTuple?();
},
EqualityComparer<AccountTuple?>.Default,
new[] { new PropertyValidation<AccountTuple?>(a => (a.HasValue && !string.IsNullOrWhiteSpace(a.Value.name) && accounts.Contains(a.Value), "Please select an account")) }.ToReadOnlyList());
_order_type = backing(
nameof(selected_order_type),
security_source.Select(s => model.factory.default_order_type_defs(s).Select(create_order_type_vm)),
security_source.Select(s => model.factory.default_order_type_templates(s).Select(kv => create_order_type_vm(kv.Value, kv.Key.key.name, kv.Key.display_name))),
order_properties.Select(get_order_type_vm_prop),
get_default_order_type_vm,
(a, b) => a.order_type == b.order_type);
EqualityComparer.null_check_wrapper(OrderTypeVMExtensions.key_comparer));
_order_type.Changed += on_selected_order_type_changed;
_time_in_force = backing(
nameof(selected_time_in_force),
@@ -77,19 +139,22 @@ namespace mbts.spark {
tif => time_in_forces.Contains(tif));
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
// local methods
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) {
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable? cd, string id, string display_name) {
IOrderTypeVM vm = null;
if(cd.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic)
vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source, cd.Dynamic.DynamicOrderType);
if(cd?.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic)
vm = OrderTypeVMFactory.create(cd.Value.CustomCase, id, display_name, model, security_md_source, cd.Value.Dynamic.DynamicOrderType);
else
vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source);
vm.load_from(cd);
vm = OrderTypeVMFactory.create(cd.Value.CustomCase, id, display_name, model, security_md_source);
vm.load_from(cd.Value);
return vm;
}
InlineUserProperty<IOrderTypeVM> get_order_type_vm_prop(InlineOrderProperties p) {
if (!p.order_type_def.has_value)
return new InlineUserProperty<IOrderTypeVM>();
return new InlineUserProperty<IOrderTypeVM>(create_order_type_vm(p.order_type_def.value), p.order_type_def.force_override);
return new InlineUserProperty<IOrderTypeVM>(
create_order_type_vm(p.order_type_def.value, p.order_type_def.value.CustomCase.ToString(),
OrderUtils.order_type_display_name(p.order_type_def.value.CustomCase)),
p.order_type_def.force_override);
}
IOrderTypeVM get_default_order_type_vm() {
var ot = model.factory.default_order_type(security);
@@ -114,13 +179,13 @@ namespace mbts.spark {
public SecurityImmutable security => _security.Value;
public ReadOnlyObservableCollection<AccountTuple> accounts => _accounts.items;
public AccountTuple selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; }
public AccountTuple? selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; }
public ReadOnlyObservableCollection<TimeInForce> time_in_forces => _time_in_force.items;
public TimeInForce selected_time_in_force { get => _time_in_force.selected_item; set => _time_in_force.selected_item = value; }
public ReadOnlyObservableCollection<IOrderTypeVM> order_types => _order_type.items;
public IOrderTypeVM selected_order_type { get => _order_type.selected_item; set => _order_type.selected_item = value; }
public IObservable<AccountTuple> observe_account(bool start_immediately = false) =>
public IObservable<AccountTuple?> observe_account(bool start_immediately = false) =>
_accounts.observe_current(start_immediately);
public IObservable<TimeInForce> observe_time_in_force(bool start_immediately = false) =>
@@ -42,5 +42,6 @@ namespace mbts.spark {
public override bool Equals(object obj) => Equals(obj as OrderRecordTuple);
public override int GetHashCode() => key.GetHashCode();
public override string ToString() => $"order:{order.Key}, sec:{security_md.display_name()}";
}
}
@@ -7,82 +7,99 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
namespace mbts.spark {
class OrderRecordTupleSource : IObservable<IIncrementalUpdate<OrderRecordTuple, uint>>, IDisposable {
// returns a ref-count Observable.Create-wrapper that initializes/destroys the [hot-observable] OrderRecordTupleSource.
// (initializes when the first subscription is made, and destroys when the last subscription is disposed).
public static IObservable<IIncrementalUpdate<OrderRecordTuple, uint>> create_observable(
IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities,
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders) {
OrderRecordTupleSource source = null;
int ref_count = 0;
return Observable.Create<IIncrementalUpdate<OrderRecordTuple, uint>>(observer => {
if (Interlocked.Increment(ref ref_count) == 1) {
SparkUtils.lazy_assert(source == null, () => fmt_assert_msg("source should be null, there must be a leak."));
source = new OrderRecordTupleSource(securities, orders);
}
IDisposable sub = source.Subscribe(observer);
return Disposable.Create(() => {
Disposable.dispose_once(ref sub);
if (Interlocked.Decrement(ref ref_count) == 0)
Disposable.dispose_once(ref source);
});
});
string fmt_assert_msg(string msg) => $"{nameof(OrderRecordTupleSource)}.{nameof(create_observable)}: {msg}";
}
class OrderRecordTupleSource : IDisposable {
readonly object _lock = new object();
// subjects
readonly Subject<IIncrementalUpdate<OrderRecordTuple, uint>> _subject = new Subject<IIncrementalUpdate<OrderRecordTuple, uint>>();
readonly Subject<IIncrementalTableUpdateTransform<OrderRecordTuple, uint>> _table_subject = new Subject<IIncrementalTableUpdateTransform<OrderRecordTuple, uint>>();
readonly Subject<IIncrementalUpdate<SecurityMD, uint>> _order_securities_subject = new Subject<IIncrementalUpdate<SecurityMD, uint>>();
// data cache
readonly Dictionary<uint, OrderRecordTuple> _state = new Dictionary<uint, OrderRecordTuple>();
readonly SetMap<uint, uint> _security_orders = new SetMap<uint, uint>();
readonly IDictionary<uint, SecurityMD> _security_md_cache = new Dictionary<uint, SecurityMD>(0);
IReadOnlyDictionary<uint, OrderRecordImmutable> _orders_cache = new Dictionary<uint, OrderRecordImmutable>(0);
IReadOnlyDictionary<uint, SecurityMD> _security_md_cache = new Dictionary<uint, SecurityMD>(0);
IDisposable _dispose;
object _lock = new object();
OrderRecordTupleSource(IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities, IObservable<TableUpdate<OrderRecordImmutable, uint>> orders) =>
public OrderRecordTupleSource(IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities, IObservable<TableUpdate<OrderRecordImmutable, uint>> orders) =>
_dispose = Disposable.Create(
securities.Subscribe(_update_securities),
orders.AddRemoveOnly().Subscribe(_update_orders));
orders.Subscribe(_update_orders));
public IDisposable Subscribe(IObserver<IIncrementalUpdate<OrderRecordTuple, uint>> observer) {
lock (_lock) {
observer.OnNext(new IncrementalUpdate<OrderRecordTuple, uint>(new Dictionary<uint, OrderRecordTuple>(_state), new Dictionary<uint, OrderRecordTuple>(0)));
return _subject.Subscribe(observer);
}
}
public IObservable<IIncrementalUpdate<OrderRecordTuple, uint>> subscribe_incremental_update() =>
Observable.Create<IIncrementalUpdate<OrderRecordTuple, uint>>(observer => {
lock (_lock) {
observer.OnNext(new IncrementalUpdate<OrderRecordTuple, uint>(new Dictionary<uint, OrderRecordTuple>(_state), new Dictionary<uint, OrderRecordTuple>(0)));
return _subject.Subscribe(observer);
}
});
public IObservable<IIncrementalTableUpdateTransform<OrderRecordTuple, uint>> subscribe_incremental_table_update() =>
Observable.Create<IIncrementalTableUpdateTransform<OrderRecordTuple, uint>>(observer => {
lock (_lock) {
observer.OnNext(IncrementalTableUpdateTranform<OrderRecordTuple, uint>.init(_state.Values));
return _table_subject.Subscribe(observer);
}
});
public IObservable<IIncrementalUpdate<SecurityMD, uint>> subscribe_order_securities() =>
Observable.Create<IIncrementalUpdate<SecurityMD, uint>>(observer => {
lock (_lock) {
var init = new Dictionary<uint, SecurityMD>();
foreach (uint sec_key in _security_orders.Keys) {
if (_security_md_cache.TryGetValue(sec_key, out SecurityMD smd))
init.Add(sec_key, smd);
}
observer.OnNext(new IncrementalUpdate<SecurityMD, uint>(init, new Dictionary<uint, SecurityMD>(0)));
return _order_securities_subject.Subscribe(observer);
}
});
public void Dispose() => Disposable.dispose(ref _dispose);
void _update_orders(LegacyTableUpdate<OrderRecordImmutable, uint> orders) {
void _update_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
lock (_lock) {
Dictionary<uint, OrderRecordTuple> removed = new Dictionary<uint, OrderRecordTuple>(), added = new Dictionary<uint, OrderRecordTuple>();
Dictionary<uint, SecurityMD> added_securities = new Dictionary<uint, SecurityMD>(), removed_securities = new Dictionary<uint, SecurityMD>();
foreach (OrderRecordImmutable order in orders.removed) {
uint sec_key = order.security_key();
OrderRecordTuple old_ft = new OrderRecordTuple(order, _get_security_md(sec_key));
removed.Add(old_ft.order.Key, old_ft);
_security_orders.remove(sec_key, order.Key);
if (_security_orders.removes_key(sec_key, order.Key) && old_ft.security_md.HasValue)
removed_securities.Add(sec_key, old_ft.security_md.Value);
}
foreach (Change<OrderRecordImmutable> ch in orders.changes) {
// orders cannot change securities; ch.New can use the same security_md as ch.Old
var sec_md = _get_security_md(ch.Old.security_key());
removed.Add(ch.Old.Key, new OrderRecordTuple(ch.Old, sec_md));
added.Add(ch.New.Key, new OrderRecordTuple(ch.New, sec_md));
}
foreach (OrderRecordImmutable order in orders.added) {
uint sec_key = order.security_key();
OrderRecordTuple new_ort = new OrderRecordTuple(order, _get_security_md(sec_key));
added.Add(new_ort.order.Key, new_ort);
_security_orders.add(sec_key, order.Key);
if (_security_orders.adds_key(sec_key, order.Key) && new_ort.security_md.HasValue)
added_securities.Add(sec_key, new_ort.security_md.Value);
}
_orders_cache = orders.state;
_apply_changes(added, removed);
_apply_changes(added, removed, added_securities, removed_securities);
}
}
void _update_securities(IReadOnlyDictionary<uint, SecurityMD> securities) {
lock (_lock) {
Dictionary<uint, OrderRecordTuple> added = new Dictionary<uint, OrderRecordTuple>(), removed = new Dictionary<uint, OrderRecordTuple>();
Dictionary<uint, SecurityMD> added_securities = new Dictionary<uint, SecurityMD>(), removed_securities = new Dictionary<uint, SecurityMD>();
_security_md_cache.Keys.difference(securities.Keys, out IEnumerable<uint> added_keys, out IEnumerable<uint> removed_keys);
foreach (var k in removed_keys) {
/// Warning: <see cref="_security_md_cache"/> must NOT be reassigned with <see cref="securities"/>; the latter is always the same object.
/// This would mean that the <see cref="GlobalExtensions.difference{T}(IEnumerable{T}, IEnumerable{T}, out IEnumerable{T}, out IEnumerable{T})"/>
/// call above would literally be calculating the difference between the keys of the same dictionary!
foreach (var k in removed_keys.ToList()) {
if (_security_orders.try_lookup(k, out ISet<uint> order_keys)) {
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
SecurityMD old_sec_md = _security_md_cache[k];
removed_securities.Add(k, old_sec_md);
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
foreach (OrderRecordImmutable order in orders) {
OrderRecordTuple old_ft = new OrderRecordTuple(order, old_sec_md);
removed.Add(old_ft.order.Key, old_ft);
@@ -90,11 +107,13 @@ namespace mbts.spark {
added.Add(new_ft.order.Key, new_ft);
}
}
_security_md_cache.Remove(k);
}
foreach (var k in added_keys) {
foreach (var k in added_keys.ToList()) {
SecurityMD new_sec_md = securities[k];
if (_security_orders.try_lookup(k, out ISet<uint> order_keys)) {
added_securities.Add(k, new_sec_md);
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
SecurityMD new_sec_md = securities[k];
foreach (OrderRecordImmutable order in orders) {
OrderRecordTuple old_ft = new OrderRecordTuple(order, null);
removed.Add(old_ft.order.Key, old_ft);
@@ -102,9 +121,9 @@ namespace mbts.spark {
added.Add(new_ft.order.Key, new_ft);
}
}
_security_md_cache.Add(k, new_sec_md);
}
_security_md_cache = securities;
_apply_changes(added, removed);
_apply_changes(added, removed, added_securities, removed_securities);
}
}
@@ -114,12 +133,15 @@ namespace mbts.spark {
OrderRecordImmutable? _get_order(uint key) =>
_orders_cache.TryGetValue(key, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?();
void _apply_changes(Dictionary<uint, OrderRecordTuple> added, Dictionary<uint, OrderRecordTuple> removed) {
void _apply_changes(Dictionary<uint, OrderRecordTuple> added, Dictionary<uint, OrderRecordTuple> removed, Dictionary<uint, SecurityMD> added_securities, Dictionary<uint, SecurityMD> removed_securities) {
foreach (var kv in removed)
_state.Remove(kv.Key);
foreach (var kv in added)
_state.Add(kv.Key, kv.Value);
_subject.OnNext(new IncrementalUpdate<OrderRecordTuple, uint>(added, removed));
var update = new IncrementalUpdate<OrderRecordTuple, uint>(added, removed);
_subject.OnNext(update);
_table_subject.OnNext(update.as_table());
_order_securities_subject.OnNext(new IncrementalUpdate<SecurityMD, uint>(added_securities, removed_securities));
}
}
}
@@ -17,22 +17,22 @@ namespace mbts.spark
{
class InlineCoverOrderProperties
{
public InlineCoverOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
public InlineCoverOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
var hedgetype = CustomOneofCase.Limit;
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
hedge_ticks = new InlineUserProperty<int>(1, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> hedge_order_types;
public readonly InlineUserProperty<int> hedge_ticks;
@@ -42,25 +42,25 @@ namespace mbts.spark
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineCoverOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
IOrderTypeVM _hedge_order;
IDisposable _hedge_order_errors_sub;
public int hedge_ticks { get; set; } = 1;
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineCoverOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
new InlineCoverOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name)
{ }
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineCoverOrderProperties> inline_properties) : base(model)
IObservable<InlineCoverOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
@@ -75,11 +75,11 @@ namespace mbts.spark
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
{
_select_hedge_order(p.selected_hedge_order_type.value);
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
}
if (p.hedge_ticks.force_override)
{
@@ -116,7 +116,7 @@ namespace mbts.spark
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _child_order_choices;
}
}
@@ -126,7 +126,7 @@ namespace mbts.spark
get
{
if (_hedge_order_choices == null)
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _hedge_order_choices;
}
}
@@ -200,7 +200,7 @@ namespace mbts.spark
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -216,7 +216,7 @@ namespace mbts.spark
void _select_hedge_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
_inline_props.selected_hedge_order_type.value :
_inline_props.selected_hedge_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_hedge_order(ot);
}
@@ -0,0 +1,54 @@
using bts.api;
using bts.api.orderman;
using mbts.ui;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark
{
[Export]
public class DynamicOrderHelper
{
readonly RegisteredDynamicOrderTypes _dynamic_types;
[ImportingConstructor]
private DynamicOrderHelper(OrderService order_service)
{
_dynamic_types = order_service.get_registered_dynamic_order_types(new VoidMessage());
}
public void add_dynamic_order_defs(List<CustomOrderDefinitionImmutable> defs,
Func<CustomOrderDefinition.CustomOneofCase, CustomOrderDefinition> factory)
{
foreach (var d in _dynamic_types.DynamicOrderDesc)
{
var dorder = factory(CustomOrderDefinition.CustomOneofCase.Dynamic);
dorder.Dynamic.DynamicOrderType = d.DynamicOrderType;
//Add only the descriptor here for reflection later
foreach(var p in d.ParamDesc)
dorder.Dynamic.OrderParams.Add( p.ParamName, new DynamicOrderParam() );
defs.Add(new CustomOrderDefinitionImmutable(dorder));
}
}
internal IDictionary<string, DynamicOrderParamVM> get_parameters(string dyntype)
{
Dictionary<string, DynamicOrderParamVM> par = new Dictionary<string, DynamicOrderParamVM>();
var order = _dynamic_types.DynamicOrderDesc.FirstOrDefault(x => x.DynamicOrderType == dyntype);
if (order != null)
{
foreach (var p in order.ParamDesc)
par[p.ParamName] = new DynamicOrderParamVM() { param = new DynamicOrderParam(), descriptor = p };
}
return par;
}
}
}
Loaded 100 of 815 files, more files were not shown because too many files have changed in this diff. Show more