Compare commits

...
440 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
Eric Harding 112a5703e0 SPARK-1906 Stream Auth : C++ Server 2018-12-03 17:35:01 -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
Eric Harding 86199755f5 SPARK-1906 Stream Auth : C# Client 2018-11-30 17:05:37 -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 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
Pedro Pinto d3043e9a19 Internalized ExecutionGateway inside OrderManager. 2018-11-30 01:55:27 +00:00
Eric Harding c6b494db16 Move AuthToken type to apicore 2018-11-29 16:34:33 -05: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
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
Pedro Pinto a5c0c95e4e Reduced allocations when creating a new order. 2018-11-28 18:32:15 +00:00
Pedro Pinto 308c5e5579 Added some experimental stuff. 2018-11-28 14:21:50 +00: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
454 changed files with 13355 additions and 6781 deletions

No files matched your search

+2
View File
@@ -28,3 +28,5 @@ src/server/sparkd/orderman/perf_analysis/trace_data/
*.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",
+47 -7
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,8 +36,8 @@
"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,
@@ -48,7 +48,7 @@
"type": "clr",
"request": "launch",
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
"args": ["${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64.xll"],
"args": ["${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
@@ -75,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",
@@ -108,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",
@@ -209,7 +249,7 @@
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test","Co2Tests"],
"args": ["test","AuthToken"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
+4 -1
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)
@@ -57,6 +57,9 @@ else ()
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)
+39 -28
View File
@@ -1,11 +1,10 @@
# The BTS Development Process #
## The Big Picture ##
Our development work is organized in projects. A project is an effort
to specifiy, 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 ##
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
@@ -14,16 +13,16 @@ are merged into the product and deployed to customers.
## The Details ##
Typically project ideas come from sales interactions, opposition
research and customer feedback, alltough on occasion development or
operational requirements will also suggest potentially valuable
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
and merge. We detail each of these phases below.
will go through several phases: inception, specification, development,
validation and merge.
* Inception. In this phase we create the infrastructure for the
project. Typically this means selecting a team, creating a project
* 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.
@@ -32,11 +31,11 @@ phase is mostly mechanical and the responsibility of Pedro.
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 our ability to
quickly deliver value to customers than good specification
work. Specification typically take the form of shared google docs,
using a mix of text and UI mockups to capture both user requirements
and suggested implementation strategies. Taha is responsible for this
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
@@ -44,20 +43,35 @@ 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 sub-optimal
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
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 concerns as well as specification
trade off concerns.
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 "Ready To Merge" statement. This marks the project as ready
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
@@ -66,6 +80,3 @@ 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.
## Tools ##
* Pre-Merge checklist
* Code Review tutorial
+90
View File
@@ -1,4 +1,94 @@
# 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
+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"
}
}
}
+1
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]
+10 -48
View File
@@ -17,31 +17,22 @@ 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<AuthToken> _token;
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<AuthToken> token,
Lazy<SyntheticSecurityService> ss) {
public MDDaemon(Lazy<IBus> bus, Lazy<AuthTokenImmutable> token, Lazy<SyntheticSecurityService> ss) : base(get_naggle(), true) {
_bus = bus;
_token = token;
_ss = ss;
_relay = new Co2RelayControl(_consumer);
_relay.preload_securities((co2.MDRequest.Types.Flags)0xff).ignore();
_dispose.Add(_consumer.on_security.Subscribe(this));
_ss = ss;
}
public void initialize() {
override public void initialize() {
_relay.set_token(_token.Value);
_dispose.Add(
_ss.Value.subscribei_metadata()
@@ -66,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);
}
@@ -74,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));
}
}
}
}
+1 -4
View File
@@ -13,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");
+7 -2
View File
@@ -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
@@ -152,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));
@@ -35,7 +35,7 @@ namespace mbts.spark {
readonly CellProperty<string> _working_bids_text, _working_asks_text;
readonly MyOrderSummarizer _order_summarizer = new MyOrderSummarizer();
readonly MDDisplayHelper.SizeFormatter _size_formatter = MDDisplayHelper.get_formatter(MarketSizeDisplay.get_default());
uint _bid_direct_size, _bid_merged_size, _ask_direct_size, _ask_merged_size;
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;
@@ -45,28 +45,33 @@ 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);
_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));
@@ -100,15 +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_direct_size => _bid_direct_size;
public uint bid_implied_size => _bid_merged_size - _bid_direct_size;
public uint bid_merged_size => _bid_merged_size;
public string bid_merged_size_text => _bid_merged_size_text ?? (_bid_merged_size_text = _size_formatter(_bid_merged_size, bid_implied_size));
public 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_direct_size => _ask_direct_size;
public uint ask_implied_size => _ask_merged_size - _ask_direct_size;
public uint ask_merged_size => _ask_merged_size;
public string ask_merged_size_text => _ask_merged_size_text ?? (_ask_merged_size_text = _size_formatter(_ask_merged_size, ask_implied_size));
public 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;
@@ -144,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);
refresh_size(ref b, ref _bid_direct_size, ref _bid_merged_size, ref _bid_merged_size_text, nameof(bid_merged_size_text));
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);
refresh_size(ref a, ref _ask_direct_size, ref _ask_merged_size, ref _ask_merged_size_text, nameof(ask_merged_size_text));
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);
}
@@ -162,10 +167,10 @@ namespace mbts.spark {
if (dirty)
invalidated?.Invoke();
void refresh_size(ref NativePriceSize nps, ref uint direct, ref uint merged, ref string text, string text_prop) {
if (nps.size != merged || nps.direct_size != direct) {
merged = nps.size;
direct = nps.direct_size;
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);
@@ -205,36 +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.BidMergedSize:
order.Side = Side.Sell;
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_merged_size));
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_merged_size));
break;
case MarketViewView.Columns.BidDirectSize:
order.Side = Side.Sell;
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_direct_size));
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), bid_price, bid_direct_size));
break;
case MarketViewView.Columns.BidImpliedSize:
order.Side = Side.Sell;
_bus.post(SparkTags.ShowOrderTicketTag, (order, bid_price, bid_implied_size));
_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.AskMergedSize:
order.Side = Side.Buy;
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_merged_size));
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Buy), ask_price, ask_merged_size));
break;
case MarketViewView.Columns.AskDirectSize:
order.Side = Side.Sell;
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_direct_size));
_bus.post(SparkTags.ShowOrderTicketTag, (order.with(side:Side.Sell), ask_price, ask_direct_size));
break;
case MarketViewView.Columns.AskImpliedSize:
order.Side = Side.Sell;
_bus.post(SparkTags.ShowOrderTicketTag, (order, ask_price, ask_implied_size));
_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));
@@ -266,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
@@ -96,7 +96,7 @@ namespace bts.shell {
public static GridColumnSettings day_change => new GridColumnSettings(col_id(Columns.DayChange), DefaultColumnHeaders.DayChange, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings session_low => new GridColumnSettings(col_id(Columns.SessionLow), DefaultColumnHeaders.SessionLow, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings session_high => new GridColumnSettings(col_id(Columns.SessionHigh), DefaultColumnHeaders.SessionHigh, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
@@ -97,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));
}
}
}
}
}
@@ -38,7 +38,6 @@ namespace mbts.spark {
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
@@ -47,7 +46,6 @@ namespace mbts.spark {
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DefaultColumnWidths.Aggressor, nameof(Row.aggressor_side), aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DefaultColumnWidths.TradeKey, nameof(Row.trade_key), trade_key_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DefaultColumnWidths.SecurityKey, nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex);
void add_column(TimeAndSalesView.Columns col_key, string header, double width, string sort_member_path, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
columns.Add(new TimeAndSalesColumn(
@@ -66,7 +64,7 @@ namespace mbts.spark {
_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);
@@ -103,7 +101,6 @@ 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));
FilterStates _get_filter_states() =>
new FilterStates {
@@ -111,7 +108,6 @@ namespace mbts.spark {
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()
};
@@ -175,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;
}
@@ -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();
@@ -84,8 +84,7 @@ namespace bts.shell {
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings size => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings aggressor => new GridColumnSettings(col_id(Columns.Aggressor), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor);
public static GridColumnSettings trade_key => new GridColumnSettings(col_id(Columns.TradeKey), DefaultColumnHeaders.TradeKey, width: DefaultColumnWidths.TradeKey);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
public static 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 {
@@ -100,7 +99,6 @@ namespace bts.shell {
yield return price;
yield return size;
yield return aggressor;
yield return trade_key;
yield return security_key;
}
}
@@ -117,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)]
@@ -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;
}
@@ -66,7 +66,7 @@ namespace bts.shell {
public static GridColumnSettings description => new GridColumnSettings(col_id(Columns.Description), DefaultColumnHeaders.Description, width: DefaultColumnWidths.Description);
public static GridColumnSettings n_legs => new GridColumnSettings(col_id(Columns.n_legs), DefaultColumnHeaders.N_Legs, width: DefaultColumnWidths.N_Legs);
public static GridColumnSettings n_working_orders => new GridColumnSettings(col_id(Columns.n_working_orders), DefaultColumnHeaders.N_Working_Orders, width: DefaultColumnWidths.N_Working_Orders);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
public static 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 {
@@ -205,7 +205,7 @@ namespace mbts.spark {
//new EnableHedgeModel(), //TAHA_SPREADER
new HedgeRatioModel(),
//new MinLeanQtyModel(), //TAHA_SPREADER
new QuoteHiddenModel(),
//new QuoteHiddenModel(),
new MarketDataTypeModel(),
new MarketDataCalculationModel(),
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
@@ -213,7 +213,7 @@ namespace mbts.spark {
quote_throttle_outside_ticks,
new ConditionalQuoteEnabledModel(conditional_quoting_ticks, conditional_quoting_price_type),
conditional_quoting_ticks,
conditional_quoting_price_type,
//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,
@@ -294,9 +294,9 @@ 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;
}
@@ -178,7 +178,7 @@ namespace mbts.spark {
} else return model.on_fill_tuples;
}
static IConnectableObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> _get_observable(IBus bus, OrdersModel model, IObservable<StartupData> state) =>
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 {
@@ -186,7 +186,7 @@ namespace mbts.spark {
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;
@@ -86,7 +86,7 @@ namespace bts.shell {
public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind);
public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey);
public static GridColumnSettings order_key => new GridColumnSettings(col_id(Columns.OrderKey), DefaultColumnHeaders.OrderKey, width: DefaultColumnWidths.OrderKey);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, width: DefaultColumnWidths.SecurityKey);
public static 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);
@@ -61,10 +61,12 @@ namespace bts.shell {
var view = new Lazy<UIElement>(() => {
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}" : "";
@@ -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() { }
}
}
@@ -194,6 +194,10 @@ namespace mbts.spark {
// quantity
Add(qty_column = new SizeColumn(
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<OrderRecordTuple>(ort => ort.order.filled(), nameof(OrderBookRowVM.filled_quantity))));
@@ -209,6 +213,10 @@ namespace mbts.spark {
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 SimpleTextColumn<DoubleMinMaxState>(
Columns.AvgFilled,
@@ -291,6 +299,7 @@ namespace mbts.spark {
public event Action filter_changed;
public CancelColumn cancel_column { get; }
public SecurityColumn security_column { get; }
public SimpleTextColumn<string[]> symbol_column { get; }
public SimpleTextColumn<TimeFilterTuple> time_created_column { get; }
@@ -303,7 +312,9 @@ namespace mbts.spark {
public SimpleTextColumn<OrderSubType[]> sub_type_column { get; }
public SideColumn side_column { get; }
public SizeColumn qty_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 SimpleTextColumn<DoubleMinMaxState> last_price_column { get; }
public SimpleTextColumn<DoubleMinMaxState> avg_price_column { get; }
@@ -509,7 +520,7 @@ namespace mbts.spark {
: base(key, header, get_text, sort_prop, width, filter, filter_template, comparer) { }
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, RowHeight,
CellRender.draw_text(dc, _get_glyph_context(row), font_size, _get_text(row), position, row_height,
font_brush, null, horizontal_alignment, VerticalAlignment.Center, DefaultCellPadding);
}
@@ -519,7 +530,7 @@ namespace mbts.spark {
: 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, _get_glyph_context(row), font_size, row.side.ToString(), position, RowHeight,
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);
}
@@ -530,7 +541,7 @@ namespace mbts.spark {
}
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.ToString(), position, RowHeight,
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);
}
@@ -541,7 +552,7 @@ namespace mbts.spark {
}
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.ToString(), position, RowHeight,
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);
}
@@ -640,6 +651,8 @@ 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));
@@ -651,6 +664,88 @@ 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 {
@@ -14,6 +14,8 @@ 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;
@@ -6,6 +6,9 @@ using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace mbts.spark {
@@ -13,15 +16,34 @@ namespace mbts.spark {
const string _unknown_security_price_format_string = "0.#########";
const int _unknown_security_price_num_fractional_digits = 9;
readonly OrderBookVM _owner;
IRaiseCanExecuteChangedCommand _show_order_modify_cmd, _toggle_expanded_cmd;
bool _is_parent, _is_expanded;
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;
public OrderBookRowVM(OrderBookVM owner, OrderRecordTuple order_tuple) {
// for mini-ob
uint _prospective_quantity;
double _prospective_price;
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;
@@ -34,6 +56,7 @@ namespace mbts.spark {
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();
@@ -72,6 +95,29 @@ namespace mbts.spark {
}
}
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));
@@ -79,6 +125,38 @@ namespace mbts.spark {
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);
@@ -103,6 +181,14 @@ namespace mbts.spark {
// 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!)
@@ -16,6 +16,7 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
using static bts.shell.OrderBookWindowFactory;
@@ -33,44 +34,84 @@ namespace mbts.spark {
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
readonly List<IDisposable> _resources;
OrderBookStartupData _state;
bool _is_row_collection_populated;
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model, IWindowSettingsManager<OrderBookGridSettings> settings_manager) {
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))));
_row_collection = new OrderBookRowCollection(ort => new OrderBookRowVM(this, ort), _columns.get_sorting_data().comparer) { filter = _columns.get_filter() };
// 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));
_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, columns);
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),
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
grid_settings_manager,
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();
}
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 ObservableCollection<DIYGrid.ColumnBase> columns => _columns;
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 ?? _selected_rows.FirstOrDefault(); }
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 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);
}
@@ -131,6 +172,15 @@ namespace mbts.spark {
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) {
@@ -187,7 +237,8 @@ namespace mbts.spark {
return pvm;
},
(gen, anims) => new OrderBookGridSettings(gen, anims.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))),
notification_service.data_cache, col_settings_flags);
(notification_service == null) ? null : notification_service.data_cache,
col_settings_flags);
}
static OrderRecordTuplePredicate.PredicateTypeOneofCase _corresponding_predicate_type(Columns col) {
@@ -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() { }
}
}
@@ -131,7 +131,7 @@ namespace mbts.spark {
return _vm.grid_settings_manager.create_vm(
// we need to get the most up to date GridSettings when launching b/c users may
// have changed some settings on the grid directly, like column widths.
s.general.with(_vm.columns.Select(c => c.create())),
s.general.with(_vm.core_columns.Select(c => c.create())),
this.observe_title()
);
}
@@ -167,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();
}
}
}
}
@@ -90,6 +90,8 @@ namespace bts.shell {
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);
@@ -110,7 +112,7 @@ namespace bts.shell {
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: DIYGrid.DefaultColumnWidths.LastMessage);
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, width: DIYGrid.DefaultColumnWidths.SecurityKey);
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.ParentKey);
@@ -145,7 +147,23 @@ 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]
@@ -25,6 +25,8 @@ namespace mbts.ui.spark {
public DataTemplate JoinOrderTemplate { get; set; }
public DataTemplate TopOrderTemplate { 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; }
@@ -43,6 +45,8 @@ 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;
@@ -139,13 +139,13 @@ 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, string id, string display_name) {
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, id, display_name, 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, id, display_name, 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) {
@@ -126,6 +126,8 @@ namespace mbts.spark
return _child_order_choices;
}
}
public bool has_child => _order_def_tags.Any();
public IOrderTypeVM child_order
{
@@ -0,0 +1,257 @@
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.Linq;
using System.Reactive.Linq;
using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark
{
static partial class OrderTypeVMFactory
{
class InlineOCOOrderProperties
{
public InlineOCOOrderProperties(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<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
var othertype = CustomOneofCase.Limit;
this.selected_other_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(othertype), true);
this.other_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
other_ticks = new InlineUserProperty<int>(1, false);
other_side = new InlineUserProperty<bts.api.RelativeSide>(bts.api.RelativeSide.Same, false);
}
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_other_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> other_order_types;
public readonly InlineUserProperty<int> other_ticks;
public readonly InlineUserProperty<bts.api.RelativeSide> other_side;
}
class OCOOrderVM : OrderTypeVMBase<OCOOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineOCOOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _other_order_choices;
IOrderTypeVM _other_order;
IDisposable _other_order_errors_sub;
public int other_ticks { get; set; } = 1;
public bts.api.RelativeSide other_side { get; set; } = bts.api.RelativeSide.Same;
public OCOOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineOCOOrderProperties(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 OCOOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineOCOOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p =>
{
if (_order_types.Count == 0 || p.child_order_types.force_override)
{
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (_other_order_choices != null && (_other_order == null || p.selected_other_order_type.force_override))
{
_select_other_order(p.selected_other_order_type.value.key.type);
}
if (p.other_ticks.force_override)
{
other_ticks = p.other_ticks.value;
RaisePropertyChanged(nameof(other_ticks));
}
if (p.other_side.force_override)
{
other_side = p.other_side.value;
RaisePropertyChanged(nameof(other_side));
}
_inline_props = p;
_validate();
})
);
_validate();
}
public override void assign_to(CustomOrderDefinition value)
{
if (_child_order == null)
_select_child_order(model.factory.default_order_type(_security.Value, true));
if (_other_order == null )
_select_other_order(model.factory.default_order_type(_security.Value, true));
_validate();
if (!HasErrors)
{
var i = factory.oco_order();
i.OtherTicks = other_ticks;
i.OtherSide = other_side;
child_order.assign_to(i.Child);
if (other_order != null)
other_order.assign_to(i.Other);
value.Oco = i;
}
}
public IEnumerable<bts.api.RelativeSide> other_side_types => EnumHelper.get_values_except<bts.api.RelativeSide>(bts.api.RelativeSide.Undefined);
public ICollection<IOrderTypeVM> child_order_types
{
get
{
if (_child_order_choices == null)
_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;
}
}
public ICollection<IOrderTypeVM> other_order_types
{
get
{
if (_other_order_choices == null)
_other_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _other_order_choices;
}
}
public IOrderTypeVM child_order
{
get
{
init();
return _child_order;
}
set
{
Disposable.dispose(ref _child_order_errors_sub);
_child_order = value;
_child_order?.init();
if (_child_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(child_order));
}
}
public IOrderTypeVM other_order
{
get
{
init();
return _other_order;
}
set
{
Disposable.dispose(ref _other_order_errors_sub);
_other_order = value;
_other_order?.init();
if (_other_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_other_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(other_order));
}
}
public void Dispose()
{
Disposable.dispose(ref _dispose);
if (_child_order_choices != null)
_child_order_choices.OfType<IDisposable>().DisposeAll();
if (_other_order_choices != null)
_other_order_choices.OfType<IDisposable>().DisposeAll();
}
public override void init()
{
if (_child_order == null && _security.HasValue)
_select_child_order();
if (_other_order == null && _security.HasValue)
_select_other_order();
_validate();
}
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
void _select_child_order(CustomOneofCase ot)
{
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_child_order == null)
_child_order = child_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(child_order));
}
void _select_other_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_other_order_type.has_value ?
_inline_props.selected_other_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_other_order(ot);
}
void _select_other_order(CustomOneofCase ot)
{
_other_order = other_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_other_order == null)
_other_order = other_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(other_order));
}
void _validate()
{
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
set_error(nameof(child_order), string.Empty, true);
else clear_error(nameof(child_order));
if ((_other_order == null ) || (_other_order is INotifyDataErrorInfo hdei && hdei.HasErrors))
set_error(nameof(other_order), string.Empty, true);
else clear_error(nameof(other_order));
}
}
}
}
@@ -15,11 +15,12 @@ namespace mbts.spark {
public static IOrderTypeVM stop(OrdersModel model, string id, string display_name) => new StopOrderVM(model, id, display_name);
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new StopLimitOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TimeBoxOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TickOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new LeanOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new JoinOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TopOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM cover(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new CoverOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM oco(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new OCOOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM pricer(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new PricerOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM offset(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new OffsetOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TriggerOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new IcebergOrderVM(model, security_md_source, id, display_name);
@@ -34,11 +35,12 @@ namespace mbts.spark {
case CustomOneofCase.Stop: return stop(model, id, display_name);
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source, id, display_name);
case CustomOneofCase.TimeBox: return time_box(model, security_md_source, id, display_name);
case CustomOneofCase.Tick: return tick(model, security_md_source, id, display_name);
case CustomOneofCase.Lean: return lean(model, security_md_source, id, display_name);
case CustomOneofCase.Join: return join(model, security_md_source, id, display_name);
case CustomOneofCase.Top: return top(model, security_md_source, id, display_name);
case CustomOneofCase.Cover: return cover(model, security_md_source, id, display_name);
case CustomOneofCase.Oco : return oco(model, security_md_source, id, display_name);
case CustomOneofCase.Pricer: return pricer(model, security_md_source, id, display_name);
case CustomOneofCase.Offset: return offset(model, security_md_source, id, display_name);
case CustomOneofCase.Trigger: return trigger(model, security_md_source, id, display_name);
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source, id, display_name);
@@ -15,52 +15,42 @@ namespace mbts.spark
{
static partial class OrderTypeVMFactory
{
class InlineTickOrderProperties
class InlinePricerOrderProperties
{
public InlineTickOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
public InlinePricerOrderProperties (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<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
cross_ticks = new InlineUserProperty<uint>(0, false);
max_chase_ticks = new InlineUserProperty<uint>(10, false);
detach_on_toward = new InlineUserProperty<bool>(true, false);
price_offset_ticks = new InlineUserProperty<int>(0, false);
price_factor = new InlineUserProperty<double>(0, false);
}
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
public readonly InlineUserProperty<uint> cross_ticks;
public readonly InlineUserProperty<uint> max_chase_ticks;
public readonly InlineUserProperty<bool> detach_on_toward;
public readonly InlineUserProperty<int> price_offset_ticks;
public readonly InlineUserProperty<double> price_factor;
}
class TickOrderVM : OrderTypeVMBase<TickOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
class PricerOrderVM : OrderTypeVMBase<PricerOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineTickOrderProperties _inline_props;
InlinePricerOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public uint tick_offset_on_deploy { get; set; } = 0;
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
public uint cross_ticks { get; set; } = 0;
public uint max_chase_ticks { get; set; } = 10;
public bool detach_on_toward { get; set; } = true;
public int price_offset_ticks { get; set; } = 0;
public double price_factor { get; set; } = 1.0;
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
public PricerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTickOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
new InlinePricerOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineTickOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
public PricerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlinePricerOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
@@ -76,31 +66,16 @@ namespace mbts.spark
{
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (p.tick_offset_on_deploy.force_override)
if (p.price_offset_ticks.force_override)
{
tick_offset_on_deploy = p.tick_offset_on_deploy.value;
RaisePropertyChanged(nameof(tick_offset_on_deploy));
price_offset_ticks = p.price_offset_ticks.value;
RaisePropertyChanged(nameof(price_offset_ticks));
}
if (p.cross_threshold.force_override)
if (p.price_factor.force_override)
{
cross_threshold = p.cross_threshold.value;
RaisePropertyChanged(nameof(cross_threshold));
}
if (p.cross_ticks.force_override)
{
cross_ticks = p.cross_ticks.value;
RaisePropertyChanged(nameof(cross_ticks));
}
if (p.max_chase_ticks.force_override)
{
max_chase_ticks = p.max_chase_ticks.value;
RaisePropertyChanged(nameof(max_chase_ticks));
}
if (p.detach_on_toward.force_override)
{
detach_on_toward = p.detach_on_toward.value;
RaisePropertyChanged(nameof(detach_on_toward));
}
price_factor = p.price_factor.value;
RaisePropertyChanged(nameof(price_factor));
}
_inline_props = p;
_validate();
})
@@ -115,14 +90,11 @@ namespace mbts.spark
_validate();
if (!HasErrors)
{
var i = factory.tick_order();
i.TickOffsetOnDeploy = tick_offset_on_deploy;
i.CrossThreshold = cross_threshold;
i.CrossTicks = cross_ticks;
i.MaxChaseTicks = max_chase_ticks;
i.DetachOnToward = detach_on_toward;
var i = factory.pricer_order();
i.PriceOffsetTicks = price_offset_ticks;
i.PriceFactor = price_factor;
child_order.assign_to(i.Child);
value.Tick = i;
value.Pricer = i;
}
}
@@ -19,7 +19,8 @@ namespace mbts.spark {
var so = model.factory.spreader_order();
so.Security = s.CloneToMutable();
so.ExecutionStrategy = strategy.CloneToMutable();
return new InlineUserProperty<SpreaderOrderDefinitionImmutable>(so, true);
var soi = new SpreaderOrderDefinitionImmutable(so);
return new InlineUserProperty<SpreaderOrderDefinitionImmutable>(soi, true);
})), id, display_name) { }
public SpreaderOrderVM(
+10 -7
View File
@@ -44,8 +44,8 @@ namespace mbts.spark {
_dynamic_helper = dynamic_helper;
var scheduler = ThreadPoolScheduler.Instance;
var interval = TimeSpan.FromMilliseconds(100);
on_orders = _service.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_orders = _service.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
working_orders =
on_orders.Scan(
ImmutableDictionary<uint,OrderRecordImmutable>.Empty,
@@ -55,7 +55,7 @@ namespace mbts.spark {
on_order_tuples = order_record_tuple_source.subscribe_incremental_update();
on_order_tuples_table = order_record_tuple_source.subscribe_incremental_table_update();
on_order_securities = order_record_tuple_source.subscribe_order_securities();
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
_activity = new OrderActivitySummarizer(bus, on_orders);
on_fill_tuples = FillTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders, on_fills);
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
@@ -93,10 +93,13 @@ namespace mbts.spark {
#endregion
#region Input
public async Task<uint?> send_order(OrderDefinitionImmutable order, double price, uint size) {
var result = (await send_order_get_error(order, price, size));
_activity.on_send_order(order, price, size, result.key, result.error);
return result.key;
public async Task<uint?> send_order(OrderDefinitionImmutable? order, double price, uint size) {
if (order.HasValue) {
var result = (await send_order_get_error(order.Value, price, size));
_activity.on_send_order(order.Value, price, size, result.key, result.error);
return result.key;
}
return 0;
}
async Task<(uint? key, string error)> send_order_get_error(OrderDefinitionImmutable order, double price, uint size) {
+11 -1
View File
@@ -31,7 +31,7 @@
<Reference Include="WindowsBase" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.ComponentModel.Composition" />
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="IronPython" Version="2.7.7" />
<PackageReference Include="NAudio" Version="1.8.4" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
@@ -54,6 +54,7 @@
<ItemGroup>
<None Remove="Sounds\game_sound_correct.wav" />
<None Remove="Sounds\whistle.wav" />
<None Remove="view\Controls\TickUpDown\TickUpDownNullableUIntCommit.xaml" />
<None Remove="view\Shell\Workspaces\WorkspacesView.xaml" />
</ItemGroup>
<ItemGroup>
@@ -71,11 +72,20 @@
<Folder Include="view\Extensions\ItemsControl\ObservableItemsSource\" />
</ItemGroup>
<ItemGroup>
<Compile Update="view\Controls\TickUpDown\TickUpDownNullableUIntCommit.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="view\Shell\Workspaces\WorkspacesDialogView.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Modules\Trading\OrderBook\MiniOrderBook\MiniOrderBookView.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
<None Update="Modules\Trading\OrderBook\MiniOrderBook\OrderBookView.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
<None Update="view\Controls\DIYGrid\Settings\SimpleGridSettingsDialog.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
@@ -3,6 +3,7 @@ using bts.api.orderman;
using bts.shell;
using mbts.spark;
using mbts.ui;
using mbts;
using System;
using System.Collections.Generic;
@@ -211,6 +211,8 @@ namespace mbts.ui {
public static BTSColor lean_order { get { return new BTSColor(255, 170, 210); } }
public static BTSColor join_order { get { return new BTSColor(200, 171, 232); } }
public static BTSColor cover_order { get { return new BTSColor(134, 232, 224); } }
public static BTSColor oco_order { get { return new BTSColor(110, 94, 11); } }
public static BTSColor pricer_order { get { return new BTSColor(170, 132, 150); } }
public static BTSColor trigger_order { get { return new BTSColor(172, 255, 179); } }
public static BTSColor off_order { get { return new BTSColor(0x8a, 0x00, 0x5f); } }//8a005f
public static BTSColor top_order { get { return new BTSColor(0xdd, 0x7a, 0x27); } }//dd7a27
@@ -218,9 +218,7 @@ namespace mbts
private Func<T, T, int> _comparer_func;
public DelegateComparer(Func<T, T, int> comparer_func) { _comparer_func = comparer_func; }
public int Compare(T x, T y) { return _comparer_func(x, y); }
}
public static void ignore(this Task t) { }
}
public static Google.Protobuf.WellKnownTypes.Timestamp ToGoogleTime(this DateTime self) {
return Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(self);
@@ -22,12 +22,12 @@ namespace mbts.ui.settings
readonly PersistenceService _persist;
[ImportingConstructor]
public UISettingsFactory(UserService login, [Import("settings")] PersistenceService persist) {
public UISettingsFactory(AuthService login, [Import("settings")] PersistenceService persist) {
_persist = persist;
_users = get_users(login);
}
async Task<User[]> get_users(UserService login) {
async Task<User[]> get_users(AuthService login) {
var users = await login.get_users_async(null);
if (users.success) return users.value.Users.ToArray();
else return new User[0];
@@ -120,7 +120,7 @@ namespace mbts.persist
public IObservable<IEnumerable<PersistResult<T>>> subscribe<T>(string ns, string pattern) {
var re = new Regex(pattern);
return _persist
.subscribe_ns(new NamespaceSubArgs() { Ns = uns(ns) })
.subscribe_ns(uns(ns))
.Select(update =>
update.added
.Where(item => re.IsMatch(item.Key))
@@ -51,7 +51,7 @@ namespace bts.proto {
static class DateTimeProviderExtensions {
public static DateTime get_date_time(this DateTimeProvider dtp) {
switch (dtp.mode) {
case DateTimeProviderMode.CMEOpen: return MarketSchedule.get_cme_gen_open();
case DateTimeProviderMode.CMEOpen: return MarketSchedule.get_start_of_trading_day();
case DateTimeProviderMode.Today: return DateTime.Today;
case DateTimeProviderMode.Explicit:
default: return dtp.explicit_date.GetValueOrDefault();
+16
View File
@@ -9,6 +9,7 @@ using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using FontFamily = System.Windows.Media.FontFamily;
@@ -38,6 +39,7 @@ namespace mbts.ui.controls {
CustomOrderDefinition.CustomOneofCase _parent_algo_type;
(Point p1, Point p2, Point p3) _current_triangle_vertices;
StreamGeometry _triangle_geometry;
Popup _popup;
static Badge() {
@@ -181,6 +183,7 @@ namespace mbts.ui.controls {
_has_multiple_orders_at_level = HasMultipleOrdersAtLevel;
_is_hedge_order = IsHedgeOrder;
_gcr.glyph_context = GlyphContext.create(FontFamily, FontWeight);
_popup = Popup;
}
public static readonly DependencyProperty TextLinesProperty =
@@ -264,6 +267,19 @@ namespace mbts.ui.controls {
(b, v) => b._is_hedge_order = v,
false);
public Popup Popup {
get { return (Popup)GetValue(PopupProperty); }
set { SetValue(PopupProperty, value); }
}
public static readonly DependencyProperty PopupProperty =
WPFHelper.create_dp<Popup, Badge>(
nameof(Popup),
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._popup = v,
null);
protected override Size ArrangeOverride(Size finalSize) => finalSize;
protected override Size MeasureOverride(Size availableSize) {
@@ -1,16 +1,21 @@
using System;
using mbts.ui.shell;
using System;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace mbts.spark {
class CellProperty<T> : ISubject<T, (T oldval, T newval)> {
readonly Func<T, string> _format;
readonly IRaiseNotifyPropertyChanged _owner;
readonly string _prop_name;
string _text;
public CellProperty(T initial_value = default)
: this(t => t.ToString(), initial_value) { }
public CellProperty(IRaiseNotifyPropertyChanged owner, string prop_name, T initial_value = default)
: this(owner, prop_name, t => t.ToString(), initial_value) { }
public CellProperty(Func<T, string> format, T initial_value = default) {
public CellProperty(IRaiseNotifyPropertyChanged owner, string prop_name, Func<T, string> format, T initial_value = default) {
_owner = owner;
_prop_name = prop_name;
_format = format;
value = initial_value;
}
@@ -24,6 +29,7 @@ namespace mbts.spark {
if (!value.Equals(this.value)) {
OnNext(value);
dirty = true;
_owner.raise_notify_property_changed(_prop_name);
}
}
@@ -36,7 +36,6 @@ namespace mbts.ui.controls {
Symbol = 78,
Time = 112,
TimeInForce = 47,
TradeKey = 76,
User = 53,
Aggressor = 80,
Volume = 74,
@@ -71,7 +70,6 @@ namespace mbts.ui.controls {
ImpliedSize = "Qty (I)",
Symbol = "Symbol",
TimeInForce = "TIF",
TradeKey = "Trade Key",
User = "User",
Time = "Time",
Aggressor = "Aggressor",
@@ -97,7 +95,8 @@ namespace mbts.ui.controls {
DayPnL = "Day PnL",
TotalNetPos = "Total Net Pos",
TotalPnL = "Total PnL",
PnLPrice = "PnL Price";
PnLPrice = "PnL Price",
Cancel = "Cancel";
}
public interface IKeyedColumn<TColKey> {
@@ -139,6 +139,7 @@ namespace mbts.ui.controls {
public static readonly Thickness
DefaultCellPadding = new Thickness(4, 2, 4, 2),
AnimatableCellPadding = new Thickness(8, 2, 4, 2);
public static readonly Visibility DefaultRowsHorizontalScrollBarVisibility = Visibility.Visible;
#region RoutedCommands
public static readonly RoutedCommand SortCommand = new RoutedCommand(), DefaultRowCommand = new RoutedCommand();
@@ -695,6 +696,14 @@ namespace mbts.ui.controls {
public static readonly DependencyProperty SelectionModeProperty = ListBox.SelectionModeProperty.AddOwner(typeof(DIYGrid));
public Visibility RowsHorizontalScrollBarVisibility {
get { return (Visibility)GetValue(RowsHorizontalScrollBarVisibilityProperty); }
set { SetValue(RowsHorizontalScrollBarVisibilityProperty, value); }
}
public static readonly DependencyProperty RowsHorizontalScrollBarVisibilityProperty = DependencyProperty.Register(
nameof(RowsHorizontalScrollBarVisibility), typeof(Visibility), typeof(DIYGrid), new PropertyMetadata(DefaultRowsHorizontalScrollBarVisibility));
#endregion
// Internal Methods
@@ -62,7 +62,8 @@
<Setter Property="ColumnHeaderStyle">
<Setter.Value>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Background" Value="{mbts:BTSColor header}"/>
<!-- Background - note that we're settings this property on the containing stackpanel in which the GridViewHeaderRowPresenter resides -->
<Setter Property="Background" Value="{x:Null}"/>
<Setter Property="Foreground" Value="{mbts:BTSColor header_text}"/>
<Setter Property="BorderBrush" Value="{mbts:BTSColor header}"/>
<Setter Property="BorderThickness" Value="0,1,1,0"/>
@@ -301,7 +302,9 @@
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
x:Name="_header_scroll_viewer">
<StackPanel>
<!-- setting the bg color for this stackpanel is a work-around for the vertical white lines that appear on high-dpi displays between some columns -->
<!-- in reality, there is some high dpi rounding thing going on, but that's a battle for another day -->
<StackPanel Background="{mbts:BTSColor Name=header}">
<!-- We don't need super headers over the fixed columns -->
<StackPanel Orientation="Horizontal" x:Name="_super_headers" />
<GridViewHeaderRowPresenter x:Name="_header_scroll" AllowsColumnReorder="False" ColumnHeaderContainerStyle="{TemplateBinding ColumnHeaderStyle}" UseLayoutRounding="True" />
@@ -332,7 +335,7 @@
</ControlTemplate>
</bts:DIYGridRowsPresenter.Template>
</bts:DIYGridRowsPresenter>
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" />
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" Visibility="{TemplateBinding RowsHorizontalScrollBarVisibility}" />
<!-- todo: All edit controls go in a canvas - edit api is called with an edit type. Hide all controls -->
<!-- This stole hours of my life. The Text binding down there used to be 'value' and I was encountering -->
@@ -1,4 +1,5 @@
using bts.proto;
using bts.utils;
using ProtoBuf;
using System;
using System.ComponentModel;
@@ -18,7 +19,7 @@ namespace mbts.ui.settings {
}
[ProtoContract]
class GridColumnSettings {
class GridColumnSettings : IEquatable<GridColumnSettings> {
const bool
_default_is_visible = true,
_default_is_collapsed = false;
@@ -103,5 +104,43 @@ namespace mbts.ui.settings {
width ?? this.width,
horizontal_alignment ?? this.horizontal_alignment,
horizontal_offset ?? this.horizontal_offset);
public bool Equals(GridColumnSettings other) =>
other != null &&
id == other.id &&
display_name == other.display_name &&
is_visible == other.is_visible &&
is_collapsed == other.is_collapsed &&
font_weight_override.Equals(other.font_weight_override) &&
font_color_override.Equals(other.font_color_override) &&
width == other.width &&
horizontal_alignment == other.horizontal_alignment &&
horizontal_offset == other.horizontal_offset &&
display_name == other.display_name;
public override bool Equals(object obj) =>
Equals(obj as GridColumnSettings);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(
id.GetHashCode(),
display_name.GetHashCode(),
is_visible.GetHashCode(),
is_collapsed.GetHashCode(),
font_weight_override.GetHashCode(),
font_color_override.GetHashCode(),
width.GetHashCode(),
horizontal_alignment.GetHashCode(),
horizontal_offset.GetHashCode(),
display_name.GetHashCode()
);
public override string ToString() {
return $"id: {id} display_name: {display_name}";
}
}
}
@@ -126,13 +126,15 @@ namespace mbts.ui {
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight);
_predicate_animations_data = value.predicate_animations;
_animation_defs.clear();
foreach (var pa in _predicate_animations_data) {
var predicate = pa.predicate_builder.create_predicate(_arg);
foreach (var s in pa.sound_notifications) {
_notification_subs.Add(_notification_service.link(predicate, s));
}
if (pa.predicate_builder is INotificationPredicateBuilder<TRowData, TPredicateBuilderArg> tpb && tpb.is_toast_enabled) {
_notification_subs.Add(_notification_service.link(tpb.create_notification_predicate(_arg)));
if (_notification_service != null && _arg != null) {
foreach (var pa in _predicate_animations_data) {
var predicate = pa.predicate_builder.create_predicate(_arg);
foreach (var s in pa.sound_notifications) {
_notification_subs.Add(_notification_service.link(predicate, s));
}
if (pa.predicate_builder is INotificationPredicateBuilder<TRowData, TPredicateBuilderArg> tpb && tpb.is_toast_enabled) {
_notification_subs.Add(_notification_service.link(tpb.create_notification_predicate(_arg)));
}
}
}
// ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings
@@ -14,7 +14,7 @@ namespace mbts.spark.filters {
TimeFilterMode _mode;
TimeTextTuple _begin, _end;
DateTime? _begin_dt, _end_dt;
readonly DateTime _cme_open = MarketSchedule.get_cme_gen_open();
readonly DateTime _cme_open = MarketSchedule.get_start_of_trading_day();
public TimeFilterVM(Func<TRow, DateTime> get_row_val, string prop_name) : base(get_row_val, prop_name) =>
_set_default_values();
@@ -34,9 +34,11 @@ namespace bts.view.controls {
private void any_button_click(object sender, RoutedEventArgs e) {
Close();
}
internal static void Show(ErrorReportService srv, ErrorReport report) {
var window = new SupportRequestWindow(new SupportRequestVM(srv, report));
window.Show();
internal static void Show(ErrorReportService srv, ErrorReportImmutable? report) {
if (report.HasValue) {
var window = new SupportRequestWindow(new SupportRequestVM(srv, report.Value));
window.Show();
}
}
}
@@ -166,51 +166,6 @@
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.TimeBoxOrderTemplate>
<usp:OrderModificationTemplateSelector.TickOrderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Cross Thresh</TextBlock>
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
<TextBlock Grid.Row="3" VerticalAlignment="Center">Cross Ticks</TextBlock>
<c:TickUpDownUInt Grid.Row="3" Grid.Column="2" Value="{Binding cross_ticks}"/>
<TextBlock Grid.Row="5" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
<c:TickUpDownUInt Grid.Row="5" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
<CheckBox Grid.Row="7" Grid.Column="2" VerticalAlignment="Center" Margin="0,0,0,0" IsChecked="{Binding detach_on_toward}">Detach on Toward</CheckBox>
<TextBlock Grid.Row="9" VerticalAlignment="Center">Initial Tick Offset</TextBlock>
<c:TickUpDownUInt Grid.Row="9" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
<TextBlock Grid.Row="11">Child</TextBlock>
<uag:OrderDetailsPopupControl
Grid.Row="11" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
ItemsSource="{Binding child_order_types}"
SelectedItem="{Binding child_order}"
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
/>
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.TickOrderTemplate>
<usp:OrderModificationTemplateSelector.LeanOrderTemplate>
<DataTemplate>
<Grid>
@@ -379,16 +334,16 @@
Orientation="Horizontal"
Grid.Row="1"
>
<TextBlock VerticalAlignment="Center" >Hedge Ticks</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks to offset the hedge." />
<TextBlock VerticalAlignment="Center" >Profit Ticks</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks in your favor to offset the profit/hedge order." />
</StackPanel>
<c:TickUpDownInt Grid.Row="1" Grid.Column="2" Value="{Binding hedge_ticks}"/>
<StackPanel
Orientation="Horizontal"
Grid.Row="3"
>
<TextBlock VerticalAlignment="Center">Hedge</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the hedge order to send for each fill of Child Order." />
<TextBlock VerticalAlignment="Center">Profit Order</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the profit/hedge order to send for each fill of Child Order." />
</StackPanel>
<uag:OrderDetailsPopupControl
Grid.Row="3" Grid.Column="2"
@@ -415,6 +370,68 @@
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.CoverOrderTemplate>
<usp:OrderModificationTemplateSelector.OCOOrderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="1" >
<TextBlock VerticalAlignment="Center" >Stop Ticks</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks away from the main order to start the 'stop' order." />
</StackPanel>
<c:TickUpDownInt Grid.Row="1" Grid.Column="2" Value="{Binding other_ticks}"/>
<StackPanel Orientation="Horizontal" Grid.Row="3" >
<TextBlock VerticalAlignment="Center" >Stop Side</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="The Side of the 'stop' order" />
</StackPanel>
<ComboBox Grid.Row="3" Grid.Column="2" ItemsSource="{Binding other_side_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding other_side}"/>
<StackPanel
Orientation="Horizontal"
Grid.Row="5"
>
<TextBlock VerticalAlignment="Center">Stop Order</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the 'stop' order to send." />
</StackPanel>
<uag:OrderDetailsPopupControl
Grid.Row="5" Grid.Column="2"
IsOpen="{Binding other_order.editable_properties_open, FallbackValue=False}"
ItemsSource="{Binding other_order_types}"
SelectedItem="{Binding other_order}"
HasEditableProperties="{Binding other_order.has_editable_properties, FallbackValue=False}"
/>
<StackPanel
Orientation="Horizontal"
Grid.Row="7"
>
<TextBlock VerticalAlignment="Center">Child</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered." />
</StackPanel>
<uag:OrderDetailsPopupControl
Grid.Row="7" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
ItemsSource="{Binding child_order_types}"
SelectedItem="{Binding child_order}"
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
/>
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.OCOOrderTemplate>
<usp:OrderModificationTemplateSelector.OffsetOrderTemplate>
<DataTemplate>
<Grid>
@@ -773,9 +790,11 @@
</usp:DynamicOrderParamTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
<TextBlock Grid.Row="3">Child</TextBlock>
<TextBlock Grid.Row="3"
Visibility="{Binding has_child, Converter={StaticResource BooleanToVisibilityConverter}}" >Child</TextBlock>
<uag:OrderDetailsPopupControl
Grid.Row="3" Grid.Column="2"
Grid.Row="3" Grid.Column="2"
Visibility="{Binding has_child, Converter={StaticResource BooleanToVisibilityConverter}}"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
ItemsSource="{Binding child_order_types}"
SelectedItem="{Binding child_order}"
@@ -0,0 +1,72 @@
<UserControl x:Class="mbts.ui.controls.PriceEntryCommitControl"
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"
mc:Ignorable="d"
xmlns:u="clr-namespace:mbts.ui"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:usp="clr-namespace:mbts.ui.spark"
d:DesignHeight="100" d:DesignWidth="800" x:Name="_me_price">
<UserControl.Resources>
<cv:NullableDoubleConverter x:Key="TickDefaultPriceConverter" DefaultValue="0"/>
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter" x:Shared="False"/>
<ControlTemplate x:Key="ReadTemplate">
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.ColumnSpan="2">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="SourceValue" ElementName="_me_price"/>
<Binding Path="SourceContractSpec" ElementName="_me_price"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="EditTemplate">
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<usp:PriceEntryControl x:Name="price_ticker"
Margin="0,0,2,0"
TickDefaultValue="{Binding SourceValue, ElementName=_me_price, Converter={StaticResource TickDefaultPriceConverter}, FallbackValue={u:Double 0}}"
ContractSpec="{Binding SourceContractSpec, ElementName=_me_price}"
Value="{Binding SourceValue, ElementName=_me_price}" />
<Button Grid.Column="2" x:Name="commit_button" CommandParameter="{Binding Value, ElementName=price_ticker}" Command="{Binding OnCommit, ElementName=_me_price}" Content="✔" >
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Background" Value="Green" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</ControlTemplate>
</UserControl.Resources>
<UserControl.Style>
<Style TargetType="UserControl">
<Setter Property="Template" Value="{StaticResource ReadTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding is_selected}" Value="True">
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding is_price_dirty}" Value="True">
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
</UserControl>
@@ -0,0 +1,73 @@
using bts.api;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace mbts.ui.controls {
/// <summary>
/// Interaction logic for PriceEntryCommitControl.xaml
/// </summary>
public partial class PriceEntryCommitControl : UserControl {
public static readonly DependencyProperty SourceValueProperty =
DependencyProperty.Register("SourceValue",
typeof(double),
typeof(PriceEntryCommitControl),
new FrameworkPropertyMetadata {
BindsTwoWayByDefault = true,
PropertyChangedCallback = source_value_changed
}
);
public double SourceValue {
get => (double)GetValue(SourceValueProperty);
set => SetValue(SourceValueProperty, value);
}
private static void source_value_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
if (d is PriceEntryCommitControl _instance_of_me) {
_instance_of_me.Dispatcher.BeginInvoke(
(Action)(() => _instance_of_me.OnCommit?.TryRaiseCanExecuteChanged())
);
}
}
public static readonly DependencyProperty SourceContractSpecProperty =
DependencyProperty.Register("SourceContractSpec", typeof(ContractSpecificationImmutable?), typeof(PriceEntryCommitControl));
public ContractSpecificationImmutable? SourceContractSpec {
get => (ContractSpecificationImmutable?)GetValue(SourceContractSpecProperty);
set => SetValue(SourceContractSpecProperty, value);
}
// public static readonly DependencyProperty PriceTextProperty =
//DependencyProperty.Register("PricText", typeof(string), typeof(PriceEntryCommitControl));
// public string PriceText {
// get => (string)GetValue(PriceTextProperty);
// set => SetValue(PriceTextProperty, value);
// }
public static readonly DependencyProperty OnCommitProperty =
DependencyProperty.Register("OnCommit", typeof(ICommand), typeof(PriceEntryCommitControl));
public ICommand OnCommit {
get => (ICommand)GetValue(OnCommitProperty);
set => SetValue(OnCommitProperty, value);
}
public PriceEntryCommitControl() {
InitializeComponent();
}
}
}
@@ -0,0 +1,55 @@
<UserControl x:Class="mbts.ui.controls.TickUpDownNullableUIntCommit"
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"
mc:Ignorable="d"
xmlns:c="clr-namespace:mbts.ui.controls"
d:DesignHeight="100" d:DesignWidth="800" x:Name="_me">
<UserControl.Resources>
<ControlTemplate x:Key="ReadTemplate">
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.ColumnSpan="2" Text="{Binding SourceValue, ElementName=_me}" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="EditTemplate">
<Grid HorizontalAlignment="Right" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<c:TickUpDownNullableUInt x:Name="qty_ticker" Margin="0,0,2,0" Value="{Binding SourceValue, ElementName=_me}" />
<Button Grid.Column="2" x:Name="commit_button" CommandParameter="{Binding Value, ElementName=qty_ticker}" Command="{Binding OnCommit, ElementName=_me}" Content="✔" >
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Background" Value="Green" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</ControlTemplate>
</UserControl.Resources>
<UserControl.Style>
<Style TargetType="UserControl">
<Setter Property="Template" Value="{StaticResource ReadTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding is_selected}" Value="True">
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding is_qty_dirty}" Value="True">
<Setter Property="Template" Value="{StaticResource EditTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
</UserControl>
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace mbts.ui.controls {
/// <summary>
/// Interaction logic for TickUpDownNullableUIntCommit.xaml
/// </summary>
public partial class TickUpDownNullableUIntCommit : UserControl {
public static readonly DependencyProperty SourceValueProperty =
DependencyProperty.Register("SourceValue",
typeof(uint?),
typeof(TickUpDownNullableUIntCommit),
new FrameworkPropertyMetadata {
BindsTwoWayByDefault = true,
PropertyChangedCallback = source_value_changed
}
);
private static void source_value_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
if (d is TickUpDownNullableUIntCommit _instance_of_me) {
_instance_of_me.Dispatcher.BeginInvoke(
(Action)(() => _instance_of_me.OnCommit?.TryRaiseCanExecuteChanged())
);
}
}
public uint? SourceValue {
get => GetValue(SourceValueProperty) as uint?;
set => SetValue(SourceValueProperty, value);
}
public static readonly DependencyProperty OnCommitProperty =
DependencyProperty.Register("OnCommit", typeof(ICommand), typeof(TickUpDownNullableUIntCommit));
public ICommand OnCommit {
get => (ICommand)GetValue(OnCommitProperty);
set => SetValue(OnCommitProperty, value);
}
public TickUpDownNullableUIntCommit() {
InitializeComponent();
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace mbts.ui.converters {
class ResponsiveTextConverter : IValueConverter {
public string ShortText { get; set; }
public string LongText { get; set; }
public double Threshold { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is double _input) {
if (_input < Threshold) {
return ShortText;
} else {
return LongText;
}
}
return LongText;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
}
+45 -69
View File
@@ -1,39 +1,29 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using mbts.config;
using mbts.ui.native;
using mbts.utils;
using bts.co2;
using bts.api.user;
using bts;
using bts.api;
using bts.api.core;
using bts.api.persistence;
using bts;
using bts.api.user;
using mbts.config;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace mbts.ui.shell
{
namespace mbts.ui.shell {
// The LoginContainer participates in the startup Import/Export dependency system
// Since the login form should not depend on *anything* it will be shown before this is created
// Once this is created it access the global LoginModel and retrieves the username
internal class LoginContainer
{
[ImportingConstructor]
public LoginContainer(UserService user, [Import("settings")] PersistenceService persist) {
public LoginContainer(ServiceContext context) {
var model = LoginModel.instance;
model.provide_services(user, persist);
model.provide_services(context);
// await_login pumps the dispatcher but doesn't allow control to return to MEF until the username and workspace are available
model.await_login();
var u = model.get_user();
UserName = u.name;
UserId = u.id;
Token = u.token;
(UserName,UserId,Token) = model.get_user();
Workspace = model.get_workspace();
model.Dispose();
}
@@ -44,21 +34,19 @@ namespace mbts.ui.shell
[Export("userid")]
public uint UserId { get; private set; }
[Export]
public AuthToken Token { get; private set; }
public AuthTokenImmutable Token { get; private set; }
[Export("workspace")]
public string Workspace { get; private set; }
}
public struct User {
public User(string name, uint id, AuthToken token) {
public User(string name, uint id) {
this.name = name;
this.id = id;
this.token = token;
}
public readonly string name;
public readonly UInt32 id;
public readonly AuthToken token;
}
public class LoginModel : IDisposable
@@ -66,11 +54,10 @@ namespace mbts.ui.shell
// Singleton to help bootstrap the system
private static LoginModel _instance = new LoginModel();
public static LoginModel instance { get { return _instance; } }
private User _user;
private string _workspace;
private PreloginWorkspaceEnumerator _enum_workspace;
UserService _logon;
ServiceContext _ctx;
DispatcherFrame _frame = new DispatcherFrame();
private LoginModel() {
@@ -82,13 +69,14 @@ namespace mbts.ui.shell
public string ErrorMessage { get; private set; }
public bool HasFatalError { get; set; }
public User[] Users { get; private set; }
// The final username
public User get_user() {
Log.assert_d(!string.IsNullOrEmpty(_user.name));
return _user;
public (string, uint, AuthTokenImmutable) get_user() {
Log.assert_d(!string.IsNullOrEmpty(_ctx.user));
return (_ctx.user, _ctx.user_id, _ctx.token.Value);
}
public string get_workspace() {
return _workspace ?? "";
}
@@ -112,13 +100,13 @@ namespace mbts.ui.shell
}
// Wait for the hub to create the login doodad
(var users, var persist) = await _hub_src.Task;
_logon = users;
_ctx = await _hub_src.Task;
// Populate the UI
_enum_workspace = new PreloginWorkspaceEnumerator(persist);
var response = await _logon.get_users_async(new GetUsersRequest());
_enum_workspace = new PreloginWorkspaceEnumerator(_ctx.locate<PersistenceService>("settings"));
var auth = _ctx.locate<AuthService>();
var response = await auth.get_users_async(new GetUsersRequest());
if (response.success) {
Users = response.value.Users.Select(x => new User(x.Login,x.Id, null)).OrderBy(u => u.name).ToArray();
Users = response.value.Users.Select(x => new User(x.Login,x.Id)).OrderBy(u => u.name).ToArray();
}
else {
ErrorMessage = "Logon: " + response.error_message;
@@ -129,49 +117,37 @@ namespace mbts.ui.shell
#endregion
public async Task<(bool success,string info)> do_login(string username, string workspace, string password) {
public async Task<LoginResult> do_login(string username, string workspace, string password) {
if (Users.Select(u => u.name).Contains(username)) {
var response = await _logon.authenticate_login_async(new AuthenticateLoginRequest() {
Login = username,
Password = password ??""
});
if (response.success) {
if (response.value.IsValid) {
_workspace = workspace;
set_authenticated_user(new User(username, response.value.UserId, response.value.Token));
return (true, "");
}
else {
return (false, response.value.Reason);
}
}
else {
return (false, response.error_message);
var result = await _ctx.login(username, password);
if (result.success) {
_workspace = workspace;
on_user_authenticated(_ctx.user, _ctx.token);
}
return result;
}
return (false, "Invalid username");
return new LoginResult("Invalid username");
}
private void set_authenticated_user(User user) {
Log.assert(!string.IsNullOrEmpty(user.name));
Log.assert(Users.Select(u => u.name).Contains(user.name));
_user = user;
Environment.SetEnvironmentVariable("BTS_USER", user.name);
private void on_user_authenticated(string username, AuthTokenImmutable? token) {
Log.assert(!string.IsNullOrEmpty(username));
Log.assert(Users.Select(u => u.name).Contains(username));
Environment.SetEnvironmentVariable("BTS_USER", username);
_frame.Continue = false;
}
public void await_login() {
if (string.IsNullOrEmpty(_user.name)) {
if (!_ctx.token.HasValue) {
Dispatcher.PushFrame(_frame);
}
}
#region provide hub
public void provide_services(UserService u, PersistenceService p) {
_hub_src.TrySetResult((u,p));
internal void provide_services(ServiceContext ctx) {
_hub_src.TrySetResult(ctx);
}
private TaskCompletionSource<(UserService, PersistenceService)> _hub_src = new TaskCompletionSource<(UserService, PersistenceService)>();
private TaskCompletionSource<ServiceContext> _hub_src = new TaskCompletionSource<ServiceContext>();
#endregion
#region Dispose
@@ -138,14 +138,15 @@ namespace mbts.ui.shell
if (selected_workspace == "New workspace...") {
selected_workspace = null;
}
(bool success, string message) = await _model.do_login(Name, selected_workspace, Password);
if (success) { VisualState = "Loading"; }
if (!success && !string.IsNullOrEmpty(message)) {
WarningMessage = message;
var result = await _model.do_login(Name, selected_workspace, Password);
if (result.success) { VisualState = "Loading"; }
if (!result.success && !string.IsNullOrEmpty(result.reason)) {
WarningMessage = result.reason;
VisualState = "BadPassword";
}
else if (!string.IsNullOrEmpty(message)) {
BTSMessageBox.Show(message, "Login Message", BTSMessageBoxButtons.OK, MessageBoxImage.Information);
else if (!string.IsNullOrEmpty(result.reason)) {
// Login was successful but we have a message for the user
BTSMessageBox.Show(result.reason, "Login Message", BTSMessageBoxButtons.OK, MessageBoxImage.Information);
}
}
private void OnCancel(object unused) {
@@ -11,7 +11,7 @@ namespace bts.settings {
public class SettingsManagerTool {
public static void run() {
var sc = new ServiceContext();
var users = sc.locate<UserService>();
var users = sc.locate<AuthService>();
var persist = sc.locate<PersistenceService>("settings");
var user_list = users.get_users(new GetUsersRequest());
var user_names = user_list.Users.Select(u => u.Login);
@@ -123,7 +123,7 @@ namespace mbts.spark {
return intpart + floatpart;
}
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable spec) =>
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable? spec) =>
TickStore.get(spec);
public static RegionalTickFactors get_regional_ticks(SecurityImmutable security) =>
@@ -177,8 +177,14 @@ namespace mbts.spark {
public static RegionalTickFactors get(ContractSpecification spec) =>
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
public static RegionalTickFactors get(ContractSpecificationImmutable spec) =>
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
public static RegionalTickFactors get(ContractSpecificationImmutable? spec) {
if (spec.HasValue) {
return _exchange_ticks.GetOrAdd(new SpecKey(spec.Value), _create_rtf);
}
else {
return null;
}
}
public static RegionalTickFactors get(SecurityImmutable security) =>
security.HasContractSpec ? get(security.ContractSpec) : default_rtf;
@@ -110,7 +110,7 @@ namespace mbts.spark {
{
var lgh = source as LadderGridHost;
double width_adjustment = (double)(uint) e.NewValue - (double)(uint) e.OldValue;
lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Order), width_adjustment);
lgh._adjust_column_width(lgh.columns.FirstOrDefault(c => c.column_type == ColumnType.Order), width_adjustment);
}
public uint bid_column_width
@@ -126,7 +126,7 @@ namespace mbts.spark {
{
var lgh = source as LadderGridHost;
double width_adjustment = (double)(uint) e.NewValue - (double)(uint) e.OldValue;
lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Bid), width_adjustment);
lgh._adjust_column_width(lgh.columns.FirstOrDefault(c => c.column_type == ColumnType.Bid), width_adjustment);
}
public uint price_column_width
@@ -142,7 +142,7 @@ namespace mbts.spark {
{
var lgh = source as LadderGridHost;
double width_adjustment = (double)(uint) e.NewValue - (double)(uint) e.OldValue;
lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Price), width_adjustment);
lgh._adjust_column_width(lgh.columns.FirstOrDefault(c => c.column_type == ColumnType.Price), width_adjustment);
}
@@ -159,14 +159,14 @@ namespace mbts.spark {
{
var lgh = source as LadderGridHost;
double width_adjustment = (double)(uint) e.NewValue - (double)(uint) e.OldValue;
lgh._adjust_column_width(lgh.columns.First(c => c.column_type == ColumnType.Ask), width_adjustment);
lgh._adjust_column_width(lgh.columns.FirstOrDefault(c => c.column_type == ColumnType.Ask), width_adjustment);
}
public static readonly DependencyProperty VolumeColumnWidthProperty =
WPFHelper.create_dp<uint, LadderGridHost>(
nameof(VolumeColumnWidth),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, oldval, newval) => o._adjust_column_width(o.columns.First(c => c.column_type == ColumnType.Volume), (double)newval - (double)oldval),
(o, oldval, newval) => o._adjust_column_width(o.columns.FirstOrDefault(c => c.column_type == ColumnType.Volume), (double)newval - (double)oldval),
50u);
public uint VolumeColumnWidth {
@@ -178,7 +178,7 @@ namespace mbts.spark {
WPFHelper.create_dp<uint, LadderGridHost>(
nameof(LastColumnWidth),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, oldval, newval) => o._adjust_column_width(o.columns.First(c => c.column_type == ColumnType.Last), (double)newval - (double)oldval),
(o, oldval, newval) => o._adjust_column_width(o.columns.FirstOrDefault(c => c.column_type == ColumnType.Last), (double)newval - (double)oldval),
50u);
public uint LastColumnWidth {
@@ -327,6 +327,8 @@ namespace mbts.spark {
}
void _adjust_column_width(Column column, double adjustment) {
if (column == null)
return;
int new_col_width;
switch (column.column_type) {
case ColumnType.Order: new_col_width = (int)orders_column_width; break;
@@ -662,22 +664,10 @@ namespace mbts.spark {
} else {
init = false;
prev_row_height = _settings.row_height;
// This is a general issue with default-settings:
// "Not every property in the settings object should be shared across windows."
// Column widths are one such property (they are changed from the actual Ladder window, not the settings dialog).
// Below is a hack which prevents overwriting current column widths; until we come up with a better solution this will suffice.
// TODO: fix this by moving such properties from the settings proto into a separate proto (or come up with a better workaround/solution)
copy_col_width(v => v.orders_column);
copy_col_width(v => v.bid_column);
copy_col_width(v => v.price_column);
copy_col_width(v => v.ask_column);
copy_col_width(v => v.volume_column.general);
copy_col_width(v => v.last_column);
void copy_col_width(Func<LadderSettings, LadderColumnSettings> select_col) =>
select_col(s).width = select_col(_settings).width;
}
//
_settings = s;
/// The viewmodel will always have the most up-to-date settings, so ignore <see cref="s"/> and just get it from <see cref="_vm"/>.
/// This is a workaround for the global settings-defaults issue: when defaults are updated, some properties are inadvertantly overwritten, such as column-widths, etc.
_settings = _vm.get_save_state();
_bid_vis_tup.glyph_context = glyph_context(s.bid_column);
_ask_vis_tup.glyph_context = glyph_context(s.ask_column);
_price_vis_tup.glyph_context = glyph_context(s.price_column);
@@ -698,18 +688,19 @@ namespace mbts.spark {
// Renders the grid lines and column backgrounds
_visualHost.AddVisualChild(_gridvis);
// Orders column
orders_column_width = s.orders_column.width;
SetCurrentValue(orders_column_widthProperty, s.orders_column.width);
_ordershost.update_settings(_price_list, _price_comparer, s.row_height, s.orders_column);
foreach (var col in columns) {
if (col.visual_tup != null) {
if (col.settings.show) {
_visualHost.AddVisualChild(col.visual_tup.visual);
switch (col.column_type) {
case ColumnType.Bid: bid_column_width = col.settings.width; break;
case ColumnType.Price: price_column_width = col.settings.width; break;
case ColumnType.Ask: ask_column_width = col.settings.width; break;
case ColumnType.Bid: SetCurrentValue(bid_column_widthProperty, col.settings.width); break;
case ColumnType.Price: SetCurrentValue(price_column_widthProperty, col.settings.width); break;
case ColumnType.Ask: SetCurrentValue(ask_column_widthProperty, col.settings.width); break;
case ColumnType.Last: SetCurrentValue(LastColumnWidthProperty, col.settings.width); break;
case ColumnType.Volume:
VolumeColumnWidth = col.settings.width;
SetCurrentValue(VolumeColumnWidthProperty, col.settings.width);
_volume_container.mark_dirty();
break;
}
@@ -37,7 +37,7 @@ namespace mbts.spark {
readonly Property<Brush> _foreground, _background, _hoverbackground, _border, _parent_algo_type_brush;
readonly Property<bool> _is_exchange_order, _has_multiple_orders_at_same_level, _is_hedge_order;
readonly Property<CustomOrderDefinition.CustomOneofCase> _parent_algo_type;
readonly Property<string> _summary, _description;
readonly Property<string> _summary;
IObservable<LadderSettings.BadgeStyle> _bs;
LadderSettings.BadgeStyle _current_badge_style;
int _n_active_price_change_ops = 0;
@@ -63,7 +63,6 @@ namespace mbts.spark {
_hoverbackground = backing(nameof(hoverbackground), _color_bg_brush);
_border = backing(nameof(border), _color_bg_brush);
_summary = backing(nameof(summary), string.Empty);
_description = backing(nameof(description), string.Empty);
_is_exchange_order = backing(nameof(is_exchange_order), false);
_has_multiple_orders_at_same_level = backing(nameof(has_multiple_orders_at_same_level), false);
_is_hedge_order = backing(nameof(is_hedge_order), false);
@@ -75,6 +74,7 @@ namespace mbts.spark {
Brush get_foreground(LadderSettings s) => s.orders_column.foreground.rgb_brush();
//
add_order(o);
_dispose = Disposable.Create(
_bs.Subscribe(badge => {
_current_badge_style = badge;
@@ -122,7 +122,6 @@ namespace mbts.spark {
if (_orders.Any()) {
summary = _orders.Values.Select(order => order.short_description(_parent.security.ContractSpec)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
description = _orders.Values.Select(order => order.full_description(_parent.security)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
}
_set_badge_text();
@@ -185,11 +184,10 @@ namespace mbts.spark {
public Brush hoverbackground { get => _hoverbackground.Value; private set => _hoverbackground.Value = value; }
public Brush border { get => _border.Value; private set => _border.Value = value; }
public string summary { get => _summary.Value; private set => _summary.Value = value; }
public string description { get => _description.Value; private set => _description.Value = value; }
public bool is_exchange_order { get => _is_exchange_order.Value; private set => _is_exchange_order.Value = value; }
public bool has_multiple_orders_at_same_level { get => _has_multiple_orders_at_same_level.Value; private set => _has_multiple_orders_at_same_level.Value = value; }
public bool is_hedge_order { get => _is_hedge_order.Value; private set => _is_hedge_order.Value = value; }
public Brush parent_algo_type_brush { get => _parent_algo_type_brush.Value; private set => _parent_algo_type_brush.Value = value; }
public Brush parent_algo_type_brush { get => _parent_algo_type_brush.Value; private set => _parent_algo_type_brush.Value = value; }
// the setter is public so that the LadderVM can set this field
public CustomOrderDefinition.CustomOneofCase parent_algo_type { get => _parent_algo_type.Value; set => _parent_algo_type.Value = value; }
@@ -224,6 +222,56 @@ namespace mbts.spark {
}
}
public OrderBookVM create_orderbook_vm() {
// We are returning a new OrderBookVM each time b/c after
// successive launches of the mini-ob from the same badge, we get an exception
// System.InvalidOperationException: Sharing GridViewColumn among multiple GridViewColumnCollections or adding the same GridViewColumn into one GridViewColumnCollection more than once is not allowed.
// I checked with Eric and he guesses that what is happening is that the Popup is throwing away the grid, but the binding doesn't remove the columns from that grid
//
// I think returning a new OrderbookVM is okay at the moment since this Popup window will not be saving any state at this time
var price_filter = new filters.DoubleMinMaxState();
price_filter.minimum.value = real_price;
price_filter.maximum.value = real_price;
var obvm = new OrderBookVM(null, _parent.bus, null, _parent.model.order_gateway, null, true);
var state = new bts.shell.OrderBookWindowFactory.OrderBookStartupData(
new bts.shell.OrderBookWindowFactory.FilterStates {
security_criteria = new SecurityCriteria(_parent.security),
price = price_filter,
statuses = new[] { OrderStatus.Working }
},
bts.shell.OrderBookWindowFactory.OrderBookStartupData.default_mini_grid,
use_defaults: false
);
// NOTE:
// we might need the ObservableAppend to InsertAt
// if these EditXXX ever support settings.
// insert the cancel column
obvm.columns.Insert(0, new OrderBookVM.CancelColumn(nameof(OrderBookRowVM.cancel_command)));
var qty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Size.ToString());
var qty_index = obvm.columns.indexOf(qty_col);
if (qty_index > -1) {
// insert the edit column in the same position that the hidden column occupies
var existing_editqty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditableQty.ToString());
var existing_editqty_index = obvm.columns.indexOf(existing_editqty_col);
if (existing_editqty_index == -1) {
obvm.columns.Insert(qty_index, new OrderBookVM.EditableQtyColumn());
}
}
var price_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Price.ToString());
var price_index = obvm.columns.indexOf(price_col);
if (price_index > -1) {
var existing_editprice_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditablePrice.ToString());
var existing_editprice_index = obvm.columns.indexOf(existing_editprice_col);
if (existing_editprice_index == -1) {
// insert the edit price column immediately after the edit qty
obvm.columns.Insert(qty_index + 1, new OrderBookVM.EditablePriceColumn());
}
}
obvm.load_from(state);
return obvm;
}
public override string ToString() {
return $"Price: {this.price} Order Count: {this.orders.Count()}";
}
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
@@ -91,15 +92,26 @@ namespace mbts.spark {
order.PropertyChanged += _order_PropertyChanged;
// get the stackpanel it belongs in
Panel panel = get_panel(price);
//var coords = GetAbsolutePlacement(panel, true);
// insert Badge and wire up DataContext
if (panel != null) {
panel.Children.Add(mkbadge(order));
panel.Children.Add(mkbadge(order, panel));
}
} //else {
//Log.Ui.warn($"Failed to add order ({order.id}): order already in {nameof(LadderGridHost)}'s {nameof(_orders)}.");
//}
}
public static Rect GetAbsolutePlacement(FrameworkElement element, bool relativeToScreen = false) {
var absolutePos = element.PointToScreen(new System.Windows.Point(0, 0));
if (relativeToScreen) {
return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
}
var posMW = Application.Current.MainWindow.PointToScreen(new System.Windows.Point(0, 0));
absolutePos = new System.Windows.Point(absolutePos.X - posMW.X, absolutePos.Y - posMW.Y);
return new Rect(absolutePos.X, absolutePos.Y, element.ActualWidth, element.ActualHeight);
}
void _order_PropertyChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(LadderOrderVM.price)) {
var order = (LadderOrderVM)sender;
@@ -108,35 +120,38 @@ namespace mbts.spark {
}
}
Badge mkbadge(LadderOrderVM order) {
Badge mkbadge(LadderOrderVM order, UIElement popup_placement_target) {
Badge b = new Badge();
b.DataContext = order;
// Hook up the style from xaml
b.Style = this.TryFindResource(typeof(Badge)) as Style;
b.MouseUp += (s, e) => {
b.MouseUp += async (s, e) => {
var curr_order = b.DataContext as LadderOrderVM;
if (e.ChangedButton == MouseButton.Middle) {
b.ContextMenu.DataContext = b.DataContext;
b.ContextMenu.IsOpen = true;
// close context menu after command executed
if (curr_order != null) {
Action command_executed = null;
command_executed = () => {
try {
b.ContextMenu.IsOpen = false;
curr_order.command_executed -= command_executed;
} catch (Exception ex) {
Log.Ui.warn($"Error in {typeof(LadderGridHost)}'s {typeof(Badge)} {nameof(LadderOrderVM.command_executed)} local handler: {ex}");
}
};
curr_order.command_executed += command_executed;
}
// create the new popup window in here
var obvm = order.create_orderbook_vm();
await Task.Delay(100); // Wait for obvm to load rows collection before showing UI to avoid a flash
var view = new MiniOrderBookView(obvm);
var popup = new System.Windows.Controls.Primitives.Popup {
// we only keep the obvm around assigned to the DataContext so that we can
// properly Dispose of it later when the popup is closed.
DataContext = obvm,
Child = view,
StaysOpen = false,
PlacementTarget = popup_placement_target,
Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom,
VerticalOffset = 5
};
popup.SetBinding(System.Windows.Controls.Primitives.Popup.IsOpenProperty,
nameof(OrderBookVM.is_row_collection_populated)
);
popup.Closed += (sender, event_args) => (popup.DataContext as IDisposable)?.Dispose();
b.Popup = popup;
} else if (e.ChangedButton == MouseButton.Left) {
if (curr_order?.cancel_command?.CanExecute(null) == true)
curr_order.cancel_command.Execute(null);
}
};
b.ContextMenuOpening += (s, e) => e.Handled = true;
return b;
}
@@ -8,6 +8,7 @@ using System.Windows;
using bts.proto;
using mbts.ui.spark;
using System.Collections.Generic;
using System.ComponentModel;
namespace mbts.spark {
// IMPORTANT: BE SURE TO ADD ANY NEW PROPERTIES TO THE EQUALS FUNCTION!
@@ -179,6 +180,34 @@ namespace mbts.spark {
// Settings are liable to change in the near future, so an internal IEqualityComparer simply won't do as it risks becoming out of date.
[ProtoContract]
class LadderSettings : IEquatable<LadderSettings> {
const double _default_order_entry_panel_width = 110.0;
const double _default_zoom_factor = 1.0;
// Default proto constructor
// Added this so that when clients upgrade to a version with new settings,
// the default values are applied correctly; thx to John for this tip
public LadderSettings() {
day_visible = true;
high_visible = true;
low_visible = true;
open_visible = true;
settle_visible = true;
volume_visible = true;
account_visible = true;
tif_visible = true;
order_type_visible = true;
account_position_visible = true;
size_presets_visible = true;
cancel_buys_visible = true;
cancel_sells_visible = true;
cancel_all_visible = true;
auto_clear_visible = true;
clear_visible = true;
order_entry_panel_initial_width = _default_order_entry_panel_width;
zoom_factor = _default_zoom_factor;
use_defaults = true;
}
public static LadderSettings Default {
get {
LadderColumnSettings
@@ -216,7 +245,26 @@ namespace mbts.spark {
session_high_line = new LadderMarketBreakLine { color = BTSColors.MarketData.session_high, show = true },
session_range_fill = new LadderMarketBreakLine { color = BTSColors.MarketData.session_range_fill, show = true },
alternate_order_size = 0,
size_display = MarketSizeDisplay.get_default()
size_display = MarketSizeDisplay.get_default(),
day_visible = true,
high_visible = true,
low_visible = true,
open_visible = true,
settle_visible = true,
volume_visible = true,
account_visible = true,
tif_visible = true,
order_type_visible = true,
account_position_visible = true,
size_presets_visible = true,
cancel_buys_visible = true,
cancel_sells_visible = true,
cancel_all_visible = true,
auto_clear_visible = true,
clear_visible = true,
order_entry_panel_initial_width = _default_order_entry_panel_width,
zoom_slider_visible = false,
zoom_factor = _default_zoom_factor,
};
}
}
@@ -278,6 +326,54 @@ namespace mbts.spark {
[ProtoMember(26)]
public MarketSizeDisplay size_display { get; set; }
// order entry panel items show/hide
[ProtoMember(27), DefaultValue(true)]
public bool day_visible { get; set; }
[ProtoMember(28), DefaultValue(true)]
public bool high_visible { get; set; }
[ProtoMember(29), DefaultValue(true)]
public bool low_visible { get; set; }
[ProtoMember(30), DefaultValue(true)]
public bool open_visible { get; set; }
[ProtoMember(31), DefaultValue(true)]
public bool settle_visible { get; set; }
[ProtoMember(32), DefaultValue(true)]
public bool volume_visible { get; set; }
[ProtoMember(33), DefaultValue(true)]
public bool account_visible { get; set; }
[ProtoMember(34), DefaultValue(true)]
public bool tif_visible { get; set; }
[ProtoMember(35), DefaultValue(true)]
public bool order_type_visible { get; set; }
[ProtoMember(36), DefaultValue(true)]
public bool account_position_visible { get; set; }
[ProtoMember(37), DefaultValue(true)]
public bool size_presets_visible { get; set; }
[ProtoMember(38), DefaultValue(true)]
public bool cancel_buys_visible { get; set; }
[ProtoMember(39), DefaultValue(true)]
public bool cancel_sells_visible { get; set; }
[ProtoMember(40), DefaultValue(true)]
public bool cancel_all_visible { get; set; }
[ProtoMember(41), DefaultValue(true)]
public bool auto_clear_visible { get; set; }
[ProtoMember(42), DefaultValue(true)]
public bool clear_visible { get; set; }
//
[ProtoMember(43), DefaultValue(_default_order_entry_panel_width)]
public double order_entry_panel_initial_width { get; set; }
[ProtoMember(44), DefaultValue(true)]
public bool use_defaults { get; set; }
// the zoom slider is the first new widget on the panel
// since our initial general release, so its enablement
// will be opt-in (default not visible)
[ProtoMember(45)]
public bool zoom_slider_visible { get; set; }
[ProtoMember(46), DefaultValue(_default_zoom_factor)]
public double zoom_factor { get; set; }
public bool Equals(LadderSettings other) =>
other != null &&
row_height == other.row_height &&
@@ -302,7 +398,27 @@ namespace mbts.spark {
session_high_line.safe_equals(other.session_high_line) &&
session_range_fill.safe_equals(other.session_range_fill) &&
volume_column.safe_equals(other.volume_column) &&
size_display.safe_equals(other.size_display);
size_display.safe_equals(other.size_display) &&
day_visible == other.day_visible &&
high_visible == other.high_visible &&
low_visible == other.low_visible &&
open_visible == other.open_visible &&
settle_visible == other.settle_visible &&
volume_visible == other.volume_visible &&
account_visible == other.account_visible &&
tif_visible == other.tif_visible &&
order_type_visible == other.order_type_visible &&
account_position_visible == other.account_position_visible &&
size_presets_visible == other.size_presets_visible &&
cancel_buys_visible == other.cancel_buys_visible &&
cancel_sells_visible == other.cancel_sells_visible &&
cancel_all_visible == other.cancel_all_visible &&
auto_clear_visible == other.auto_clear_visible &&
clear_visible == other.clear_visible &&
order_entry_panel_initial_width == other.order_entry_panel_initial_width &&
zoom_slider_visible == other.zoom_slider_visible &&
zoom_factor == other.zoom_factor &&
use_defaults == other.use_defaults;
public override bool Equals(object obj) => Equals(obj as LadderSettings);
@@ -330,7 +446,46 @@ namespace mbts.spark {
EqualityComparer.safe_get_hash_code(session_high_line),
EqualityComparer.safe_get_hash_code(session_range_fill),
EqualityComparer.safe_get_hash_code(volume_column),
EqualityComparer.safe_get_hash_code(size_display));
EqualityComparer.safe_get_hash_code(size_display),
day_visible.GetHashCode(),
high_visible.GetHashCode(),
low_visible.GetHashCode(),
open_visible.GetHashCode(),
settle_visible.GetHashCode(),
volume_visible.GetHashCode(),
account_visible.GetHashCode(),
tif_visible.GetHashCode(),
order_type_visible.GetHashCode(),
account_position_visible.GetHashCode(),
size_presets_visible.GetHashCode(),
cancel_buys_visible.GetHashCode(),
cancel_sells_visible.GetHashCode(),
cancel_all_visible.GetHashCode(),
auto_clear_visible.GetHashCode(),
clear_visible.GetHashCode(),
order_entry_panel_initial_width.GetHashCode(),
zoom_slider_visible.GetHashCode(),
zoom_factor.GetHashCode(),
use_defaults.GetHashCode());
public struct WindowSpecifics {
public readonly double order_entry_panel_width, zoom_factor;
public readonly uint orders_column_width, bid_column_width, price_column_width, ask_column_width, last_column_width, volume_column_width;
public WindowSpecifics(LadderSettings s)
: this(s.order_entry_panel_initial_width, s.orders_column.width, s.bid_column.width, s.price_column.width, s.ask_column.width, s.last_column.width, s.volume_column.general.width, s.zoom_factor) { }
public WindowSpecifics(double order_entry_panel_width, uint orders_column_width, uint bid_column_width, uint price_column_width, uint ask_column_width, uint last_column_width, uint volume_column_width, double zoom_factor) {
this.order_entry_panel_width = order_entry_panel_width;
this.orders_column_width = orders_column_width;
this.bid_column_width = bid_column_width;
this.price_column_width = price_column_width;
this.ask_column_width = ask_column_width;
this.last_column_width = last_column_width;
this.volume_column_width = volume_column_width;
this.zoom_factor = zoom_factor;
}
}
}
static class LadderSettingsExtensions {
@@ -345,5 +500,22 @@ namespace mbts.spark {
public static IEnumerable<LadderColumnSettings> visible_columns(this LadderSettings settings) =>
settings.all_columns().Where(c => c.show);
public static LadderSettings.WindowSpecifics get_window_specifics(this LadderSettings settings) =>
new LadderSettings.WindowSpecifics(settings);
public static LadderSettings with_window_specifics(this LadderSettings source, LadderSettings.WindowSpecifics specifics) {
// must use clone bc we are applying settings that are specific to each individual LadderWindow, and should not be shared among all ladders (even if it is default settings).
source = SparkUtils.clone_proto(source);
source.order_entry_panel_initial_width = specifics.order_entry_panel_width;
source.orders_column.width = specifics.orders_column_width;
source.bid_column.width = specifics.bid_column_width;
source.price_column.width = specifics.price_column_width;
source.ask_column.width = specifics.ask_column_width;
source.last_column.width = specifics.last_column_width;
source.volume_column.general.width = specifics.volume_column_width;
source.zoom_factor = specifics.zoom_factor;
return source;
}
}
}
@@ -17,6 +17,7 @@ namespace mbts.spark {
readonly Property<LadderSettings.BadgeStyle> _badge_style;
readonly Property<uint> _alternate_order_size;
readonly DelegateProperty<double> _row_height;
Func<double> _get_order_entry_panel_width, _get_zoom_factor;
LadderSettings _current;
LadderColumnSettingsVM
_orders = new OrdersColumnSettingsVM(),
@@ -32,14 +33,35 @@ namespace mbts.spark {
_session_low_mkt_line = new LadderMarketBreakLineVM("Session Low", 2, 1),
_session_high_mkt_line = new LadderMarketBreakLineVM("Session High", 2, 1, false),
_session_range_fill = new LadderMarketBreakLineVM("Session Range", 0, 0, false);
OrderEntryPanelItemVisibilityVM
_zoom_slider_visbility = new OrderEntryPanelItemVisibilityVM("Zoom Slider"),
_day_visibility = new OrderEntryPanelItemVisibilityVM("Day Change"),
_high_visibility = new OrderEntryPanelItemVisibilityVM("High"),
_low_visibility = new OrderEntryPanelItemVisibilityVM("Low"),
_open_visibility = new OrderEntryPanelItemVisibilityVM("Open"),
_settle_visibility = new OrderEntryPanelItemVisibilityVM("Settle"),
_volume_visibility = new OrderEntryPanelItemVisibilityVM("Volume"),
_account_visibility = new OrderEntryPanelItemVisibilityVM("Account"),
_tif_visibility = new OrderEntryPanelItemVisibilityVM("Time In Force"),
_order_type_visibility = new OrderEntryPanelItemVisibilityVM("Order Type"),
_account_position_visibility = new OrderEntryPanelItemVisibilityVM("Account Position"),
_size_preset_visibility = new OrderEntryPanelItemVisibilityVM("Size Presets"),
_auto_clear_visibility = new OrderEntryPanelItemVisibilityVM("Auto Clear"),
_clear_visibility = new OrderEntryPanelItemVisibilityVM("Clear"),
_cancel_buys_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Buys"),
_cancel_sells_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Sells"),
_cancel_all_visibility = new OrderEntryPanelItemVisibilityVM("Cancel All");
public LadderSettingsVM(bool is_synthetic) {
public LadderSettingsVM(bool is_synthetic, Func<double> get_left_panel_width, Func<double> get_zoom_factor) {
this.is_synthetic = is_synthetic;
size_presets = new ObservableEditableCollection<IMutableValueWrapperVM<uint>>(_create_size_preset_vm);
size_presets.CollectionChanged += (s, e) => RaisePropertyChanged(nameof(size_presets_text));
_badge_style = backing<LadderSettings.BadgeStyle>(nameof(badge_style));
_badge_style.Changed += _badge_style_Changed;
_alternate_order_size = backing<uint>(nameof(alternate_order_size));
_get_order_entry_panel_width = get_left_panel_width;
_get_zoom_factor = get_zoom_factor;
// be sure to initialize backings before this measure_row_height call
_row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result()));
_orders.font_size_changed += on_font_size_changed;
_bid.font_size_changed += on_font_size_changed;
@@ -108,6 +130,28 @@ namespace mbts.spark {
}
}
public IEnumerable<OrderEntryPanelItemVisibilityVM> order_entry_panel_items {
get {
yield return _zoom_slider_visbility;
yield return _day_visibility;
yield return _high_visibility;
yield return _low_visibility;
yield return _open_visibility;
yield return _settle_visibility;
yield return _volume_visibility;
yield return _account_visibility;
yield return _tif_visibility;
yield return _order_type_visibility;
yield return _account_position_visibility;
yield return _size_preset_visibility;
yield return _clear_visibility;
yield return _auto_clear_visibility;
yield return _cancel_buys_visibility;
yield return _cancel_sells_visibility;
yield return _cancel_all_visibility;
}
}
public string key => GridOrLadderSettingsSelectorKeys.Ladder;
public void load(LadderSettings settings) {
@@ -128,6 +172,27 @@ namespace mbts.spark {
_session_low_mkt_line.assign_from(_current.session_low_line);
_session_high_mkt_line.assign_from(_current.session_high_line);
_session_range_fill.assign_from(_current.session_range_fill);
_zoom_slider_visbility.show = settings.zoom_slider_visible;
_day_visibility.show = settings.day_visible;
_high_visibility.show = settings.high_visible;
_low_visibility.show = settings.low_visible;
_open_visibility.show = settings.open_visible;
_settle_visibility.show = settings.settle_visible;
_volume_visibility.show = settings.volume_visible;
_account_visibility.show = settings.account_visible;
_tif_visibility.show = settings.tif_visible;
_order_type_visibility.show = settings.order_type_visible;
_account_position_visibility.show = settings.account_position_visible;
_size_preset_visibility.show = settings.size_presets_visible;
_clear_visibility.show = settings.clear_visible;
_auto_clear_visibility.show = settings.auto_clear_visible;
_cancel_buys_visibility.show = settings.cancel_buys_visible;
_cancel_sells_visibility.show = settings.cancel_sells_visible;
_cancel_all_visibility.show = settings.cancel_all_visible;
// do not read the order entry panel width here from settings
// because it could have changed from the UI
// there might be a better way of doing this, like how
// does the widths of the ladder grid column pull this off?
}
public LadderSettings get_result() {
@@ -149,6 +214,25 @@ namespace mbts.spark {
clone.session_high_line = _session_high_mkt_line.get_settings();
clone.session_range_fill = _session_range_fill.get_settings();
clone.row_height = LadderRender.measure_row_height(clone);
clone.day_visible = _day_visibility.show;
clone.high_visible = _high_visibility.show;
clone.low_visible = _low_visibility.show;
clone.open_visible = _open_visibility.show;
clone.settle_visible = _settle_visibility.show;
clone.volume_visible = _volume_visibility.show;
clone.account_visible = _account_visibility.show;
clone.tif_visible = _tif_visibility.show;
clone.order_type_visible = _order_type_visibility.show;
clone.account_position_visible = _account_position_visibility.show;
clone.size_presets_visible = _size_preset_visibility.show;
clone.auto_clear_visible = _auto_clear_visibility.show;
clone.clear_visible = _clear_visibility.show;
clone.cancel_buys_visible = _cancel_buys_visibility.show;
clone.cancel_sells_visible = _cancel_sells_visibility.show;
clone.cancel_all_visible = _cancel_all_visibility.show;
clone.order_entry_panel_initial_width = _get_order_entry_panel_width();
clone.zoom_slider_visible = _zoom_slider_visbility.show;
clone.zoom_factor = _get_zoom_factor();
return clone;
}
@@ -276,6 +360,19 @@ namespace mbts.spark {
};
}
public class OrderEntryPanelItemVisibilityVM : ViewModelBase {
readonly Property<bool> _show;
public OrderEntryPanelItemVisibilityVM(string name) {
this.name = name;
_show = backing(nameof(show), true);
}
public string name { get; }
public bool show { get => _show.Value; set => _show.Value = value; }
}
public class VolumeColumnVM : ViewModelBase {
readonly DelegateProperty<bool> _any_visible;
public VolumeColumnVM() {
@@ -140,6 +140,7 @@
<Setter Property="VerticalContentAlignment" Value="Top"/>
</Style>
</TabControl.ItemContainerStyle>
<!-- Fonts and Colors Tab -->
<TabItem Header="Fonts and Colors" VerticalAlignment="Stretch">
<Grid>
<Grid.RowDefinitions>
@@ -232,6 +233,7 @@
</WrapPanel>
</Grid>
</TabItem>
<!-- Market Break Lines Tab -->
<TabItem Header="Market Break Lines" VerticalAlignment="Stretch">
<Grid>
<Grid.RowDefinitions>
@@ -350,6 +352,7 @@
</ItemsControl>
</Grid>
</TabItem>
<!-- Market Size Tab -->
<TabItem Header="Market Size Display">
<Grid DataContext="{Binding size_display}">
<Grid.RowDefinitions>
@@ -440,6 +443,7 @@
</GroupBox>
</Grid>
</TabItem>
<!-- Volume Display Tab -->
<TabItem Header="Volume Display" VerticalAlignment="Stretch" Visibility="{Binding is_synthetic, Converter={StaticResource FalseVisibilityConverter}}">
<Grid>
<Grid.RowDefinitions>
@@ -557,6 +561,7 @@
</WrapPanel>
</Grid>
</TabItem>
<!-- Size Presets Tab-->
<TabItem Header="Size Presets" VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Left">
<Grid.RowDefinitions>
@@ -592,6 +597,63 @@
</Button>
</Grid>
</TabItem>
<!-- Order Entry Panel Item Control Visbility -->
<TabItem Header="Left Panel" VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Left" Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="{Binding Source={StaticResource VerticalPush}}"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" ItemsSource="{Binding order_entry_panel_items}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" Focusable="False"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="A" />
</Grid.ColumnDefinitions>
<Border Margin="0,5,5,0">
<CheckBox Content="{Binding name}" FlowDirection="RightToLeft" IsChecked="{Binding show}">
<CheckBox.Resources>
<Style TargetType="{x:Type Path}">
<Setter Property="FlowDirection" Value="LeftToRight" />
</Style>
</CheckBox.Resources>
</CheckBox>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--
<ItemsControl ItemsSource="{Binding order_entry_panel_items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" Focusable="False"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="52"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" VerticalAlignment="Center" Text="{Binding name}"/>
<CheckBox Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding show}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
-->
</Grid>
</TabItem>
</TabControl>
</Grid>
</UserControl>
+80 -14
View File
@@ -20,18 +20,24 @@ using System.Windows.Media;
namespace mbts.spark {
class LadderVM : DisposableViewModelBase, IWillLetYouKnowWhenIDispose, IKeyListener {
static readonly Brush _transparent_status_brush = BTSColors.UI.transparent;
readonly IBus _bus;
readonly LadderModel _model;
readonly Property<double> _high, _low, _change, _open, _settlement, _position;
readonly Property<double> _high, _low, _change, _open, _settlement, _position, _left_panel_width, _zoom_factor;
//readonly Property<double> _window_width, _window_height;
readonly Property<uint> _orders_column_width, _bid_column_width, _price_column_width, _ask_column_width, _last_column_width, _volume_column_width;
readonly Property<ulong> _volume;
readonly Property<bool> _auto_clear;
readonly Property<bool> _auto_clear, _show_day, _show_high, _show_low, _show_open, _show_settle, _show_volume, _show_account,
_show_tif, _show_order_type, _show_account_position, _show_size_preset, _show_auto_clear, _show_clear, _show_cancel_buys,
_show_cancel_sells, _show_cancel_all, _show_zoom;
readonly Property<Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
IWindowSettingsManager<LadderSettings> _settings_manager;
ulong _misc_time = 0;
IEqualityComparer<double> _equality;
Dictionary<double, LadderOrderVM> _slot_price_vs_orders;
public LadderVM(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager) {
public LadderVM(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager, LadderSettings init) {
_bus = bus;
_model = model;
_equality = TickUtils.tick_equality_comparer(_model.security.ContractSpec);
@@ -44,13 +50,23 @@ namespace mbts.spark {
_change = backing(nameof(change), double.NaN);
_open = backing(nameof(open), double.NaN);
_settlement = backing(nameof(settlement), double.NaN);
// init window-specific settings: these properties should not observe settings updates; they are independent from the settings dialog, so it doesn't make sense that the settings dialog should cause them to change.
var ws = init.get_window_specifics();
_orders_column_width = backing(nameof(orders_column_width), ws.orders_column_width);
_bid_column_width = backing(nameof(bid_column_width), ws.bid_column_width);
_price_column_width = backing(nameof(price_column_width), ws.price_column_width);
_ask_column_width = backing(nameof(ask_column_width), ws.ask_column_width);
_last_column_width = backing(nameof(last_column_width), ws.last_column_width);
_volume_column_width = backing(nameof(volume_column_width), ws.volume_column_width);
_left_panel_width = backing(nameof(left_panel_width), ws.order_entry_panel_width);
_zoom_factor = backing(nameof(zoom_factor), ws.zoom_factor);
// remember: account_positions needs to subscribe to selected_account; both the sub and account_positions need to be disposed
var account_positions = new KeyDrivenObservableSeries<string, double>(a => _model.order_gateway.security_total_position(security.Id, a));
_position = backing(nameof(position), account_positions.OnDispatcher());
_volume = backing(nameof(volume), 0ul);
_auto_clear = backing(nameof(auto_clear), false);
_status = backing(nameof(status_message), "");
_status_brush = backing(nameof(status_background), (Brush)BTSColors.UI.spark_menu_background);
_status_brush = backing(nameof(status_background), _transparent_status_brush);
// brush props
_bid_background = brush_prop(nameof(bid_background), s => s.bid_column.background.rgb_brush());
_bid_foreground = brush_prop(nameof(bid_foreground), s => s.bid_column.foreground.rgb_brush());
@@ -58,7 +74,31 @@ namespace mbts.spark {
_ask_foreground = brush_prop(nameof(ask_foreground), s => s.ask_column.foreground.rgb_brush());
Property<Brush> brush_prop(string name, Func<LadderSettings, Brush> get_brush) =>
backing(name, settings_manager.subscribe_current().OnDispatcher(), get_brush);
// bool visibility props
_show_day = bool_prop(nameof(show_day), s => s.day_visible);
_show_high = bool_prop(nameof(show_high), s => s.high_visible);
_show_low = bool_prop(nameof(show_low), s => s.low_visible);
_show_open = bool_prop(nameof(show_open), s => s.open_visible);
_show_settle = bool_prop(nameof(show_settle), s => s.settle_visible);
_show_volume = bool_prop(nameof(show_volume), s => s.volume_visible);
_show_account = bool_prop(nameof(show_account), s => s.account_visible);
_show_tif = bool_prop(nameof(show_tif), s => s.tif_visible);
_show_order_type = bool_prop(nameof(show_order_type), s => s.order_type_visible);
_show_account_position = bool_prop(nameof(show_account_position), s => s.account_position_visible);
_show_size_preset = bool_prop(nameof(show_size_preset), s => s.size_presets_visible);
_show_auto_clear = bool_prop(nameof(show_auto_clear), s => s.auto_clear_visible);
_show_clear = bool_prop(nameof(show_clear), s => s.clear_visible);
_show_cancel_buys = bool_prop(nameof(show_cancel_buys), s => s.cancel_buys_visible);
_show_cancel_sells = bool_prop(nameof(show_cancel_sells), s => s.cancel_sells_visible);
_show_cancel_all = bool_prop(nameof(show_cancel_all), s => s.cancel_all_visible);
_show_zoom = bool_prop(nameof(show_zoom), s => s.zoom_slider_visible);
Property<bool> bool_prop(string name, Func<LadderSettings, bool> get_bool) =>
backing(name, settings_manager.subscribe_current().OnDispatcher(), get_bool);
//
//_window_height = backing(nameof(window_height), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 600);
//_window_width = backing(nameof(window_width), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 420);
position_click_command = new DelegateCommand(() => size_calculator.value = DoubleHelper.safe_convert_to_uint(Math.Abs(position)));
send_order_command = new DelegateCommand<(double price, Side? side, bool size_from_alternate)>(send_order);
cancel_command = new AsyncCommand<LadderModel.CancelType>(_model.cancel_orders, true);
@@ -67,11 +107,11 @@ namespace mbts.spark {
settings_command = new DialogCommand(() => {
var vm = new WindowSettingsDialogVM<LadderSettings, LadderSettingsVM>(
settings_manager, s => s.ladder, UserSettings.FileExtensions.LADDER,
s => {
var svm = new LadderSettingsVM(security.ContractCase == Security.ContractOneofCase.Synthetic);
s => {
var svm = new LadderSettingsVM(security.ContractCase == Security.ContractOneofCase.Synthetic, () => left_panel_width, () => zoom_factor);
svm.load(s);
return svm;
});
}, s => s.with_window_specifics(get_save_state().get_window_specifics()));
var dialog = new ui.spark.SimpleGridSettingsDialog {
DataContext = vm,
Title = $"Ladder Settings - '{security}'"
@@ -202,7 +242,7 @@ namespace mbts.spark {
await Task.Delay(TimeSpan.FromSeconds(5));
if (_status.Value == a.message) {
_status.set("");
_status_brush.set(BTSColors.UI.spark_menu_background);
_status_brush.set(_transparent_status_brush);
}
}
}
@@ -233,16 +273,41 @@ namespace mbts.spark {
public Brush ask_background => _ask_background.Value;
public Brush ask_foreground => _ask_foreground.Value;
public DialogCommand settings_command { get; }
public bool show_day { get => _show_day.Value; set => _show_day.set(value); }
public bool show_high { get => _show_high.Value; set => _show_high.set(value); }
public bool show_low { get => _show_low.Value; set => _show_low.set(value); }
public bool show_open { get => _show_open.Value; set => _show_open.set(value); }
public bool show_settle { get => _show_settle.Value; set => _show_settle.set(value); }
public bool show_volume { get => _show_volume.Value; set => _show_volume.set(value); }
public bool show_account { get => _show_account.Value; set => _show_account.set(value); }
public bool show_tif { get => _show_tif.Value; set => _show_tif.set(value); }
public bool show_order_type { get => _show_order_type.Value; set => _show_order_type.set(value); }
public bool show_account_position { get => _show_account_position.Value; set => _show_account_position.set(value); }
public bool show_size_preset { get => _show_size_preset.Value; set => _show_size_preset.set(value); }
public bool show_auto_clear { get => _show_auto_clear.Value; set => _show_auto_clear.set(value); }
public bool show_clear { get => _show_clear.Value; set => _show_clear.set(value); }
public bool show_cancel_buys { get => _show_cancel_buys.Value; set => _show_cancel_buys.set(value); }
public bool show_cancel_sells { get => _show_cancel_sells.Value; set => _show_cancel_sells.set(value); }
public bool show_cancel_all { get => _show_cancel_all.Value; set => _show_cancel_all.set(value); }
public bool show_zoom { get => _show_zoom.Value; set => _show_zoom.set(value); }
public double left_panel_width { get => _left_panel_width.Value; set => _left_panel_width.set(value); }
public double zoom_factor { get => _zoom_factor.Value; set => _zoom_factor.set(value); }
//public double window_height { get => _window_height.Value; set => _window_height.set(value); }
//public double window_width { get => _window_width.Value; set => _window_width.set(value); }
public uint orders_column_width { get => _orders_column_width.Value; set => _orders_column_width.set(value); }
public uint bid_column_width { get => _bid_column_width.Value; set => _bid_column_width.set(value); }
public uint price_column_width { get => _price_column_width.Value; set => _price_column_width.set(value); }
public uint ask_column_width { get => _ask_column_width.Value; set => _ask_column_width.set(value); }
public uint last_column_width { get => _last_column_width.Value; set => _last_column_width.set(value); }
public uint volume_column_width { get => _volume_column_width.Value; set => _volume_column_width.set(value); }
internal LadderModel model => _model;
internal IObservable<LadderSettings> settings => _settings_manager.subscribe_current();
public LadderSettings get_save_state() {
// TODO: save an actual OrderDefinition to preserve order params.
if (_settings_manager.instance_settings == null)
return null;
var settings_clone = SparkUtils.clone_proto(_settings_manager.instance_settings);
settings_clone.default_order_type = order_properties_vm.selected_order_type?.order_type ?? settings_clone.default_order_type;
return settings_clone;
var state = _settings_manager.current_settings();
state = state.with_window_specifics(new LadderSettings.WindowSpecifics(left_panel_width, orders_column_width, bid_column_width, price_column_width, ask_column_width, last_column_width, volume_column_width, zoom_factor));
state.use_defaults = _settings_manager.instance_settings == null;
return state;
}
public void key_down(KeyEventArgs e) {
@@ -299,6 +364,7 @@ namespace mbts.spark {
_change.set(mid - md.settlement_price);
}
_volume.set(volume);
_misc_time = _model.md.misc_timestamp;
}
}
+266 -181
View File
@@ -1,16 +1,20 @@
<UserControl x:Class="mbts.spark.LadderView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mbts="clr-namespace:mbts.ui"
xmlns:controls="clr-namespace:mbts.ui.controls"
xmlns:spark="clr-namespace:mbts.spark"
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
xmlns:converters="clr-namespace:mbts.ui.converters"
xmlns:txt="clr-namespace:mbts.ui.utility.text"
Height="600"
Width="420"
Background="{x:Static mbts:BTSColors.common_background_brush}"
TextElement.FontSize="{x:Static mbts:FontSizes.Small}">
<UserControl
x:Class="mbts.spark.LadderView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mbts="clr-namespace:mbts.ui"
xmlns:controls="clr-namespace:mbts.ui.controls"
xmlns:spark="clr-namespace:mbts.spark"
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
xmlns:converters="clr-namespace:mbts.ui.converters"
xmlns:txt="clr-namespace:mbts.ui.utility.text"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Height="600"
Width="420"
Background="{x:Static mbts:BTSColors.common_background_brush}"
TextElement.FontSize="{x:Static mbts:FontSizes.Small}"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
@@ -27,6 +31,13 @@
</converters:BooleanToObjectConverter>
<converters:EnumCharacterCasingConverter x:Key="casing_converter"/>
<converters:DoubleTextConverter x:Key="PositionTextConverter" Format="0" NaNText="-"/>
<converters:GridLengthConverter x:Key="GridLengthPixelConverter" UnitType="Pixel" />
<converters:ResponsiveTextConverter x:Key="DayTextConverter" LongText="Day:" ShortText="D:" Threshold="99" />
<converters:ResponsiveTextConverter x:Key="HighTextConverter" LongText="High:" ShortText="H:" Threshold="99" />
<converters:ResponsiveTextConverter x:Key="LowTextConverter" LongText="Low:" ShortText="L:" Threshold="99" />
<converters:ResponsiveTextConverter x:Key="OpenTextConverter" LongText="Open:" ShortText="O:" Threshold="99" />
<converters:ResponsiveTextConverter x:Key="SettleTextConverter" LongText="Settle:" ShortText="S:" Threshold="99" />
<converters:ResponsiveTextConverter x:Key="VolumeTextConverter" LongText="Volume:" ShortText="V:" Threshold="99" />
<Style TargetType="GridViewColumnHeader">
<Setter Property="Background" Value="{mbts:BTSColor header}"/>
<Setter Property="Foreground" Value="{mbts:BTSColor header_text}"/>
@@ -34,6 +45,7 @@
<Setter Property="BorderThickness" Value="0,0,1,0"/>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Height" Value="23" />
<Setter Property="MinWidth" Value="30" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
@@ -57,8 +69,8 @@
</Border>
</Grid>
</Border>
<Canvas>
<Thumb x:Name="PART_HeaderGripper">
<Canvas MinWidth="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MinWidth, Mode=OneTime}">
<Thumb x:Name="PART_HeaderGripper" DragDelta="Thumb_DragDelta">
<Thumb.Style>
<Style TargetType="{x:Type Thumb}">
<Setter Property="Canvas.Right" Value="-9"/>
@@ -123,7 +135,7 @@
<Style x:Key="LadderMenuButton" TargetType="Button">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="5,0,5,5"/>
<Setter Property="Margin" Value="2,0,2,5"/>
<Setter Property="Padding" Value="2"/>
</Style>
<Style x:Key="CancelBuys" TargetType="Button" BasedOn="{StaticResource LadderMenuButton}">
@@ -244,8 +256,7 @@
Background="{mbts:BTSColor UI.Button.normal_background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True"
VerticalAlignment="Bottom"
Margin="0,0,0,12">
Margin="2,0,2,10">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
@@ -263,108 +274,166 @@
</Setter.Value>
</Setter>
</Style>
<sys:Double x:Key="MaxZoomFactor">1.5</sys:Double>
<sys:Double x:Key="MinZoomFactor">0.5</sys:Double>
</ResourceDictionary>
</UserControl.Resources>
<AdornerDecorator>
<Grid x:Name="_root">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="112"/>
<ColumnDefinition Width="2"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid RowSpan="2" Background="{mbts:BTSColor UI.spark_menu_background}">
<Grid.LayoutTransform>
<ScaleTransform CenterX="0" CenterY="0"
ScaleX="{Binding ElementName=_ui_scaler, Path=Value}"
ScaleY="{Binding ElementName=_ui_scaler, Path=Value}"
/>
</Grid.LayoutTransform>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding left_panel_width, Mode=TwoWay, Converter={StaticResource GridLengthPixelConverter}}"
MinWidth="60" MaxWidth="150"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Margin="5,0">
<StackPanel.Resources>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</StackPanel.Resources>
<Grid Margin="8,4,0,0">
<ScrollViewer Grid.Row="0" Grid.RowSpan="2" VerticalScrollBarVisibility="Hidden">
<Grid RowSpan="2" Background="{mbts:BTSColor UI.spark_menu_background}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Day"/>
<TextBlock Grid.Column="0" Grid.Row="2" Text="High"/>
<TextBlock Grid.Column="0" Grid.Row="4" Text="Low"/>
<TextBlock Grid.Column="0" Grid.Row="6" Text="Open"/>
<TextBlock Grid.Column="0" Grid.Row="8" Text="Settle"/>
<TextBlock Grid.Column="0" Grid.Row="10" Text="Volume"/>
<TextBlock Grid.Column="2" Grid.Row="0" Foreground="{Binding change, Converter={StaticResource NumericSignBrushConverter}}" FontWeight="Bold">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="change"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="2" Foreground="{mbts:BTSColor UI.default_text}" FontWeight="Bold">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="high"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="4" Foreground="{mbts:BTSColor UI.default_text}" FontWeight="Bold">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="low"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="6" FontWeight="Bold">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="open"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="8" FontWeight="Bold">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="settlement"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="2" Grid.Row="10" Text="{Binding volume, Mode=OneWay}" FontWeight="Bold" />
</Grid>
<ComboBox DataContext="{Binding order_properties_vm}" Margin="2,10,2,2" ItemsSource="{Binding accounts}" SelectedItem="{Binding selected_account}"/>
<ComboBox DataContext="{Binding order_properties_vm}" ItemsSource="{Binding time_in_forces}" SelectedItem="{Binding selected_time_in_force}" Margin="2">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource casing_converter}, ConverterParameter=Upper}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<uag:OrderDetailsPopupControl
<StackPanel Margin="2,0,5,0">
<StackPanel.Resources>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</StackPanel.Resources>
<StackPanel x:Name="_slider_stack_panel" Margin="5,5,5,0">
<Slider x:Name="_ui_scaler" Value="{Binding zoom_factor}"
Minimum="{StaticResource MinZoomFactor}"
Maximum="{StaticResource MaxZoomFactor}"
Width="{Binding Path=ActualWidth, ElementName=_slider_stack_panel}"
Visibility="{Binding show_zoom, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
<Grid Margin="2,4,2,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="6"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Day -->
<TextBlock Grid.Column="0" Grid.Row="0"
Text="{Binding left_panel_width, Converter={StaticResource DayTextConverter}}"
Margin="0,0,0,4"
Visibility="{Binding show_day, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Grid.Column="2" Grid.Row="0" FontWeight="Bold"
Margin="0,0,0,4"
Foreground="{Binding change, Converter={StaticResource NumericSignBrushConverter}}"
Visibility="{Binding show_day, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="change"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<!-- High -->
<TextBlock Grid.Column="0" Grid.Row="1"
Text="{Binding left_panel_width, Converter={StaticResource HighTextConverter}}"
Margin="0,0,0,4"
Visibility="{Binding show_high, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Grid.Column="2" Grid.Row="1" Foreground="{mbts:BTSColor UI.default_text}" FontWeight="Bold"
Margin="0,0,0,4"
Visibility="{Binding show_high, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="high"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<!-- Low -->
<TextBlock Grid.Column="0" Grid.Row="2"
Text="{Binding left_panel_width, Converter={StaticResource LowTextConverter}}"
Margin="0,0,0,4"
Visibility="{Binding show_low, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Grid.Column="2" Grid.Row="2" Foreground="{mbts:BTSColor UI.default_text}" FontWeight="Bold"
Margin="0,0,0,4"
Visibility="{Binding show_low, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="low"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<!-- Open -->
<TextBlock Grid.Column="0" Grid.Row="3"
Text="{Binding left_panel_width, Converter={StaticResource OpenTextConverter}}"
Margin="0,0,0,4"
Visibility="{Binding show_open, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Grid.Column="2" Grid.Row="3" FontWeight="Bold"
Margin="0,0,0,4"
Visibility="{Binding show_open, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="open"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<!-- Settle -->
<TextBlock Grid.Column="0" Grid.Row="4"
Text="{Binding left_panel_width, Converter={StaticResource SettleTextConverter}}"
Margin="0,0,0,4"
Visibility="{Binding show_settle, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Grid.Column="2" Grid.Row="4" FontWeight="Bold"
Margin="0,0,0,4"
Visibility="{Binding show_settle, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="settlement"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<!-- Volume -->
<TextBlock Grid.Column="0" Grid.Row="5"
Text="{Binding left_panel_width, Converter={StaticResource VolumeTextConverter}}"
Margin="0,0,0,4"
Visibility="{Binding show_volume, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Grid.Column="2" Grid.Row="5" Text="{Binding volume, Mode=OneWay}" FontWeight="Bold"
Margin="0,0,0,4"
Visibility="{Binding show_volume, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
<!-- Account combobox -->
<ComboBox DataContext="{Binding order_properties_vm}" Margin="2,10,2,2"
ItemsSource="{Binding accounts}" SelectedItem="{Binding selected_account}"
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path = DataContext.show_account,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
<!-- TIF combobox-->
<ComboBox DataContext="{Binding order_properties_vm}" ItemsSource="{Binding time_in_forces}"
SelectedItem="{Binding selected_time_in_force}" Margin="2"
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path = DataContext.show_tif,
Converter={StaticResource BooleanToVisibilityConverter}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource casing_converter}, ConverterParameter=Upper}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Order Type combobox -->
<uag:OrderDetailsPopupControl
DataContext="{Binding order_properties_vm}"
Margin="2"
ExpandDirection="Left"
@@ -372,19 +441,25 @@
IsOpen="{Binding selected_order_type.editable_properties_open, FallbackValue=False}"
ItemsSource="{Binding order_types}"
SelectedItem="{Binding selected_order_type}"
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path = DataContext.show_order_type,
Converter={StaticResource BooleanToVisibilityConverter}}"
/>
<Button
<!-- Position into qty button -->
<Button
Margin="2"
Command="{Binding position_click_command}"
Content="{Binding position, Converter={StaticResource PositionTextConverter}}"
FontWeight="Bold"
Foreground="{Binding position, Converter={StaticResource NumericSignBrushConverter}}"
Visibility="{Binding show_account_position, Converter={StaticResource BooleanToVisibilityConverter}}"
>
<Button.ToolTip>
<TextBlock Text="{Binding order_properties_vm.selected_account, StringFormat='{}{0} total position (click to copy to size)'}"/>
</Button.ToolTip>
</Button>
<TextBox
<Button.ToolTip>
<TextBlock Text="{Binding order_properties_vm.selected_account, StringFormat='{}{0} total position (click to copy to size)'}"/>
</Button.ToolTip>
</Button>
<!-- Qty textbox -->
<TextBox
mbts:TextBoxBehavior.InputFilter="{mbts:RegexInputFilter Regex={x:Static Member=Integer, MemberType={mbts:Nested txt:TextUtils.RegexFilters}}}"
mbts:TextBoxBehavior.IncrementDecrementOnMouseClick="True"
HorizontalContentAlignment="Right"
@@ -392,42 +467,64 @@
Text="{Binding size_calculator.value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ContextMenu="{x:Null}"
/>
<ItemsControl ItemsSource="{Binding size_calculator.buttons}" Focusable="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" Focusable="False"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="2" Command="{Binding Path=DataContext.size_calculator.click, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}" Content="{Binding}" Focusable="False"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Margin="2" HorizontalAlignment="Stretch" Command="{Binding size_calculator.clear}">Clear</Button>
<!-- reset to defaults after order -->
<CheckBox Margin="2" IsChecked="{Binding auto_clear}">Auto Clear</CheckBox>
</StackPanel>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Margin="0,17,0,0" VerticalAlignment="Top">
<Button Command="{Binding cancel_command}" CommandParameter="Bids" Style="{StaticResource CancelBuys}"/>
<Button Command="{Binding cancel_command}" CommandParameter="Asks" Style="{StaticResource CancelSells}"/>
<Button Command="{Binding cancel_command}" CommandParameter="All" Style="{StaticResource CancelAll}"/>
</StackPanel>
<Button Grid.Row="1" HorizontalContentAlignment="Stretch" Command="{Binding settings_command}" Style="{StaticResource SettingsButton}" />
</Grid>
</Grid>
<Border Grid.Column="2" HorizontalAlignment="Stretch">
<GridViewHeaderRowPresenter x:Name="_column_headers_presenter" AllowsColumnReorder="False"/>
</Border>
<spark:LadderGridHost x:Name="_ladder" Grid.Column="2" Grid.Row="1"
Command="{Binding send_order_command}"
Orders="{Binding orders}">
<spark:LadderGridHost.Resources>
<!-- Qty quick buttons -->
<ItemsControl ItemsSource="{Binding size_calculator.buttons}" Focusable="False"
Visibility="{Binding show_size_preset, Converter={StaticResource BooleanToVisibilityConverter}}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" Focusable="False"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="2" Command="{Binding Path=DataContext.size_calculator.click, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}" Content="{Binding}" Focusable="False"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Clear button -->
<Button Margin="2" HorizontalAlignment="Stretch" Command="{Binding size_calculator.clear}"
Visibility="{Binding show_clear, Converter={StaticResource BooleanToVisibilityConverter}}">Clear</Button>
<!-- Auto Clear button - reset to defaults after order -->
<CheckBox Margin="2" IsChecked="{Binding auto_clear}"
Visibility="{Binding show_auto_clear, Converter={StaticResource BooleanToVisibilityConverter}}">
Auto Clear
</CheckBox>
<StackPanel Margin="0,17,0,0" VerticalAlignment="Top">
<!-- Cancel Buttons -->
<Button Command="{Binding cancel_command}" CommandParameter="Bids" Style="{StaticResource CancelBuys}"
Visibility="{Binding show_cancel_buys, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button Command="{Binding cancel_command}" CommandParameter="Asks" Style="{StaticResource CancelSells}"
Visibility="{Binding show_cancel_sells, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button Command="{Binding cancel_command}" CommandParameter="All" Style="{StaticResource CancelAll}"
Visibility="{Binding show_cancel_all, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
<StackPanel Grid.Row="1" Margin="0,17,0,0" VerticalAlignment="Bottom">
<!-- Settings Button -->
<Button Command="{Binding settings_command}" Style="{StaticResource SettingsButton}"/>
</StackPanel>
</StackPanel>
</Grid>
</ScrollViewer>
<!-- Ladder Column Headers -->
<Border Grid.Column="2" HorizontalAlignment="Stretch">
<GridViewHeaderRowPresenter x:Name="_column_headers_presenter" AllowsColumnReorder="False" />
</Border>
<!-- LadderGridHost -->
<spark:LadderGridHost
x:Name="_ladder"
Grid.Column="2"
Grid.Row="1"
Command="{Binding send_order_command}"
Orders="{Binding orders}"
orders_column_width="{Binding orders_column_width, Mode=TwoWay}"
bid_column_width="{Binding bid_column_width, Mode=TwoWay}"
price_column_width="{Binding price_column_width, Mode=TwoWay}"
ask_column_width="{Binding ask_column_width, Mode=TwoWay}"
LastColumnWidth="{Binding last_column_width, Mode=TwoWay}"
VolumeColumnWidth="{Binding volume_column_width, Mode=TwoWay}"
>
<spark:LadderGridHost.Resources>
<Style TargetType="controls:Badge">
<Setter Property="Background" Value="{Binding background}" />
<Setter Property="BorderBrush" Value="{Binding border}" />
@@ -436,29 +533,12 @@
<Setter Property="Foreground" Value="{Binding foreground}"/>
<Setter Property="TextLines" Value="{Binding text_lines}" />
<Setter Property="Width" Value="{x:Static controls:Badge.DefaultWidth}"/>
<Setter Property="ToolTip" Value="{Binding description}" />
<Setter Property="IsExchangeOrder" Value="{Binding is_exchange_order}" />
<Setter Property="HasMultipleOrdersAtLevel" Value="{Binding has_multiple_orders_at_same_level}" />
<Setter Property="IsHedgeOrder" Value="{Binding is_hedge_order}" />
<Setter Property="ParentAlgoType" Value="{Binding parent_algo_type}" />
<Setter Property="ParentAlgoTypeBrush" Value="{Binding parent_algo_type_brush}" />
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding cancel_command}" Header="Cancel">
<MenuItem.Icon>
<Image Source="{StaticResource CancelSelected}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Command="{Binding detach_command}" Header="Detach">
<MenuItem.Icon>
<Image Source="{StaticResource ModifyOrder}" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding hoverbackground}" />
@@ -467,7 +547,7 @@
</Style>
</spark:LadderGridHost.Resources>
</spark:LadderGridHost>
<spark:RecenterCountdown Grid.Column="2" Grid.Row="1" Margin="0,0,18,0"
<spark:RecenterCountdown Grid.Column="2" Grid.Row="1" Margin="0,0,18,0"
VerticalAlignment="Bottom" HorizontalAlignment="Right"
Width="30" Height="30"
Mode="Pie" Delay="0:0:10"
@@ -483,14 +563,19 @@
<Pen Thickness="0" Brush="MediumPurple" />
</spark:RecenterCountdown.PieStroke>
</spark:RecenterCountdown>
<Button x:Name="_status" Grid.ColumnSpan="4" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Command="{Binding status_click_command}">
<Button.Template>
<ControlTemplate>
<TextBlock Text="{Binding status_message}" Background="{Binding status_background}" Padding="5,0" />
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
<!-- Since the GridSplitter will share a cell with other elements,
we add it in toward the end, but we still want the ladder status
message to have the topmost Z order
-->
<GridSplitter Grid.Column="0" Grid.RowSpan="2" Width="5" />
<!-- Ladder Status message -->
<Button x:Name="_status" Grid.ColumnSpan="4" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Command="{Binding status_click_command}">
<Button.Template>
<ControlTemplate>
<TextBlock Text="{Binding status_message}" Background="{Binding status_background}" Padding="5,0" />
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -3,10 +3,14 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
namespace mbts.spark {
public partial class LadderView : UserControl {
partial class LadderView : UserControl {
readonly double _max_zoom, _min_zoom;
internal LadderView(LadderVM vm) {
InitializeComponent();
_column_headers_presenter.Columns = new GridViewColumnCollection();
@@ -15,12 +19,87 @@ namespace mbts.spark {
this.vm = vm;
DataContext = vm;
DataContextChanged += datacontext_changed;
if (TryFindResource("MaxZoomFactor") is double found_max) {
_max_zoom = found_max;
} else {
// is there a better way of not specifying the constant twice ?
// once in the resource dictionary here and again in the xaml, seems like there has to be a better way
_max_zoom = 1.5;
}
// ditto
if (TryFindResource("MinZoomFactor") is double found_min) {
_min_zoom = found_min;
} else {
_min_zoom = 0.5;
}
_ui_scaler.MouseDoubleClick += _ui_scaler_MouseDoubleClick;
this.PreviewMouseWheel += LadderView_PreviewMouseWheel;
this.PreviewMouseDown += LadderView_PreviewMouseDown;
}
// (Left|Right) CTRL + Wheel to zoom
// This is neat b/c the user doesn't even need to have the slider visible to zoom
private void LadderView_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) {
base.OnPreviewMouseWheel(e);
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) {
var delta = (e.Delta > 0) ? 0.1 : -0.1;
var proposed_scaler_value = _ui_scaler.Value + delta;
// Here we enforce the same contraints that are on the gui slider regarding min and max
if (_min_zoom < proposed_scaler_value && proposed_scaler_value < _max_zoom) {
_ui_scaler.Value = proposed_scaler_value;
}
}
}
// (Left|Right) CTRL + Wheel Click to reset
private void LadderView_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
base.OnPreviewMouseDown(e);
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) {
if (e.MiddleButton == MouseButtonState.Pressed) {
_ui_scaler_MouseDoubleClick(_ui_scaler, e);
}
}
}
// Double click on scaler itself to reset
private void _ui_scaler_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) {
if (sender is Slider _the_slider) {
_the_slider.Value = 1.0;
}
}
private void datacontext_changed(object sender, DependencyPropertyChangedEventArgs e) {
this.vm = DataContext as LadderVM;
}
// I found this on StackOverflow (and the XAML) and thought it was a neat
// way of enforcing a MinWidth on the columns. This is so that the columns
// cannot be resized to nothing with the mouse.
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) {
if (sender != null) {
DependencyObject parent = sender as DependencyObject;
if (parent != null) {
try {
do {
parent = System.Windows.Media.VisualTreeHelper.GetParent(parent as DependencyObject);
} while (parent.GetType() != typeof(Canvas));
Canvas canvas = parent as Canvas;
if (canvas != null) {
if (canvas.ActualWidth + e.HorizontalChange < canvas.MinWidth) {
e.Handled = true;
}
}
} catch {
}
}
}
}
internal LadderVM vm { get; private set; }
protected override void OnDragOver(DragEventArgs e) {
@@ -40,31 +119,31 @@ namespace mbts.spark {
switch (type) {
case LadderGridHost.ColumnType.Order:
header = "Orders";
width_prop = nameof(LadderGridHost.orders_column_width);
width_prop = nameof(LadderVM.orders_column_width);
break;
case LadderGridHost.ColumnType.Bid:
header = "Bid";
width_prop = nameof(LadderGridHost.bid_column_width);
width_prop = nameof(LadderVM.bid_column_width);
break;
case LadderGridHost.ColumnType.Price:
header = "Price";
width_prop = nameof(LadderGridHost.price_column_width);
width_prop = nameof(LadderVM.price_column_width);
break;
case LadderGridHost.ColumnType.Ask:
header = "Ask";
width_prop = nameof(LadderGridHost.ask_column_width);
width_prop = nameof(LadderVM.ask_column_width);
break;
case LadderGridHost.ColumnType.Volume:
header = "Volume";
width_prop = nameof(LadderGridHost.VolumeColumnWidth);
width_prop = nameof(LadderVM.volume_column_width);
break;
case LadderGridHost.ColumnType.Last:
header = "Last";
width_prop = nameof(LadderGridHost.LastColumnWidth);
width_prop = nameof(LadderVM.last_column_width);
break;
default: throw ExceptionBuilder.Argument.unexpected_case(type, nameof(type));
}
return _create(host, header, width_prop);
return _create(host, header, $"DataContext.{width_prop}");
}
static GridViewColumn _create(LadderGridHost host, string header, string width_prop) {
@@ -77,5 +156,6 @@ namespace mbts.spark {
return gvc;
}
}
}
}
@@ -28,31 +28,10 @@ namespace mbts.spark {
static readonly int sod_hh = (int)config.Configuration.get<bts.config.SODTime>().sod_hh;
static readonly int sod_mm = (int)config.Configuration.get<bts.config.SODTime>().sod_mm;
static readonly TimeZoneInfo central_tzi = TimeZoneInfo.FindSystemTimeZoneById(cst_id);
static readonly DateTime _start_of_trading_day_utc;
static readonly DateTime _adjusted_central_sod;
static readonly DateTime unadjusted_central_sod = TimeZoneInfo.ConvertTimeFromUtc(
DateTime.UtcNow.Date.Add(new TimeSpan(sod_hh, sod_mm, 0)),
central_tzi
);
static MarketSchedule() {
var cme_schedule = config.Configuration.get<bts.md.CMETradingSchedule>();
_cme_gen_open_hrs = cme_schedule?.gen_open_hour ?? 8.5;
_adjusted_central_sod = !central_tzi.IsDaylightSavingTime(unadjusted_central_sod) ? unadjusted_central_sod.AddHours(1) : unadjusted_central_sod;
// Note that this isn't the acutal start of the trading day
// but it's close enough for pnl purposes.
// In reality, instruments have different schedules
// if now is less than the current sod, then we me move the sod back a day to account for the trading day start
if (_adjusted_central_sod >= TimeZoneInfo.ConvertTime(DateTime.Now, central_tzi)) {
_adjusted_central_sod = _adjusted_central_sod.AddDays(-1);
}
var local_time_zone_sod = TimeZoneInfo.ConvertTime(_adjusted_central_sod, central_tzi, TimeZoneInfo.Local);
_start_of_trading_day_utc = local_time_zone_sod.ToUniversalTime();
}
// for object initializers
public MarketSchedule() { }
@@ -73,6 +52,27 @@ namespace mbts.spark {
return last_open;
}
// DateTime? used here b/c we can't set a default parameter with DateTime.Now
// since it's not a compile-time constant
public static DateTime get_start_of_trading_day(DateTime? target_datetime = null, DateTimeKind dtk = DateTimeKind.Local ) {
if (target_datetime == null) target_datetime = DateTime.Now;
var dst_adjusted_central_sod = new DateTimeOffset(
new DateTime(target_datetime.Value.Date.Add(new TimeSpan(sod_hh, sod_mm, 0)).Ticks, DateTimeKind.Unspecified),
central_tzi.BaseUtcOffset
);
// if we're not currently past the start of the trading day, then the start must be yesterday
var temp = dst_adjusted_central_sod;
if (temp >= TimeZoneInfo.ConvertTime(target_datetime.Value, central_tzi)) {
temp = dst_adjusted_central_sod.AddDays(-1);
}
var local_time_zone_sod = TimeZoneInfo.ConvertTime(temp.DateTime, central_tzi, TimeZoneInfo.Local);
if (dtk == DateTimeKind.Utc) {
return local_time_zone_sod.ToUniversalTime();
}
return local_time_zone_sod;
}
public static DateTime get_utctimestamp_as_localtime(string s) {
return DateTimeOffset.ParseExact(s, "yyyyMMddHHmmssffffff", null, System.Globalization.DateTimeStyles.AssumeUniversal).ToLocalTime().LocalDateTime;
}
@@ -15,7 +15,7 @@ namespace mbts.spark {
class PnLSummaryRow : ViewModelBase, IEquatable<PnLSummaryRow>, DIYGrid.IRowVM {
static readonly SignedDataStore<Brush> _brushes;
readonly PnLDoubleBrushCell _start_pnl, _day_pnl, _total_pnl;
readonly PnLPriceCell _pnl_price, _weighted_average_buy, _weighted_average_sell;
readonly CellProperty<double> _pnl_price, _weighted_average_buy, _weighted_average_sell;
(string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md, SecurityType? sec_type) key;
IEnumerable<FillImmutable> _fills;
double _driver;
@@ -38,9 +38,9 @@ namespace mbts.spark {
day_short_quantity = day_sell_quant;
total_net_position = net_pos;
start_net_position = sod_pos;
_pnl_price = new PnLPriceCell(this, nameof(pnl_price));
_weighted_average_buy = new PnLPriceCell(this, nameof(weighted_average_buy), wavg_buy);
_weighted_average_sell = new PnLPriceCell(this, nameof(weighted_average_sell), wavg_sell);
_pnl_price = new CellProperty<double>(this, nameof(pnl_price), price => this.security.spec_or_default().format(price), 0);
_weighted_average_buy = new CellProperty<double>(this, nameof(weighted_average_buy), price => this.security.spec_or_default().format(price), wavg_buy);
_weighted_average_sell = new CellProperty<double>(this, nameof(weighted_average_sell), price => this.security.spec_or_default().format(price), wavg_sell);
_fills = fills;
// set the initial pnl and pnl_price when there has been a
@@ -193,19 +193,13 @@ namespace mbts.spark {
class PnLDoubleBrushCell : CellProperty<double> {
Brush _background;
public PnLDoubleBrushCell(PnLSummaryRow owner, string prop) : base(d => double.IsNaN(d) ? string.Empty : d.ToString("N2"), double.NaN) =>
public PnLDoubleBrushCell(PnLSummaryRow owner, string prop) : base(owner, prop, d => double.IsNaN(d) ? string.Empty : d.ToString("N2"), double.NaN) =>
_value_set += _ => {
_background = null;
owner.RaisePropertyChanged(prop);
};
public Brush background => _background ?? (_background = _brushes.get(value));
}
class PnLPriceCell : CellProperty<double> {
public PnLPriceCell(PnLSummaryRow owner, string prop, double init = 0)
: base(price => owner.security.spec_or_default().format(price), init) =>
_value_set += _ => owner.RaisePropertyChanged(prop);
}
}
}
@@ -25,7 +25,6 @@ namespace mbts.spark.utilities {
static readonly OrderMetadata _default_order_metadata = new OrderMetadata { KeyBinding = "", Color = BTSColors.Orders.spreader_order };
static readonly CustomOrderDefinition.CustomOneofCase[]
// The following standard types are not yet ready for production use:
// CustomOrderDefinition.CustomOneofCase.Tick,
// CustomOrderDefinition.CustomOneofCase.Dynamic,
_standard_types = new[] {
CustomOrderDefinition.CustomOneofCase.Market,
@@ -37,6 +36,7 @@ namespace mbts.spark.utilities {
CustomOrderDefinition.CustomOneofCase.Lean,
CustomOrderDefinition.CustomOneofCase.Join,
CustomOrderDefinition.CustomOneofCase.Cover,
CustomOrderDefinition.CustomOneofCase.Oco,
CustomOrderDefinition.CustomOneofCase.Offset,
CustomOrderDefinition.CustomOneofCase.Top,
CustomOrderDefinition.CustomOneofCase.Trigger,
@@ -165,7 +165,8 @@ namespace mbts.spark.utilities {
}
#endregion
public OrderDefinition order(SecurityImmutable security) => order(security, _admin_model.get_default_account());
public OrderDefinitionImmutable order(SecurityImmutable security) =>
new OrderDefinitionImmutable(order(security, _admin_model.get_default_account()));
public OrderDefinition order(SecurityImmutable security, AccountTuple? account) {
var rv = new OrderDefinition {
SecurityKey = security.Id,
@@ -227,6 +228,15 @@ namespace mbts.spark.utilities {
Child = custom_order(),
Hedge = custom_order(),
};
public OCOOrderDefinition oco_order() => new OCOOrderDefinition
{
Child = custom_order(),
Other = custom_order(),
};
public PricerOrderDefinition pricer_order() => new PricerOrderDefinition
{
Child = custom_order(),
};
public OffsetOrderDefinition offset_order() => new OffsetOrderDefinition
{
Child = custom_order(),
@@ -261,6 +271,8 @@ namespace mbts.spark.utilities {
case CustomOrderDefinition.CustomOneofCase.Join: rv.Join = join_order(); break;
case CustomOrderDefinition.CustomOneofCase.Top: rv.Top = top_order(); break;
case CustomOrderDefinition.CustomOneofCase.Cover: rv.Cover = cover_order(); break;
case CustomOrderDefinition.CustomOneofCase.Oco: rv.Oco = oco_order(); break;
case CustomOrderDefinition.CustomOneofCase.Pricer: rv.Pricer = pricer_order(); break;
case CustomOrderDefinition.CustomOneofCase.Offset: rv.Offset = offset_order(); break;
case CustomOrderDefinition.CustomOneofCase.Trigger: rv.Trigger = trigger_order(); break;
case CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = spreader_order(); break;
@@ -284,6 +296,8 @@ namespace mbts.spark.utilities {
case CustomOrderDefinition.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
case CustomOrderDefinition.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
case CustomOrderDefinition.CustomOneofCase.Cover: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.cover_order };
case CustomOrderDefinition.CustomOneofCase.Oco: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.oco_order };
case CustomOrderDefinition.CustomOneofCase.Pricer: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.pricer_order };
case CustomOrderDefinition.CustomOneofCase.Offset: return new OrderMetadata { KeyBinding = "O", Color = BTSColors.Orders.tick_order };
case CustomOrderDefinition.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.trigger_order };
case CustomOrderDefinition.CustomOneofCase.Spreader: goto default;
@@ -305,6 +319,8 @@ namespace mbts.spark.utilities {
case OrderState.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
case OrderState.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
case OrderState.CustomOneofCase.Cover: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.cover_order };
case OrderState.CustomOneofCase.Oco: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.oco_order };
case OrderState.CustomOneofCase.Pricer: return new OrderMetadata { KeyBinding = "C", Color = BTSColors.Orders.pricer_order };
case OrderState.CustomOneofCase.Offset: return new OrderMetadata { KeyBinding = "O", Color = BTSColors.Orders.tick_order };
case OrderState.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.trigger_order };
case OrderState.CustomOneofCase.Spreader: goto default;
@@ -344,6 +360,8 @@ namespace mbts.spark.utilities {
case nameof(JoinOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Join;
case nameof(TopOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Top;
case nameof(CoverOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Cover;
case nameof(OCOOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Oco;
case nameof(PricerOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Pricer;
case nameof(OffsetOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Offset;
case nameof(TriggerOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Trigger;
case nameof(DynamicOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Dynamic;
@@ -17,7 +17,9 @@ namespace mbts.spark {
readonly DelegateProperty<IEnumerable<DialogSkirtItem>> _left_skirt_items;
readonly List<IDisposable> _resources = new List<IDisposable>(2); // initialize with 2 capacity bc the only potential disposables are the instance/default vms
public WindowSettingsDialogVM(IWindowSettingsManager<T> settings_manager, Func<UserSettings, T> get_factory_settings, string settings_file_ext, Func<T, TVM> vm_factory) {
public WindowSettingsDialogVM(IWindowSettingsManager<T> settings_manager, Func<UserSettings, T> get_factory_settings, string settings_file_ext, Func<T, TVM> vm_factory, Func<T, T> merge_with_current = null) {
if (merge_with_current == null)
merge_with_current = s => s;
// by default, each new window will use their own instance settings
_use_default = backing(nameof(use_default), settings_manager.instance_settings == null);
default_vm = vm_factory(settings_manager.default_settings);
@@ -59,15 +61,14 @@ namespace mbts.spark {
_resources.Add(dispose_dvm);
// local methods
void apply() {
var result = merge_with_current(instance_vm.get_result());
if (!use_default) {
settings_manager.instance_settings = instance_vm.get_result();
settings_manager.instance_settings = result;
} else {
settings_manager.instance_settings = null;
// copy the current instance settings and persist
var my_default = instance_vm.get_result();
if (!my_default.safe_equals(settings_manager.default_settings))
settings_manager.save_default_settings(my_default).ignore();
if (!result.safe_equals(settings_manager.default_settings))
settings_manager.save_default_settings(result).ignore();
}
}
@@ -13,14 +13,9 @@ namespace mbts.co2
{
internal static unsafe T deserialize<T>(byte* buffer, ulong length) where T : new() {
var stream = new System.IO.UnmanagedMemoryStream(buffer, (long)length);
if (typeof(IMessage).IsAssignableFrom(typeof(T))) {
IMessage result = (IMessage)new T();
result.MergeFrom(stream);
return (T)result;
}
else {
return ProtoBuf.Serializer.Deserialize<T>(stream);
}
IMessage result = (IMessage)new T();
result.MergeFrom(stream);
return (T)result;
}
[DllImport("libspark")]
@@ -81,7 +76,7 @@ namespace mbts.co2
[DllImport("libspark")]
internal unsafe static extern IntPtr add_relay_symbol(IntPtr relay, UserSymbol symbol, bts.co2.MDRequest.Types.Flags flags);
[DllImport("libspark")]
internal unsafe static extern IntPtr set_relay_token(IntPtr relay, string user_name);
internal unsafe static extern IntPtr set_relay_token(IntPtr relay, byte[] token, long token_size);
[DllImport("libspark")]
internal unsafe static extern void post_on_scan_security_thread(Action callback);
@@ -14,17 +14,16 @@ namespace mbts.co2
{
MDConsumer _consumer;
IntPtr _relay;
public Co2RelayControl(MDConsumer consumer) {
public Co2RelayControl(MDConsumer consumer, uint naggle_ms) {
_consumer = consumer;
Log.assert(consumer.native_pointer() != IntPtr.Zero);
_relay =
Co2NativeMethods.start_private_relay(consumer.native_pointer(),
config.Configuration.get<bts.md.RelayOptions>().naggle_ms);
_relay = Co2NativeMethods.start_private_relay(consumer.native_pointer(), naggle_ms);
Log.assert(_relay != IntPtr.Zero);
}
public void set_token(AuthToken token) {
Co2NativeMethods.set_relay_token(_relay, token.Token);
public void set_token(AuthTokenImmutable token) {
var buffer = token.Token.ToByteArray();
Co2NativeMethods.set_relay_token(_relay, buffer, buffer.LongLength);
}
public async Task preload_securities(MDRequest.Types.Flags flags=MDRequest.Types.Flags.Security) {
File renamed without changes.
@@ -9,6 +9,7 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using security_key = System.UInt32;
using bts;
namespace mbts.co2
{
File renamed without changes.
+2 -2
View File
@@ -64,7 +64,7 @@ namespace bts.api.core {
internal static extern void api_client_send(IntPtr nchannel, byte[] buffer, int length, raw_pb_callback p, IntPtr extra);
[DllImport("libspark")]
internal static extern IntPtr api_client_subscribe(IntPtr client, string method_and_qualifier, raw_subscription_callback cb, IntPtr extra);
internal static extern IntPtr api_client_subscribe(IntPtr client, string method, string qualifier, raw_subscription_callback cb, IntPtr extra);
[DllImport("libspark")]
internal static extern void api_client_unsubscribe(IntPtr client, IntPtr subscription);
@@ -96,7 +96,7 @@ namespace bts.api.core {
}
[DllImport("libspark")]
internal static extern IntPtr api_server_create_table(IntPtr server, string method_and_qualifier);
internal static extern IntPtr api_server_create_table(IntPtr server, string method, string qualifier);
[DllImport("libspark")]
internal static extern void api_server_update_table(IntPtr table, [In] StreamUpdate[] changes, long change_count);
[DllImport("libspark")]
-9
View File
@@ -17,15 +17,6 @@ namespace bts.api.core
internal abstract class ApiServer : IApiServer
{
static JsonFormatter _formatter = new JsonFormatter(new JsonFormatter.Settings(true));
static Regex _fmt_cleanup = new Regex(@"[\s_+]", RegexOptions.Compiled | RegexOptions.Multiline);
public static string format_qualifier<T>(string method, T arg) where T : IMessage {
if (arg != null) {
string arg_fmt = _fmt_cleanup.Replace(_formatter.Format(arg), "");
return method + arg_fmt;
}
return method;
}
public abstract void dispatch(RequestHeader header, Stream request, Action<byte[]> on_response);
protected void respond<T>(int request_id, Task<T> task, Action<byte[]> on_response) where T : IMessage, IImmutableMessage {
+61 -61
View File
@@ -1,68 +1,68 @@
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bts.api {
internal static class ApiUtils {
public static byte[] serialize<T>(this RequestHeader header, T body) where T : IImmutableMessage {
int body_size = body?.CalculateSize() ?? 0;
int header_size = header.CalculateSize() + 1; // 1 byte header length
var buffer = new byte[header_size+body_size];
using bts.api.core;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bts.api {
internal static class ApiUtils {
public static byte[] serialize<T>(this RequestHeader header, T body) where T : IImmutableMessage {
int body_size = body?.CalculateSize() ?? 0;
int header_size = header.CalculateSize() + 1; // 1 byte header length
var buffer = new byte[header_size + body_size];
using (var stream = new MemoryStream(buffer)) {
checked { stream.WriteByte((byte)header_size); }
header.WriteTo(stream);
if (body != null) body.WriteTo(stream);
}
return buffer;
}
public struct DeserializeResult<THeader,TBody> {
public THeader header;
public TBody body;
}
private static DeserializeResult<THeader, TBody> mkresult<THeader,TBody>(THeader header, TBody body) {
return new DeserializeResult<THeader, TBody>() { header = header, body = body }; }
public static unsafe DeserializeResult<THeader, TBody> deserialize<THeader,TBody>(byte* buffer, long buffer_length) where THeader : IMessage,new() where TBody : class,IMessage,new() {
var header_size = buffer[0];
Debug.Assert(header_size <= buffer_length);
THeader header = new THeader();
header.MergeFrom(new UnmanagedMemoryStream(buffer+1, header_size - 1));
TBody body = new TBody();
if (buffer_length > header_size) {
body.MergeFrom(new UnmanagedMemoryStream(buffer + header_size, buffer_length - header_size));
}
return mkresult(header, body);
}
public static byte[] serialize<T>(this ResponseHeader header, T body) where T : IImmutableMessage {
int header_size = 1 + header.CalculateSize();
int body_size = body == null ? 0 : body.CalculateSize();
}
return buffer;
}
public struct DeserializeResult<THeader, TBody> {
public THeader header;
public TBody body;
}
private static DeserializeResult<THeader, TBody> mkresult<THeader, TBody>(THeader header, TBody body) {
return new DeserializeResult<THeader, TBody>() { header = header, body = body }; }
public static unsafe DeserializeResult<THeader, TBody> deserialize<THeader, TBody>(byte* buffer, long buffer_length) where THeader : IMessage, new() where TBody : class, IMessage, new() {
var header_size = buffer[0];
Debug.Assert(header_size <= buffer_length);
THeader header = new THeader();
header.MergeFrom(new UnmanagedMemoryStream(buffer + 1, header_size - 1));
TBody body = new TBody();
if (buffer_length > header_size) {
body.MergeFrom(new UnmanagedMemoryStream(buffer + header_size, buffer_length - header_size));
}
return mkresult(header, body);
}
public static byte[] serialize<T>(this ResponseHeader header, T body) where T : IImmutableMessage {
int header_size = 1 + header.CalculateSize();
int body_size = body == null ? 0 : body.CalculateSize();
var buffer = new byte[header_size + body_size];
using (var stream = new MemoryStream(buffer)) {
checked { stream.WriteByte((byte)header_size); }
header.WriteTo(stream);
if (body != null) body.WriteTo(stream);
using (var stream = new MemoryStream(buffer)) {
checked { stream.WriteByte((byte)header_size); }
header.WriteTo(stream);
if (body != null) body.WriteTo(stream);
}
return buffer;
}
public static byte[] serialize(this ResponseHeader header) {
int header_size = 1 + header.CalculateSize();
var buffer = new byte[header_size];
using (var stream = new MemoryStream(buffer)) {
checked { stream.WriteByte((byte)header_size); }
header.WriteTo(stream);
}
return buffer;
}
}
}
}
public static byte[] serialize(this ResponseHeader header) {
int header_size = 1 + header.CalculateSize();
var buffer = new byte[header_size];
using (var stream = new MemoryStream(buffer)) {
checked { stream.WriteByte((byte)header_size); }
header.WriteTo(stream);
}
return buffer;
}
}
}
@@ -35,6 +35,7 @@ namespace bts.api.core {
var header = new RequestHeader() {
Method = method_id,
RequestId = id,
Token = _context.token.HasValue ? new AuthToken() { Token = _context.token?.Token } : null
};
var buffer = header.serialize(request);
var tcs = new TaskCompletionSource<Result<Response>>();
@@ -56,60 +57,99 @@ namespace bts.api.core {
return tcs.Task;
}
public unsafe override IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(string method_and_qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter) {
var result = Observable.Create<RawUpdate<TOut>>(observer => {
bool first_update = true;
// Memory optimization. There is only one update per subscription.
RawUpdate<TOut> the_update = new RawUpdate<TOut>();
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
the_update.Clear();
try {
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
foreach (var change in changes) {
switch (change.type) {
case ApiNativeMethods.ChangeType.REMOVE:
the_update.removed_keys.Add(change.key);
break;
case ApiNativeMethods.ChangeType.REPLACE:
the_update.changed_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
the_update.changed_keys.Add(change.key);
break;
case ApiNativeMethods.ChangeType.CREATE:
the_update.added_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
the_update.added_keys.Add(change.key);
break;
}
}
observer.OnNext(the_update);
public override IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(int method_id, string method, string qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter) {
var result = Observable.Create<RawUpdate<TOut>>(async observer => {
IntPtr temp_subscription = IntPtr.Zero;
void unsubscribe_temp_sub() {
// Unsubscribe late to keep qb refcount > 0 continuously
if (temp_subscription != IntPtr.Zero) ApiNativeMethods.api_client_unsubscribe(_client, temp_subscription);
}
while (true) {
var response = await check_stream_access(method_id, qualifier);
if (response.success) {
if (response.value.Authorized) {
// success. Break loop and subscribe
break;
}
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
if (!first_update) {
observer.OnNext(the_update.MarkAsReset());
}
first_update = false;
else {
// Not authorized. return empty stream
unsubscribe_temp_sub();
observer.OnCompleted();
return System.Reactive.Disposables.Disposable.Empty;
}
}
catch (Exception e) {
ApiNativeMethods.api_client_log_error($"Exception in subscription callback for sub: '{method_and_qualifier}' \n--\n '{e}'");
// Sleep to let the log message get asyncronously written to disk
Thread.Sleep(100);
Environment.Exit(-1);
else {
// Wait for stream to fire once before trying again
unsubscribe_temp_sub();
var tcs = new TaskCompletionSource<bool>();
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
tcs.TrySetResult(true);
};
temp_subscription = ApiNativeMethods.api_client_subscribe(_client, method, qualifier, cb, IntPtr.Zero);
await tcs.Task;
GC.KeepAlive(cb);
}
};
var subscription = ApiNativeMethods.api_client_subscribe(_client, method_and_qualifier, cb, IntPtr.Zero);
return FinalizeableAnonymousDisposable.Create((disposing) => {
ApiNativeMethods.api_client_unsubscribe(_client, subscription);
subscription = IntPtr.Zero;
GC.KeepAlive(cb);
cb = null;
// Cannot (ever) call logging from the finalizer thread
// if (!disposing) {
// ApiNativeMethods.api_client_log_error($"Subscription has been terminated because the returned IDisposable has been finalized. Subscription: {method_and_qualifier}");
// }
});
}
// Authorized. Stream events
unsubscribe_temp_sub();
return SubscribeNative(method, qualifier, parser, converter, observer);
});
return result;
}
private Task<Result<StreamAccessResponse>> check_stream_access(int method_id, string qualifier) {
var request = new StreamAccessRequest() { Qualifier = qualifier ?? "" };
return send<StreamAccessRequest, StreamAccessResponse>(method_id, request);
}
private unsafe IDisposable SubscribeNative<TParse, TOut>(string method, string qualifier, MessageParser<TParse> parser, Func<TParse, TOut> converter, IObserver<RawUpdate<TOut>> observer) where TParse : IMessage<TParse> {
bool first_update = true;
// Memory optimization. There is only one update per subscription.
RawUpdate<TOut> the_update = new RawUpdate<TOut>();
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
the_update.Clear();
try {
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
foreach (var change in changes) {
switch (change.type) {
case ApiNativeMethods.ChangeType.REMOVE:
the_update.removed_keys.Add(change.key);
break;
case ApiNativeMethods.ChangeType.REPLACE:
the_update.changed_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
the_update.changed_keys.Add(change.key);
break;
case ApiNativeMethods.ChangeType.CREATE:
the_update.added_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
the_update.added_keys.Add(change.key);
break;
}
}
observer.OnNext(the_update);
}
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
if (!first_update) {
observer.OnNext(the_update.MarkAsReset());
}
first_update = false;
}
}
catch (Exception e) {
ApiNativeMethods.api_client_log_error($"Exception in subscription callback for sub: '{method}/{qualifier}' \n--\n '{e}'");
// Sleep to let the log message get asyncronously written to disk
Thread.Sleep(100);
Environment.Exit(-1);
}
};
var subscription = ApiNativeMethods.api_client_subscribe(_client, method, qualifier, cb, IntPtr.Zero);
return FinalizeableAnonymousDisposable.Create((disposing) => {
ApiNativeMethods.api_client_unsubscribe(_client, subscription);
subscription = IntPtr.Zero;
GC.KeepAlive(cb);
cb = null;
});
}
private class FinalizeableAnonymousDisposable : IDisposable {
private Action<bool> _action;
public FinalizeableAnonymousDisposable(Action<bool> dispose) {
+2 -2
View File
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
namespace bts.api.core {
internal abstract class ClientChannel : IDisposable
{
readonly ServiceContext _context;
protected readonly ServiceContext _context;
public ClientChannel(ServiceContext context) {
_context = context;
@@ -13,7 +13,7 @@ namespace bts.api.core {
public abstract void Dispose();
public abstract Task<Result<Response>> send<Request, Response>(int method_id, Request request)
where Request: IImmutableMessage where Response : class,IMessage,new();
public abstract IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(string method_and_qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter=null)
public abstract IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(int method_id, string method, string qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter=null)
where TParse : IMessage<TParse>;
public abstract bool is_sync_call_ok();
+6 -1
View File
@@ -37,7 +37,12 @@ namespace bts.api.core
public T value {
get {
if (success) return _result;
else throw new ApiException(error_message);
else {
if (typeof(T).IsValueType) { throw new ApiException(error_message); }
else {
return default(T);// always null
}
}
}
}
public bool success { get { return _code == ApiError.Success; } }
+2 -2
View File
@@ -34,8 +34,8 @@ namespace bts.api.core {
}
public Action<RequestHeader, Stream, Action<byte[]>> on_request;
public IntPtr create_table(string method_and_qualifier) {
return ApiNativeMethods.api_server_create_table(_server, method_and_qualifier);
public IntPtr create_table(string method, string qualifier) {
return ApiNativeMethods.api_server_create_table(_server, method, qualifier);
}
public void Dispose() {
+43 -3
View File
@@ -4,12 +4,14 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using bts.api.user;
namespace bts.api.core
{
namespace bts.api.core {
public class ServiceContext : IDisposable
{
IntPtr _qbus;
AuthToken _token;
public ServiceContext() {
_qbus = ApiNativeMethods.api_create_quickbus(null, 0);
}
@@ -22,7 +24,6 @@ namespace bts.api.core
Type[] arg_types = new[] { typeof(ClientChannel) };
ConstructorInfo ctor = typeof(T).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).First(ci => ci.GetParameters().SingleOrDefault()?.ParameterType == typeof(ClientChannel));
return (T)ctor.Invoke(new object[] { channel });
//return (T)Activator.CreateInstance(typeof(T), channel);
}
internal ServerChannel publish<T>(T service, string service_type, string qualifier) where T : IApiServer {
@@ -31,6 +32,38 @@ namespace bts.api.core
return ch;
}
public AuthTokenImmutable? token => _token ?? (AuthTokenImmutable?)null;
public string user { get; private set; }
public uint user_id { get; private set; }
public bool is_authenticated => token.HasValue;
public async Task<LoginResult> login(string username, string password) {
var auth_service = locate<AuthService>();
var result = await auth_service.authenticate_login_async(new AuthenticateLoginRequest() {
Login = username ?? "",
Password = password ?? ""
});
if (result.success) {
if (result.value.IsValid) {
_token = result.value.Token;
user = username;
user_id = result.value.UserId;
return new LoginResult();
}
else {
return new LoginResult(result.value.Reason);
}
}
else {
return new LoginResult(result.error_message);
}
}
public void login(AuthTokenImmutable token) {
this.user = "";
this.user_id = 0;
this._token = token.CloneToMutable();
}
#region IDisposable Support
protected virtual void Dispose(bool disposing) {
@@ -52,4 +85,11 @@ namespace bts.api.core
}
#endregion
}
public class LoginResult {
public LoginResult() { success = true; }
public LoginResult(string reason) { success = false; this.reason = reason; }
public bool success { get; }
public string reason { get; }
}
}
-23
View File
@@ -1,23 +0,0 @@
using System;
using System.Threading.Tasks;
using bts.api;
using bts.api.core;
namespace bts.api
{
public class Authenticator
{
readonly static Lazy<user.UserService> _auth = new Lazy<user.UserService>(() => Locator.locate<user.UserService>(), true);
user.AuthToken _token = null;
public Authenticator() { }
public user.AuthToken last_token => _token;
public async Task<user.AuthToken> login(string login, string password)
{
var res = await _auth.Value.authenticate_login_async(new user.AuthenticateLoginRequest { Login = login, Password = password });
if (res.success)
_token = res.value.Token;
return _token;
}
}
}
+59
View File
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using bts.api;
using mbts;
using mbts.co2;
namespace bts.md
{
interface IMDUpdate
{
bool update(SecurityMD smd, ref ulong last_update_time);
}
public interface IMDData
{
SecurityImmutable security { get; }
}
public interface IMDBook : IMDData
{
List<PriceSize> bids { get; }
List<PriceSize> asks { get; }
}
class MDBook : IMDBook, IMDUpdate
{
public MDBook()
{
}
public SecurityImmutable security { get; private set; }
public List<PriceSize> bids { get; private set; }
public List<PriceSize> asks { get; private set; }
bool IMDUpdate.update(SecurityMD smd, ref ulong last_update_time)
{
this.security = smd.security;
NativePriceSize[] bids = null, asks = null;
if (smd.md.get_depth(ref last_update_time, ref bids, ref asks, NativePriceHelpers.COCONUT_MAX_DEPTH))
{
this.bids = bids.Select(x => new PriceSize(x.size, x.direct_size, x.implied_size, x.price)).ToList();
this.asks = asks.Select(x => new PriceSize(x.size, x.direct_size, x.implied_size, x.price)).ToList();
return true;
}
return false;
}
public override string ToString()
{
return security.ToString() + Environment.NewLine +
"Bid BidSz BidISz" + Environment.NewLine +
string.Join("\n", bids.Select(x => x.ToString())) + Environment.NewLine +
"Ask AskSz AskISz" + Environment.NewLine +
string.Join("\n", asks.Select(x => x.ToString()));
}
}
}
Loaded 100 of 454 files, more files were not shown because too many files have changed in this diff. Show more