Compare commits

...
254 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
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
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
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
Taha Afzal 0644d301b2 merging from master 2018-12-27 13:24:48 -05: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
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
Kevin Darby 30891bfb49 need a little boost 2018-12-21 15:28:45 -05: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
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
Pedro Pinto 5078b74f72 Dev doc 2018-12-18 09:22:12 -05:00
Taha Afzal 6a1a2d5f0a organizing around HegderManager 1 2018-12-18 07:12:33 -05:00
Kevin Darby 664c1f28ed Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2018-12-17 18:05:59 -05: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
Kevin Darby 00dba92636 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos2 2018-12-17 15:36:32 -05:00
Taha Afzal e82db39f8c merging from SPARK-1887-speed-up-order-manager 2018-12-17 14:45:54 -05:00
Kevin Darby 5a5de65292 OCO and Pricer Algos 2018-12-17 13:56:24 -05:00
Taha Afzal dffb9c5320 guard around SpreaderChildOrder::order_key() to deal with in active state 2018-12-17 11:34:48 -05:00
Taha Afzal 2be4eb1c5c adding is_permenantly_cancelled to hedger 2018-12-16 22:15:39 -05:00
Taha Afzal 175204efdf removed the init call from Hedger 2018-12-14 17:50:44 -05:00
Taha Afzal 8894aea1ad removed the hedge vector from SpreaderBookkeepingData 2018-12-14 13:42:05 -05:00
Taha Afzal 43e82fb8c4 Added HedgerManager that keeps track of activ hedgers 2018-12-13 22:04:56 -05:00
187 changed files with 6913 additions and 2238 deletions

No files matched your search

+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",
+43 -3
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",
@@ -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",
+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)
+35 -37
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,24 +43,26 @@ 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 think carefully about the best way
to verify that it is good before it goes to production. We don't have a
dedicated QA cycle to flush out bugs so it's important that a change be
fully tested before it's marked "Ready to Merge." For any non-trivial
change consider enlisting a 'test buddy' to help review the code and
think of test cases that you may not have considered. Stability from
release to release is especially critical right now as we develop a
reputation in the futures world.
- Code Reviews
* 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
@@ -70,7 +71,7 @@ reputation in the futures world.
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
@@ -79,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
+51
View File
@@ -1,4 +1,55 @@
# 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
+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"
}
}
}
+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));
@@ -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);
@@ -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)]
@@ -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,
@@ -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);
@@ -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 {
@@ -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;
@@ -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;
}
}
+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>
@@ -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
@@ -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) {
@@ -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();
@@ -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();
}
}
}
@@ -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;
}
@@ -533,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}" />
@@ -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;
}
@@ -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,
@@ -228,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(),
@@ -262,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;
@@ -285,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;
@@ -306,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;
@@ -345,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;
+1 -1
View File
@@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
</ItemGroup>
+1
View File
@@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
namespace bts {
enum Level {
NoLog,
Diag,
Info,
Warn,
@@ -9,12 +9,12 @@ using System.Threading.Tasks;
namespace bts.test {
public static class InProcessTestRunner {
public static void run_test_syncronously(string v, TestEnumerator enumerator, Action<TestResult> report) {
public static void run_test_synchronously(string v, TestEnumerator enumerator, Action<TestResult> report) {
foreach (var t in enumerator.find(v)) {
run_test_syncronously(t, report);
run_test_synchronously(t, report);
}
}
public static void run_test_syncronously(TestMethod test, Action<TestResult> report) {
public static void run_test_synchronously(TestMethod test, Action<TestResult> report) {
var result = new TestResult() {
TestName = test.full_name,
Status = TestResult.Types.Step.Setup,
@@ -2,8 +2,16 @@
namespace bts.test {
public enum TestType {
Unit, System, Shell
}
[AttributeUsage(AttributeTargets.Class)]
public class TestGroupAttribute : Attribute {
public TestGroupAttribute() {
this.TestType = TestType.Unit;
}
public TestType TestType;
}
[AttributeUsage(AttributeTargets.Method)]
public class TestAttribute : Attribute {
@@ -7,23 +7,34 @@ namespace bts.test {
public class TestEnumerator {
public TestEnumerator(params string[] assemblies) {
TestGroups =
TestGroups =
assemblies
.Select(Assembly.Load)
.SelectMany(a => a.GetTypes())
.Where(t => t.GetCustomAttribute<TestGroupAttribute>() != null)
.Select(t =>
new TestClass(t,
new TestClass(t,
t.GetCustomAttribute<TestGroupAttribute>().TestType,
t.GetMethods()
.Where(m => m.GetCustomAttribute<TestAttribute>() != null)
.Select(m => new TestMethod(t, m))
.ToList()))
.ToList();
.ToList();
}
public IReadOnlyCollection<TestClass> TestGroups { get; }
public IEnumerable<TestClass> get_groups_of_type(TestType tt) {
return TestGroups.Where(wt => wt.test_type == tt);
}
public IEnumerable<TestMethod> Tests => TestGroups.SelectMany(t => t.methods);
public IEnumerable<TestMethod> get_of_type(TestType tt) {
return TestGroups.Where(wt => wt.test_type == tt).SelectMany(t => t.methods);
}
public IEnumerable<TestMethod> find(string name) {
foreach (var g in TestGroups) {
foreach (var t in g.methods) {
@@ -41,13 +52,15 @@ namespace bts.test {
public class TestClass {
public TestClass(Type type, IEnumerable<TestMethod> methods) {
public TestClass(Type type, TestType test_type, IEnumerable<TestMethod> methods) {
this.type = type;
this.test_type = test_type;
this.methods = methods;
}
public string name => type.Name;
public Assembly assembly => type.Assembly;
public Type type { get; }
public TestType test_type { get; }
public IEnumerable<TestMethod> methods { get; }
public override string ToString() {
return $"[TestGroup:{name}]";
+1 -1
View File
@@ -21,7 +21,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
<PackageReference Include="NUnit" Version="3.10.1" />
+30 -14
View File
@@ -1,8 +1,31 @@
# BTS Spark
Our offering consists of a client UI [Spark Client](https://www.bluetradingsystems.com/images/Images/spark/02.06.19/Ladder.png), a high performance professional futures front end, connected via our proprietary protocol over TCP to our Spark Server, which in turn connects to execution venues via their native protocols (typically FIX/TCP for execution and some proprietary protocol over UDP multicast for market data).
![alt text](https://www.bluetradingsystems.com/images/Images/api/API.png)
# BTS Spark Client SDK
#### Getting Started
We currently offer two API extension points. This repository, our BTS **Spark Client SDK** allows users to control our Spark Server externally, that is, out of process, using the exact same protocol our own Spark Client does. In other words, a client application developed via the Spark Client SDK, say a VB .NET UI, would be able to provide any functionality our own UI provides.
## Git
We currently offer bindings for the Spark protocol to C++, C# and Excel clients. Since the underlying transport is TCP, clients developed via the Spark Client SDK can be located anywhere in the world, as long as they can reach the Spark Server via TCP. We do require a VPN or a secure network, since the Spark Protocol is currently not yet encrypted.
Our second API extension point is intended for users that require adding functionality to our Spark Server. If you are interested in extending the server process through custom order types, market data sources, or execution gateways, please see the [Spark Server SDK](https://github.com/BlueTradingSystems/spark_sdk).
### In Depth
* [Interface Overview](doc/Overview.md)
* [Threading Notes](doc/Threading.md)
## Getting Started
### Git
* First clone the spark_client_sdk repo
@@ -11,7 +34,7 @@
```
## Build
### Build
* Use msbuild to target windows / .net 4.71
* Install studio here if you don't have it: https://visualstudio.microsoft.com/downloads/
* Use VSCode for editing, it's better: https://code.visualstudio.com/
@@ -24,7 +47,7 @@ spark_client_sdk\src\spark_sdk> msbuild
* spark_sdk binaries will be produced in the bin\Debug directory
## Connecting to Spark Server
### Connecting to Spark Server
* Start your exe using the -ConfigServer parameter to point at a running Spark Server
```
@@ -35,7 +58,7 @@ spark_client_sdk\src\spark_sdk> msbuild
include: "http://location:8000/config/"
```
## Examples
### Examples
* Make sure you can connect to a running server
* run spark_sdk.exe with no parameters to see help
```
@@ -58,22 +81,15 @@ Arguments:
* [Driver](src/spark_sdk/samples/SDKMain.cs)
## Excel
### Excel
```
spark_sdk\bin\Debug\net471> start spark_excel-addin64-packed.xll
```
* This will start excel and load the bts addin
* Check the [```excel```](excel/) directory for an example spreadsheet
## Notes
* Our client side API is based on Google's protocol buffers; the message types are all protos. https://developers.google.com/protocol-buffers/
* We extended the protoc compiler to generate c#, c++, excel and js bindings to the services provided by our server process (https://github.com/BlueTradingSystems/spark_sdk)
* ### Dotnet core
### Dotnet core
* Right now we only support c# Windows 10 clients, but the package targets both net471 and .net core 2.1.6, so in theory one could run a .net core or c++ client on linux with a little work.
+103
View File
@@ -0,0 +1,103 @@
## Spark Client SDK Overview
The high level description of our order entry interface is as follows:
```
service OrderService {
// RPCs
rpc create_order(Create) returns (Response)
rpc cancel_order(Cancel) returns (Response)
rpc update_order(Update) returns (Response)
// Streams
rpc orders (OrdersSub) returns (stream OrderRecord);
rpc fills (FillsSub) returns (stream Fill);
}
```
This description uses the Google Protocol Buffers syntax to define the server interface (we use our own little compiler to generate bindings to this interface in C#, C++ and Excel). Using this server interface we can create, cancel or update orders, using the Remote Procedure Calls (RPCs) defined these orders can be exchange native orders or our own high level algo orders, or new order types created by users via the Spark Server SDK). We can monitor order state by subscribing to the orders stream above, which produces a a stream of OrderRecord objects, each capturing the latest state of a particular order, as it goes through it life cycle.
An OrderRecord looks like this:
```
message OrderRecord {
uint32 key;
uint32 parent_key;
OrderDefinition def;
OrderState state;
}
```
We have an order key, a possibly null parent order key, an OrderDefinition record, which defines the basic elements of the order, namely its type, side, security, account, etc, and an OrderState record, which captures the latest state of the order:
```
message OrderState {
// Standard order state, shared by all order types
OrderStatus status ;
double price ;
uint32 quantity ;
uint32 filled_quantity;
double last_price;
double avg_price;
string last_msg;
google.protobuf.Timestamp created;
google.protobuf.Timestamp last_change;
// Custom order state, specific to the order type in question
oneof custom {
// native order types
MarketOrderState market;
LimitOrderState limit;
StopOrderState stop;
StopLimitOrderState stop_limit;
// algo order types
IcebergOrderState iceberg;
SpreaderOrderState spreader;
TimeBoxOrderState time_box;
TickOrderState tick;
LeanOrderState lean;
JoinOrderState join;
TopOrderState top;evident
TriggerOrderState trigger;
CoverOrderState cover;
OffsetOrderState offset;
DynamicOrderState dynamic;
}
}
```
This OrderState record consists of a common part, applicable to all order types, and a custom part that is specific to the particular order type the record is associated with. Most of the standard fields should be self-explanatory, the latest price, the latest quantity, the filled quantity, etc. The status enumerate field tells you which state the order is in. We currently define the following states:
```
enum OrderStatus {
// The order has been constructed but not acted on. Inactive orders
// can be abandoned without consequences. This state should only be
// used internally by the order manager, i.e. it should never appear
// in externally visible orders.
ORDER_STATUS_INACTIVE = 0;
// The order is currently working, i.e. its algorithm is operating.
ORDER_STATUS_WORKING = 1;
// The order has completed successfully and accomplished its
// goal. This is a terminal state.
ORDER_STATUS_COMPLETED = 2;
// The order was terminated orderly before it had a chance to
// complete. This is a terminal state.
ORDER_STATUS_CANCELED = 3;
// The order has encountered an unexpected condition from which it
// cannot recover. This a terminal state.
ORDER_STATUS_ERROR = 4;
// The last known state for this order was ORDER_STATUS_WORKING, but
// since then the execution gateway has signaled a disconnect. This
// is not a terminal state.
ORDER_STATUS_DISCONNECTED = 5;
// The order is initializiYou can ng. It cannot be modified at this
point.
ORDER_STATUS_PENDING_WORKING = 6;
// The order is invalid and was rejected by Spark (not the
// exchange), on creation. This is a terminal state.
ORDER_STATUS_REJECTED_BY_SPARK= 7;
}
```
### Threading
Check out our API Threading Doc [here](Threading.md).
+74
View File
@@ -0,0 +1,74 @@
# Client API Threading
BTS API provides two types of calls. There are RPC (remote procedure calls) which accept an argument and return a value and there are streams of tabular data which we call subscriptions. All communication to and from the server happens on a connection thread owned by the ServiceContext object and data must be marshaled back to the right context when the call is complete.
## RPC
### C#
All API calls provide both a synchronous and [asynchronous](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/async) version. If you are calling the API from a WPF or Windows Forms application you should always prefer the async version of the method because it will efficiently make the call and resume execution on the correct thread without blocking or lagging user interactions. The synchronous calls are intended for use in console applications where nothing else needs use of the thread until the data is available.
The async version of the method will return a `Result<T>` type which is used to signal if an error occurred during the call. You should test the Result.success to see if the call succeeded before using Result.value.
```csharp
var s = context.locate<api.user.AuthService>();
var result = await s.get_users_async(new GetUsersRequest());
if (result.success) {
Console.WriteLine(result.value);
}
else {
Console.WriteLine($"Error {result.error_message}");
}
```
### C++
All C++ API calls also provide both a blocking synchronous version and a callback based asynchronous version. In the asynchronous case the callback will execute on the ServiceContext thread and you will need to handle marshaling that data to the appropriate thread. Just like in c# the callback will receive a `Result<T>` which will indicate success or failure. `Result.value` returns an `std::shared_ptr` so you have control over the lifetime of the value.
## Subscriptions
BTS provides streaming updates for some types of data. These updates are incremental in nature and will provide a snapshot of the current state on a new subscription. Streaming updates use the [ReactiveX library](http://reactivex.io) to manage subscribing and unsubscribing. This library provides a rich set of operators that you can use to slide and dice the data streams based on your specific application requirements.
### C#
The subscription callback will arrive on the ServiceContext thread but it can be easily marshaled to the thread of your choice using the [ObserveOn](http://reactivex.io/documentation/operators/observeon.html) operator. For a WPF or Windows Forms application the most common use of this will be [ObserveOnDispatcher]() which will simply execute the callback in the context of the original calling thread. By your choice of where to place the `ObserveOnDispatcher` call in your reactive call chain you can choose whether other operators execute before after switching threads.
In this example the `Where` and `SelectMany` operators will execute on `ServiceContext` connection thread and only the resulting matches will be marshaled across to the calling thread. It is safe to unsubscribe by disposing `sub` from any thread.
```csharp
var orders = context.locate<OrderService>();
ObservableCollection<OrderRecord> orders_with_errors;
IDisposable sub =
orders
.subscribe_orders()
.SelectMany(update => update.added)
.Where(o => o.State.Status == OrderStatus.Error)
.ObserveOnDispatcher()
.Subscribe(o => {
orders_with_errors.Add(o);
});
```
### C++
The C++ subscription callbacks use the [RxCpp](https://github.com/ReactiveX/RxCpp) library which implements the [ReactiveX](http://reactivex.io) operators. The subscribe call will return an `rxcpp::composite_subscription` which implements [RAII](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization) and will unsubscribe when it is destroyed.
```cpp
#include "lib/api/public/ServiceContext.h"
#include "lib/api/services/OrderService.client.pb.h"
#include <rxcpp/rx.hpp>
#include <google/protobuf/util/json_util.h>
//...
bts::api::ServiceContext ctx;
auto client = ctx.locate<bts::api::orderman::OrderService>();
auto sub =
client.subscribe_orders().subscribe([=](const bts::api::orderman::OrdersUpdate& update) {
std::string result;
for(const auto& order : update.added()) {
auto status = google::protobuf::util::MessageToJsonString(*order, &result, {});
std::cout << "New order: " << result << std::endl;
}
});
```
+2 -1
View File
@@ -22,7 +22,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.4.0" />
<PackageReference Include="Google.Protobuf" Version="3.6.1" />
<PackageReference Include="System.Reactive" Version="3.1.1" />
<PackageReference Include="System.Reactive.PlatformServices" Version="3.1.1" />
<PackageReference Include="NUnit" Version="3.10.1" />
@@ -61,6 +61,7 @@
<None Include="$(OutputPath)\net471\spark_excel_example_2018.xlsm" pack="true" PackagePath=".\excel\spark_excel_example_2018.xlsm" />
<None Include="$(OutputPath)\net471\spark_excel-addin64-packed.xll" pack="true" PackagePath=".\excel\spark_excel-addin64-packed.xll" />
<None Include="$(OutputPath)\net471\spark_excel-addin64.dna" pack="true" PackagePath=".\excel\spark_excel-addin64.dna" />
<None Include="doc/**" pack="true" PackagePath=".\doc\" />
</ItemGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences" Condition= " '$(SparkRoot)' != '' ">
+289
View File
@@ -0,0 +1,289 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using bts.api;
using bts.api.admin;
using bts.api.core;
using bts.api.test;
using bts.test;
using bts.api.orderman;
using mbts;
using mbts.tests;
using bts.co2;
namespace bts.tests
{
[TestGroup(TestType = TestType.System)]
public class LimitsTests : IDisposable {
ServiceContext _context;
OrderService _order_service;
AdminService _admin_service;
public LimitsTests (){
_context = new ServiceContext();
_order_service = _context.locate<OrderService>();
_admin_service = _context.locate<AdminService>();
var auth = _context.login("bts","");
auth.Wait(3000);
}
public void Dispose() {
Disposable.dispose(ref _order_service);
Disposable.dispose(ref _admin_service);
Disposable.dispose(ref _context);
}
async Task<IEnumerable<SecurityImmutable>> all_futures(string symbol) {
var request = new MDRequest() {
Host = "system_test",
Channels = {
new SecurityChannel() {
Time = 0,
Flags = 0,
Type = SecurityType.Future,
Exchange = Venue.Sim,
Symbol = symbol,
TradeCursor = 0,
RfqCursor = 0
}
}
};
var relay = _context.locate<MarketDataRelay>();
await Task.Delay(100);
var result = await relay.requestmd_async(request);
List<SecurityImmutable> securities = new List<SecurityImmutable>();
if (result.success) {
foreach (var ch in result.value.Channels) {
foreach (var update in ch.Updates) {
if (update.Security != null) {
securities.Add(new SecurityImmutable(update.Security));
}
}
}
}
return securities;
}
[Test]
public async Task test_clip_limits() {
//Get and set limit values from the admin service
uint test_limit_value = 10u;
string test_symbol = "CL";
var test_user = (name: "bts", id: 1u);
var test_account = (name: "ACC A", id: 1u);
var account_data = await AdminUtilities.get_account_data(_admin_service, test_account.name);
Assert.IsTrue(account_data.HasValue);
var old_clip_limit = AdminUtilities.get_clip_limit(account_data, test_symbol);
//create Orders Here
var all = await all_futures(test_symbol);
SecurityImmutable? sec = all.FirstOrDefault();
Assert.IsTrue(sec.HasValue);
uint under_limit_order_key;
uint over_limit_order_key;
uint no_limit_order_key;
var order_def = create_order_def(sec.Value.Id, Side.Buy, test_account, test_user);
Assert.IsTrue(sec.HasValue, "Unable to create order without security");
await AdminUtilities.remove_clip_limit(_admin_service, test_account.name, test_symbol);
Result<Response> no_limit_order_result = await _order_service.create_order_async(new Create() {
NewOrder = order_def,
Price = 5,
Quantity = test_limit_value
});
var set_success = await AdminUtilities.set_clip_limit(_admin_service, test_limit_value, account_data, test_symbol);
Assert.IsTrue(set_success, "Failed to set limit");
Result<Response> under_limit_order_result = await _order_service.create_order_async(new Create() {
NewOrder = order_def,
Price = 5,
Quantity = test_limit_value
});
Result<Response> over_limit_order_result = await _order_service.create_order_async(new Create() {
NewOrder = order_def,
Price = 5,
Quantity = test_limit_value + 1
});
under_limit_order_key = under_limit_order_result.value.OrderKey;
over_limit_order_key = over_limit_order_result.value.OrderKey;
no_limit_order_key = no_limit_order_result.value.OrderKey;
bool waiting_for_orders = true;
var orders = _order_service.subscribei_orders().Subscribe(ords =>
{
waiting_for_orders = false;
IEnumerable<OrderRecordImmutable> filtered_orders = ords.all.Where(x => x.Key == under_limit_order_key || x.Key == over_limit_order_key || x.Key == no_limit_order_key);
Assert.IsTrue(filtered_orders.Count() == 3, "There should be 3 matching orders... there are not");
foreach(var o in filtered_orders)
{
if(o.Key == under_limit_order_key) {
Assert.IsTrue(o.State.Status == OrderStatus.Working, "Under Limit Order is not 'Working'");
}
else if(o.Key == over_limit_order_key) {
Assert.IsTrue(o.State.Status == OrderStatus.RejectedBySpark, "Over Limit Order is not 'RejectedBySpark'");
}
else if(o.Key == no_limit_order_key) {
Assert.IsTrue(o.State.Status == OrderStatus.RejectedBySpark, "No Limit Order is not 'RejectedBySpark'");
}
}
});
while(waiting_for_orders) {
await Task.Delay(100);
}
// Restore the Limits to the original values
bool success = false;
if(old_clip_limit.HasValue) {
success = await AdminUtilities.set_clip_limit(_admin_service, old_clip_limit.Value, test_account.name, test_symbol);
}
else {
success = await AdminUtilities.remove_clip_limit(_admin_service, test_account.name, test_symbol);
}
Assert.IsTrue(success, "Failed to restore Limit");
}
public OrderDefinition create_order_def(uint security_id, Side side, (String name, uint id) account, (String name, uint id) user){
return new OrderDefinition {
SecurityKey = security_id,
Side = Side.Buy,
TimeInForce = TimeInForce.Day,
CustomDef = new CustomOrderDefinition { Limit = new LimitOrderDefinition { } },
CtiCode = CTICode.CtiCode1,
CustomerOrFirm = CustomerOrFirm.Firm,
ManualOrderIndicator = ManualOrderIndicator.Yes,
Account = account.name,
AccountId = account.id,
User = user.name,
UserId = user.id
};
}
}
public class AdminUtilities {
public static async Task<BTSAccountImmutable?> get_account_data (AdminService admin_service, string account_name)
{
Update<AdminDataImmutable, int> update = await admin_service.subscribei_admin_data().FirstAsync();
foreach (var acc in update.all.First().Accounts) {
if(acc.Name == account_name) {
return acc;
}
}
return null;
}
public static AccountSecurityClipLimitUnitImmutable? get_clip_limit_row(BTSAccountImmutable? account_data, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
IEnumerable<AccountSecurityClipLimitUnitImmutable> clip_limit_rows = account_data.Value.SecurityClipLimits.Limit.Where(
r => r.Security.SecuritySymbol == symbol
&& r.Security.Exchange == venue
&& r.Security.SecurityType == security_type);
return clip_limit_rows.Any() ? clip_limit_rows.First() : (AccountSecurityClipLimitUnitImmutable?) null;
}
public static uint? get_clip_limit(BTSAccountImmutable? account_data, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
var row = get_clip_limit_row(account_data,symbol,security_type,venue);
return row.HasValue ? row.Value.ClipSize : (uint?) null;
}
public static async Task<uint?> get_clip_limit(AdminService admin_service, string account_name, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
var account_data = await get_account_data(admin_service, account_name);
return get_clip_limit(account_data, symbol, security_type, venue);
}
public static async Task<bool> set_clip_limit(AdminService admin_service, uint clip_value, string account_name, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
var account_data = await get_account_data(admin_service, account_name);
return await set_clip_limit(admin_service, clip_value,account_data, symbol,security_type, venue);
}
public static async Task<bool> set_clip_limit(AdminService admin_service, uint clip_value, BTSAccountImmutable? account_data, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
AccountSecurityClipLimitUnitImmutable? origin_clip_limit_row = get_clip_limit_row(account_data,symbol,security_type,venue);
AccountSecurityClipLimitUnitImmutable new_clip_limit_row;
List<AccountSecurityClipLimitUnitImmutable> clip_limits_list = account_data.Value.SecurityClipLimits.Limit.ToList();
if(origin_clip_limit_row.HasValue) {
clip_limits_list.Remove(origin_clip_limit_row.Value);
}
new_clip_limit_row = (AccountSecurityClipLimitUnitImmutable) new AccountSecurityClipLimitUnit {
Security = new SecurityScope(){ SecuritySymbol = symbol, Exchange = venue, SecurityType = security_type },
ClipSize = clip_value
};
clip_limits_list.Add(new_clip_limit_row);
AccountSecurityClipLimits temp_scl = new AccountSecurityClipLimits();
temp_scl.Limit.AddRange(clip_limits_list.Select(x => x.CloneToMutable()));
var new_account_data = account_data.Value.with(security_clip_limits: temp_scl);
var update_ret = await admin_service.update_account_async(new AccountUpdateAPIArgument {
CompleteData = new_account_data.CloneToMutable()
});
return update_ret.success;
}
public static async Task<bool> remove_clip_limit(AdminService admin_service, string account_name, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
var account_data = await get_account_data(admin_service, account_name);
return await remove_clip_limit(admin_service, account_data, symbol, security_type, venue);
}
public static async Task<bool> remove_clip_limit(AdminService admin_service, BTSAccountImmutable? account_data, string symbol, SecurityType security_type = SecurityType.Future, Venue venue = Venue.Sim) {
AccountSecurityClipLimitUnitImmutable? origin_clip_limit_row = get_clip_limit_row(account_data,symbol,security_type,venue);
List<AccountSecurityClipLimitUnitImmutable> clip_limits_list = account_data.Value.SecurityClipLimits.Limit.ToList();
if(origin_clip_limit_row.HasValue) {
clip_limits_list.Remove(origin_clip_limit_row.Value);
AccountSecurityClipLimits temp_scl = new AccountSecurityClipLimits();
temp_scl.Limit.AddRange(clip_limits_list.Select(x => x.CloneToMutable()));
var new_account_data = account_data.Value.with(security_clip_limits: temp_scl);
var update_ret = await admin_service.update_account_async(new AccountUpdateAPIArgument {
CompleteData = new_account_data.CloneToMutable()
});
return update_ret.success;
}
else
{
return false;
}
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<Platform>x64</Platform>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
+9 -4
View File
@@ -15,12 +15,17 @@ namespace sparkcmd {
uint _parallel = 1;
bool _list;
TestType test_type = TestType.Unit;
public TestCommand() {
IsCommand("test", "Run managed unit tests");
// i comes thorugh as "t" which seems daft.
// i comes thorugh as "t" which seems daft.
HasOption("t|timeout", "Timeout unit tests", i => _timeout = 10);
HasOption<uint>("n|parallel", "How many tests to run in parallel", i => _parallel = i);
HasOption("l|list", "List all tests", _ => _list = true);
HasOption("unit","Run unit tests", _ => test_type = TestType.Unit);
HasOption("system","Run system tests", _ => test_type = TestType.System);
HasOption("shell","Run shell tests", _ => test_type = TestType.Shell);
HasAdditionalArguments(null, " [Test List]");
}
@@ -35,7 +40,7 @@ namespace sparkcmd {
if (_timeout == 0 && tests.Length == 1) {
//System.Threading.Thread.Sleep(5000);
using (var trr = new TestResultReceiver(true)) {
InProcessTestRunner.run_test_syncronously(tests[0], Enumerator, trr.get_reporter());
InProcessTestRunner.run_test_synchronously(tests[0], Enumerator, trr.get_reporter());
trr.print_summary();
return trr.ErrorCount;
}
@@ -44,7 +49,7 @@ namespace sparkcmd {
return run_tests(Enumerator.match(tests).Select(t => t.name));
}
else {
return run_tests(Enumerator.Tests.Select(t => t.full_name));
return run_tests(Enumerator.get_of_type(test_type).Select(t => t.full_name));
}
}
@@ -59,7 +64,7 @@ namespace sparkcmd {
void list_tests() {
Console.WriteLine("All tests: ");
foreach (var t in Enumerator.TestGroups) {
foreach (var t in Enumerator.get_groups_of_type(test_type)) {
ConsoleEx.WriteLine(t.name, ConsoleColor.Green);
foreach (var test in t.methods) {
Console.Write($" {test.name}");
+3 -3
View File
@@ -8,10 +8,10 @@ using System.Linq;
namespace sparkcmd {
class Program {
static void Main(string[] args) {
static int Main(string[] args) {
mbts.config.Configuration.initialize();
var commands = ConsoleCommandDispatcher.FindCommandsInSameAssemblyAs(typeof(Program));
ConsoleCommandDispatcher.DispatchCommand(commands, args, Console.Out);
return;
return ConsoleCommandDispatcher.DispatchCommand(commands, args, Console.Out);
}
}
}
+2 -3
View File
@@ -2,8 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net471</TargetFramework>
<!--<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>-->
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;ubuntu.16.04-x64</RuntimeIdentifiers>
<Authors>Blue Trading Systems</Authors>
<Company>Blue Trading Systems</Company>
@@ -18,7 +17,7 @@
<ProjectReference Include="../spark_api/spark_api.csproj" />
<!--<ProjectReference Include="..\spark\Spark.csproj" />-->
<ProjectReference Include="..\spark_core\spark_core.csproj" />
<ProjectReference Include="..\spark_test\spark_test.csproj" Condition="'$(Configuration)'=='Debug'" />
<ProjectReference Include="..\spark_test\spark_test.csproj" />
<!--<Proto3 Include="**/*.proto" />-->
</ItemGroup>
+6
View File
@@ -3,3 +3,9 @@
rm -rf $1
mkdir $1
cp -r $2/files/* $1
for filename in $1/*; do
if [[ $filename == *.xz ]]; then
unxz $filename
fi
done
Binary file not shown.
+240 -105
View File
@@ -1,3 +1,6 @@
/* -------------------------------------------------------------------------------- */
/* Main tables */
/* -------------------------------------------------------------------------------- */
create table if not exists
session (
version TEXT,
@@ -5,6 +8,7 @@ session (
build_type TEXT,
session_start TIMESTAMP PRIMARY KEY
);
create index if not exists session_idx on session(session_start);
create table if not exists
trace (
@@ -20,55 +24,46 @@ trace (
sn INTEGER,
uint32_1 INTEGER
);
drop view if exists recent_trace;
create view recent_trace as
select * from trace order by rowid desc limit 140123;
create index if not exists trace_idx on trace(session_start,channel,sn,ts);
create index if not exists trace_t_idx on trace(session_start,ts);
drop view if exists ordered_trace;
create view ordered_trace as
select
recent_trace.*,
row_number() over (order by session_start,ts) entry_rank
from
recent_trace;
/* -------------------------------------------------------------------------------- */
/* Main Views */
/* -------------------------------------------------------------------------------- */
drop view if exists delta_trace;
create view delta_trace as
/* An ordered view of the traces, with extra columns for the delta
betwen consecutive traces as well as the delta between related
(scoped) traces */
drop view if exists log_view;
create view log_view as
select
current.*,
(current.ts - previous.ts) / 1000.0 delta
from
ordered_trace current,
ordered_trace previous
where
current.entry_rank = previous.entry_rank+1;
drop view if exists log;
create view log as
select
delta_trace.*,
trace.*,
trace.rowid entry_id,
(ts-lag(ts) over by_session_ts)/1000.0 delta,
case cor_event>0 when 1 then (ts-cor_event)/1000.0 else 0 end scope_delta,
case cor_event>0 when 1 then "end of " || entry_kind else entry_kind end scoped_entry
from
delta_trace;
drop view if exists events;
create view events as
trace
window
by_session_ts as (order by session_start,ts);
/* A view identifying each high level event (requote or hedge) and
annotating it with its latency */
drop view if exists event_view;
create view event_view as
select
fix_reads.session_start,
"hedge" event_type,
fix_reads.hts hts_start,
fix_reads.entry_id event_id,
fix_reads.ts start,
fix_writes.ts stop,
(fix_writes.ts - fix_reads.ts) / 1000.0 duration,
row_number() over (order by fix_reads.session_start,fix_reads.ts,fix_writes.ts) event_rank ,
fix_reads.channel,
fix_reads.sn
from
(select session_start, channel, sn, ts from recent_trace where entry_kind='fix_msg_r') fix_reads,
(select session_start, channel, sn, ts from recent_trace where entry_kind='before_fix_socket_w') fix_writes
(select session_start, channel, sn, hts,ts,rowid entry_id from trace where entry_kind='fix_msg_r' order by ts) fix_reads,
(select session_start, channel, sn, hts,ts,rowid entry_id from trace where entry_kind='before_fix_socket_w') fix_writes
where
fix_reads.session_start = fix_writes.session_start and
fix_reads.channel = fix_writes.channel and
@@ -77,35 +72,168 @@ union
select
md_reads.session_start,
"requote" event_type,
md_reads.hts hts_start,
md_reads.rowid event_id,
md_reads.ts start,
fix_writes.ts stop,
(fix_writes.ts - md_reads.ts) / 1000.0 duration,
row_number() over (order by md_reads.session_start,md_reads.ts,fix_writes.ts) event_rank ,
md_reads.channel,
md_reads.sn
from
(select session_start, channel, sn, ts from recent_trace where entry_kind='md') md_reads,
(select session_start, channel, sn, ts from recent_trace where entry_kind='before_fix_socket_w') fix_writes
(select distinct session_start, channel, sn, hts,ts from trace where entry_kind='md' order by ts) md_reads,
(select session_start, channel, sn, hts,ts,rowid entry_id from trace where entry_kind='before_fix_socket_w') fix_writes
where
md_reads.session_start = fix_writes.session_start and
md_reads.channel = fix_writes.channel and
md_reads.sn = fix_writes.sn;
drop view if exists event_log;
create view event_log as
/* A view on hedge events, identifying each order sent as part of the event*/
drop view if exists hedge_trace_view;
create view hedge_trace_view as
select
log.*,
event_type,
event_rank,
events.duration event_duration,
events.start event_start,
events.stop event_stop
session.version,
session.host,
session.build_type,
session.session_start,
trace.hts trace_time,
uint64_1 order_key,
duration total,
event_id
from
log,
events
event_view,
trace,
session
where
log.session_start = events.session_start and
log.ts >= events.start and log.ts <= events.stop;
session.session_start = trace.session_start and
session.session_start = event_view.session_start and
event_view.channel=trace.channel and event_view.sn=trace.sn and
event_type='hedge' and
entry_kind='NativeOrder::on_activate' and
cor_event=0;
/* A view on requote events, identifying each order sent as part of the event*/
drop view if exists requote_trace_view;
create view requote_trace_view as
select
session.version,
session.host,
session.build_type,
session.session_start,
trace.hts trace_time,
uint64_1 order_key,
duration total,
event_id
from
event_view,
trace,
session
where
session.session_start = trace.session_start and
session.session_start = event_view.session_start and
event_view.channel=trace.channel and event_view.sn=trace.sn and
event_type='requote' and
entry_kind='NativeOrder::on_update' and
cor_event=0;
/* -------------------------------------------------------------------------------- */
/* Archive tables/view */
/* -------------------------------------------------------------------------------- */
/* Auxiliary tables and views, used to archive the results of the main
views to speed up analysis as data accumulates */
create table if not exists
event_archive (
session_start TIMESTAMP,
event_type TEXT,
hts_start TIMESTAMP,
event_id INTEGER,
start INTEGER,
stop INTEGER,
duration float,
channel TEXT,
sn INTEGER
);
create index if not exists event_archive_idx on event_archive(session_start,start,stop);
create table if not exists
log_archive(
session_start TIMESTAMP,
ts INTEGER,
hts timestamp,
channel TEXT,
entry_kind TEXT,
txt_1 TEXT,
uint64_1 INTEGER,
thread TEXT,
cor_event INTEGER,
sn INTEGER,
uint32_1 INTEGER,
entry_id INTEGER,
delta float,
scope_delta float,
scoped_entry TEXT
);
create index if not exists log_archive_idx on log_archive(session_start,ts);
create table if not exists
hedge_trace_archive (
version TEXT,
host TEXT,
build_type TEXT,
session_start TIMESTAMP,
trace_time TIMESTAMP,
order_key INTEGER,
total float,
event_id integer
);
drop view if exists hedge_trace;
create view hedge_trace as
select * from hedge_trace_view union select * from hedge_trace_archive;
create table if not exists
requote_trace_archive (
version TEXT,
host TEXT,
build_type TEXT,
session_start TIMESTAMP,
trace_time TIMESTAMP,
order_key INTEGER,
total float,
event_id integer
);
drop view if exists requote_trace;
create view requote_trace as
select * from requote_trace_view union select * from requote_trace_archive;
/* a synthetis of the log and events, focusing only on traces that are
part of hedge/requote events */
create table if not exists
event_log_archive (
session_start TIMESTAMP,
ts INTEGER,
hts timestamp,
channel TEXT,
entry_kind TEXT,
txt_1 TEXT,
uint64_1 INTEGER,
thread TEXT,
cor_event INTEGER,
sn INTEGER,
uint32_1 INTEGER,
entry_id INTEGER,
delta float,
scope_delta float,
scoped_entry TEXT,
event_type TEXT,
event_id INTEGER,
event_duration float,
hts_start TIMESTAMP,
event_start INTEGER,
event_stop INTEGER
);
create index if not exists event_log_archive_idx on event_log_archive(session_start);
drop view if exists pretty_log;
create view pretty_log as
@@ -115,69 +243,76 @@ select
scope_delta ,
event_duration,
event_type,
event_rank,
event_id,
hts,
ts,
session_start,
entry_rank
entry_id
from
event_log;
event_log_archive;
drop view if exists last_pretty_log;
create view last_pretty_log as
select * from pretty_log where session_start=(select max(session_start) from session);
select
*
from
pretty_log
where
session_start=(select max(session_start) from session);
-- drop view if exists summary;
-- create view summary as
-- select session_start,event_type,count(*) count, avg(duration) avg, max(duration) max, min(duration) min, median(duration) median, stdev(duration) stdev from events group by session_start,event_type;
/*
drop view if exists event_stats;
create view event_stats as
select
session_start,
event_type,
count(*) count,
avg(duration) avg,
max(duration) max,
min(duration) min,
median(duration) median,
stdev(duration) stdev
from
event_archive
group by
session_start,
event_type;
*/
drop view if exists event_log_aux;
create view event_log_aux as
select
log_archive.*,
event_type,
event_id,
event_archive.duration event_duration,
event_archive.hts_start hts_start,
event_archive.start event_start,
event_archive.stop event_stop
from
log_archive,
event_archive
where
log_archive.session_start = event_archive.session_start and
log_archive.ts >= event_archive.start and log_archive.ts <= event_archive.stop;
begin transaction;
insert into hedge_trace_archive select * from hedge_trace_view;
insert into requote_trace_archive select * from requote_trace_view;
insert into event_archive select * from event_view;
insert into log_archive select * from log_view;
insert into event_log_archive select * from event_log_aux;
delete from trace;
delete from event_log_archive where
rowid in (select rowid from event_log_archive order by rowid desc limit -1 offset 10000123);
delete from hedge_trace_archive where
rowid in (select rowid from hedge_trace_archive order by rowid desc limit -1 offset 10000123);
delete from requote_trace_archive where
rowid in (select rowid from requote_trace_archive order by rowid desc limit -1 offset 10000123);
delete from log_archive where
rowid in (select rowid from log_archive order by rowid desc limit -1 offset 10000123);
delete from event_log_archive where
rowid in (select rowid from event_log_archive order by rowid desc limit -1 offset 10000123);
commit;
drop view if exists hedge_trace;
create view hedge_trace as
select
session.version,
session.host,
session.build_type,
session.session_start,
recent_trace.hts trace_time,
recent_trace.uint64_1 order_key,
hedge_events.duration total,
hedge_events.event_rank rank
from
recent_trace,
(select * from events where event_type='hedge') hedge_events,
session
where
entry_kind='NativeOrder::on_activate' and
recent_trace.cor_event = 0 and
recent_trace.channel = hedge_events.channel and
recent_trace.session_start = hedge_events.session_start and
recent_trace.sn = hedge_events.sn and
recent_trace.session_start = session.session_start;
drop view if exists requote_trace;
create view requote_trace as
select
session.version,
session.host,
session.build_type,
session.session_start,
recent_trace.hts trace_time,
recent_trace.uint64_1 order_key,
requote_events.duration total,
requote_events.event_rank rank
from
recent_trace,
(select * from events where event_type='requote') requote_events,
session
where
entry_kind='NativeOrder::on_update' and
recent_trace.cor_event = 0 and
recent_trace.channel = requote_events.channel and
recent_trace.session_start = requote_events.session_start and
recent_trace.sn = requote_events.sn and
recent_trace.session_start = session.session_start ;
delete from trace where
rowid in (select rowid from trace order by rowid desc limit -1 offset 10000123);
+1 -1
View File
@@ -1 +1 @@
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.cpp;orderman/actors/MDSupplementarySource.cpp;orderman/actors/OrderStore.cpp;orderman/actors/SODPosition.cpp;orderman/actors/SecuritySrc.cpp;orderman/actors/ServiceBridge.cpp;orderman/actors/order_manager/OrderBuilder.cpp;orderman/actors/order_manager/OrderManager.cpp;orderman/actors/order_manager/OrderServicesData.cpp;orderman/events/CancelRejectEvent.cpp;orderman/events/NewExecutionReportEvent.cpp;orderman/limiter/LimitStore.cpp;orderman/limiter/Limiter.cpp;orderman/limiter/ProductLimitNode.cpp;orderman/limiter/ProductLimitTree.cpp;orderman/limiter/SecurityLimitNode.cpp;orderman/limiter/SecurityLimitTree.cpp;orderman/limiter/tests/limit_tests.cpp;orderman/order_factory/DynaLine truncated
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/tests/AuthTokenTests.cpp;utils/thread_manager.cpp)
+1
View File
@@ -0,0 +1 @@
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp;utils/stats.cpp;utils/tests/AuthTokenTests.cpp;utils/thread_manager.cpp)
+1 -1
View File
@@ -141,4 +141,4 @@ if (WIN32)
include(${CMAKE_SOURCE_DIR}/tools/vs_filters.cmake)
endif()
+17
View File
@@ -73,6 +73,8 @@ message CustomOrderDefinition
TriggerOrderDefinition trigger = 22;
CoverOrderDefinition cover = 23;
OffsetOrderDefinition offset = 24;
OCOOrderDefinition oco = 25;
PricerOrderDefinition pricer = 26;
DynamicOrderDefinition dynamic = 1000;
}
@@ -154,6 +156,17 @@ message OffsetOrderDefinition {
uint32 tick_offset_on_deploy = 1;
CustomOrderDefinition child = 6;
}
message OCOOrderDefinition {
CustomOrderDefinition other = 1;
int32 other_ticks = 2;
api.RelativeSide other_side = 4;
CustomOrderDefinition child = 3;
}
message PricerOrderDefinition {
int32 price_offset_ticks = 1;
double price_factor = 2;
CustomOrderDefinition child = 3;
}
message DynamicOrderDefinition {
string dynamic_order_type = 1;
map<string, DynamicOrderParam> order_params = 2;
@@ -203,6 +216,8 @@ message OrderState {
TriggerOrderState trigger = 21;
CoverOrderState cover = 22;
OffsetOrderState offset = 24;
OCOOrderState oco = 25;
PricerOrderState pricer = 26;
DynamicOrderState dynamic = 255; // do not use anything bigger than 255, or things will break
}
@@ -276,6 +291,8 @@ message TopOrderState {}
message TriggerOrderState {}
message CoverOrderState {}
message OffsetOrderState {}
message OCOOrderState {}
message PricerOrderState {}
message DynamicOrderState {}
enum OrderStatus {
+1
View File
@@ -14,6 +14,7 @@ namespace internal {
using namespace ::google::protobuf;
using namespace bts::expression;
using namespace bts::util::proto;
using std::vector;
ConfigValueQueue::ConfigValueQueue(IPropertyEvaluator& evaluator) :
_eval(evaluator),
+4 -3
View File
@@ -110,6 +110,7 @@ message CMEOptions
// Prevents parsing trades/rfqs/doing anything not required for quoting
optional bool quoter_mode = 6;
optional bool verbose_ticker = 7 [default=false];
}
message RecordOptions {
@@ -122,14 +123,14 @@ message RecordOptions {
// Replay options
//
message ReplayOptions {
optional string host = 1 [default = "mdreplay"];
optional string base_path = 2 [default = "/spark"];
optional string file_name = 3 [default = "futures.coconut"];
optional string url = 1 [default = "http://mdreplay.bluetradesys.local/spark/futures.coconut"];
// 2.0 = double speed, 0.5 = half speed
optional double speed = 11 [default = 1.0];
optional bool interactive = 12 [default = false];
optional string fallback = 20 [default ="data_files/futures.coconut"];
}
message RelayOptions {
-1
View File
@@ -3,7 +3,6 @@ package bts.config;
message PerfLogCfg {
optional bool active = 1 [default=true];
optional uint32 recent_traces_size = 2 [default=140123];
optional uint32 max_rows = 3 [default=10000123];
optional uint32 entry_batch_size = 4 [default=200];
}
+10
View File
@@ -3,4 +3,14 @@ package bts.config;
message Spreader{
optional uint32 n_failed_iocs = 10 [default=20];
//if this is set to true, the spreader internally
//generates fake fills to keep the cache (data and
//instruction) warm
message CacheWarming{
optional bool enable_fills = 10 [default=true];
optional bool enable_child_order_activate = 15 [default=true];
optional double fill_period_low = 20 [default=10];
optional double fill_period_high = 30 [default=50];
}
optional CacheWarming cache_warming = 20;
}
+1
View File
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <vector>
using std::vector;
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/stringpiece.h>
@@ -50,6 +50,7 @@
#include <stack>
#include <limits>
#include <vector>
using std::vector;
#include "text_format.h"
+8
View File
@@ -66,6 +66,14 @@ enum Side {
SIDE_SELL = 50;
}
enum RelativeSide {
RELATIVESIDE_UNDEFINED = 0;
RELATIVESIDE_OPPOSITE = 1;
RELATIVESIDE_SAME = 2;
RELATIVESIDE_BUY = 49;
RELATIVESIDE_SELL = 50;
}
enum PutOrCall {
PUTORCALL_PUT = 0;
PUTORCALL_CALL = 1;
@@ -109,7 +109,8 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
_perf_log.log(trace,"before_fix_socket_w");
auto result = ::writev(_fd, iv, (int)vec_size);
_perf_log.log(trace,"after_fix_socket_w");
_perf_log.flush();
if (trace.sn)
_perf_log.flush();
if (_log && result > 0) {
for (size_t i = 0; i < vec_size; i++)
+1 -1
View File
@@ -95,7 +95,7 @@ void configure_logging(bts::config::Logging const&cfg, bts::config::ThreadGroupC
real_cfg.logger_initial_queue_size = cfg.initial_queue_size();
real_cfg.logger_max_queue_growth = cfg.max_queue_growth();
real_cfg.level_names = { {Diag,"Diag"},{Info,"Info"},
real_cfg.level_names = { {NoLog,"NoLog"},{Diag,"Diag"},{Info,"Info"},
{Warn,"Warn"},{Error,"Error"},
{Fatal,"Fatal"},{Sys,"Sys"} };
+2 -1
View File
@@ -13,7 +13,8 @@ namespace bts {
namespace config {class Logging; class ThreadGroupCfg;}
// Please keep this list in sync with Log.cs
constexpr log::Level Diag {0};
constexpr log::Level NoLog {0};
constexpr log::Level Diag {NoLog.ordinal+1};
constexpr log::Level Info {Diag.ordinal+1};
constexpr log::Level Warn {Info.ordinal+1};
constexpr log::Level Error {Warn.ordinal+1};
+10 -2
View File
@@ -3,6 +3,8 @@
#include "lib/utils/TimeStamp.h"
#include "lib/utils/Dispatcher.h"
#include "lib/utils/datetime.h"
#include "lib/config/protos/system.pb.h"
#include "lib/utils/thread_manager.h"
#include <memory>
#include <string>
@@ -150,7 +152,11 @@ void write_header(PrintFormat format, long seq_mod, const std::vector<Entry>& en
}
}
void run_periodic_dumper(const std::set<UserSymbol>& symbols, uint32_t seq_mod, PrintFormat format) {
void run_periodic_dumper(const std::set<UserSymbol>& symbols, uint32_t seq_mod, PrintFormat format, bool monitor_all) {
auto qualifier = config::get<config::System>().qualifier();
auto thread_name = "tail-" + qualifier;
threading::configure_current_thread(thread_name);
MDConsumer consumer(symbols, nullptr);
@@ -160,7 +166,9 @@ void run_periodic_dumper(const std::set<UserSymbol>& symbols, uint32_t seq_mod,
entries.push_back({ 0, handle, std::make_unique<api::Security>(sec) });
});
entries = filter_entries(entries);
if (!monitor_all) {
entries = filter_entries(entries);
}
write_header(format, seq_mod, entries);
+2 -1
View File
@@ -10,7 +10,8 @@ enum class PrintFormat {
CSV
};
void run_periodic_dumper(const std::set<UserSymbol>&, uint32_t seq_mod, PrintFormat format);
void run_periodic_dumper(const std::set<UserSymbol>&, uint32_t seq_mod, PrintFormat format, bool monitor_all);
}}//ns
+26 -3
View File
@@ -182,23 +182,46 @@ void replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms)
auto filename = url.substr(last_slash+1);
filename = os::sparkdata_path("data/replay/") + filename;
utils::download_file_if_newer(host, path, filename);
replay_file(filename, writer, naggle_ms, true);
if (boost::filesystem::exists(filename)) {
replay_file(filename, writer, naggle_ms, true);
}
}
void replay_url_with_fallback(const std::string& url, IRelayWriter& writer, int32_t naggle_ms) {
// loops forever
replay_url(url, writer, naggle_ms);
// If it returns it means it failed to download or play cached
const auto fallback = config::get<ReplayOptions>().fallback();
if (boost::filesystem::exists(fallback)) {
replay_file(fallback, writer, naggle_ms);
}
}
shared_ptr<IRelayWriter> mkrelayproducer(bool shared);
/*this is the public one*/
void replay_recording(const string& filename, int32_t naggle_ms) {
void replay_recording(const string& filename, int32_t naggle_ms, const std::string fallback) {
auto writer = mkrelayproducer(true);
if (config::get<ReplayOptions>().interactive()) {
naggle_ms = -1;
}
// Try relay_url/replay_file
// If it fails resort to the fallback location
if (filename.find("http://") == 0) {
// loops forever
replay_url(filename, *writer, naggle_ms);
}
else {
else if (boost::filesystem::exists(filename)) {
// loops forever
replay_file(filename, *writer, naggle_ms);
}
// try fallback
if (boost::filesystem::exists(fallback)) {
replay_file(fallback, *writer, naggle_ms);
}
}
void print_recording(const string& filename) {
ConsoleReplayWriter writer;
+2 -1
View File
@@ -28,8 +28,9 @@ void record_everything(std::string path, std::string filename);
void print_recording(const std::string&);
void unprint_recording(const std::string& src, const std::string& dest);
void trace_recording(const std::string& file, security_key key);
void replay_recording(const std::string&, int32_t delay_ms);
void replay_recording(const std::string&, int32_t delay_ms, const std::string fallback="");
void replay_url(const std::string& url, IRelayWriter& writer, int32_t naggle_ms);
void replay_url_with_fallback(const std::string& url, IRelayWriter& writer, int32_t naggle_ms);
void replay_file(const std::string& filename, IRelayWriter& writer, int32_t naggle_ms, bool loop=false);
}}//namespace
+151 -35
View File
@@ -1,6 +1,7 @@
#pragma once
#include "lib/logging/logging.h"
#include "lib/md/IntPrice.h"
#include "lib/md/MDConsumer.h"
#include "lib/md/MDConsumerHandle.h"
#include "lib/md/cme/MDP3Utils.h"
@@ -9,9 +10,13 @@
#include "lib/mercury/events/TimerEvent.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/events/MDRequestEvent.h"
#include "lib/orderman/events/TickerUpdateEvent.h"
#include "lib/tracing/PerfLog.h"
#include "lib/tracing/bts_trace.h"
#include "lib/utils/TimeStamp.h"
#include "lib/domain_model/Security.h"
#include <date/date.h>
namespace bts {
namespace orderman {
@@ -25,9 +30,10 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
public:
MDSource(md::MDConsumer &consumer, metrics::MetricsContainer &stats,
trace::PerfLog &log)
: _handles(), _md_consumer(consumer), _book_update(), _perf_log(log),
_stats(stats), _loop_timer() {
: _handles(), _md_consumer(consumer), _book_update(), _ticker_update(),
_perf_log(log), _stats(stats), _loop_timer() {
_trades_buffer.Reserve(1000);
auto cme_channels = md::get_configured_cme_channels();
for (auto const &c : cme_channels)
for (auto const &s : c.symbols)
@@ -56,47 +62,43 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
auto synthetic_count_metric =
std::make_shared<metrics::CounterMetric<int>>(
"spark_mdsource_synthetic_count");
auto ticker_channel_cout_metric =
std::make_shared<metrics::CounterMetric<int>>(
"spark_mdsource_ticker_channel_count");
_stats.publish(security_count_metric);
_stats.publish(synthetic_count_metric);
_stats.publish(ticker_channel_cout_metric);
_book_update = make_stream<BookUpdateEvent>();
subscribe<MDRequestEvent>([this, security_count_metric,
synthetic_count_metric](auto const &update) {
if (update.events) {
for (auto const &ev : *update.events) {
auto k = ev.security_key.value();
_ticker_update = make_stream<TickerUpdateEvent>();
subscribe<MDRequestEvent>(
[this, security_count_metric, synthetic_count_metric,
ticker_channel_cout_metric](auto const &update) {
if (update.events) {
for (auto const &ev : *update.events) {
auto k = ev.security_key;
for (auto &h : _handles)
if (h.handle.key() == k) {
// we want to resend this MD so that late joiners can
// catch up
h.last = utils::TimeStamp();
return;
}
switch (ev.md_type) {
case MDRequestType::BOOK:
on_book_sub(k, *synthetic_count_metric);
security_count_metric->set(_handles.size());
break;
case MDRequestType::TICKER:
on_ticker_sub(k, *ticker_channel_cout_metric);
break;
}
}
}
});
LOG(OrderMan, Warn,
"Starting to publish book updates for security key: {}", k);
auto handle = _md_consumer.get_handle(k);
auto &channel = _symbol_to_channel[handle.get_usersymbol()];
if (channel.empty())
channel = "sim_md";
_handles.push_back(
{utils::TimeStamp(), handle, channel.c_str()});
if (handle.is_synthetic()) {
synthetic_count_metric->increment();
}
}
security_count_metric->set(_handles.size());
}
});
security_count_metric->set(_handles.size());
}
private:
void on_poll() override {
Book book;
// Book Updates
Book book;
auto start = utils::TimeStamp::now();
for (auto &h : _handles) {
auto &handle = h.handle;
@@ -104,24 +106,138 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
&book.bid()[0],
&book.ask()[0]) == md::HandleReadResult::SUCCESS) {
book._security_key = SecurityKey(handle.key());
trace::CorID trace{h.channel, handle.sequence()};
trace::CorID trace{h.external_event_channel, handle.sequence()};
_perf_log.log(trace, h.last, "md", handle.key());
_perf_log.log(trace, "md_r", h.last, h.symbol->c_str(),
handle.key());
publish(_book_update, book, trace);
}
}
// Ticker Updates
for (auto &c : _channels) {
auto &channel = c.channel;
_trades_buffer.Clear();
c.cursor = channel.read_trades(_trades_buffer, c.cursor);
for (auto const &trade : _trades_buffer) {
size_t trade_count = 0;
for (auto &per_sec_ev : c.securities) {
if (trade.key() == per_sec_ev.key().value()) {
trade_count++;
per_sec_ev.trades.emplace_back(TickerUpdateEvent::Trade{
datetime::utc_ll_to_timepoint(trade.time()),
Price(md::IntPrice(trade.mantissa(),
(int8_t)trade.exponent())
.price()),
Quantity(trade.size()), Quantity(trade.total_volume()),
trade.aggressor_side()});
}
}
if (trade_count)
for (auto &per_sec_ev : c.securities)
if (!per_sec_ev.trades.empty()) {
publish(_ticker_update, per_sec_ev);
per_sec_ev.trades.clear();
}
}
}
// Publish stats
auto stop = utils::TimeStamp::now();
_loop_timer->observe((start - stop).nanoseconds());
}
void on_book_sub(SecurityKey sk,
metrics::CounterMetric<int> &synthetic_count_metric) {
auto k = sk.value();
for (auto &h : _handles)
if (h.handle.key() == k) {
// we want to resend this MD so that late joiners can
// catch up
h.last = utils::TimeStamp();
return;
}
LOG(OrderMan, Warn,
"Starting to publish book updates for security key: {}", k);
auto handle = _md_consumer.get_handle(k);
_handles.push_back({utils::TimeStamp(), handle,
get_external_event_channel(handle.get_usersymbol()),
std::make_unique<std::string>(securities::descriptor(
handle.get_normalized_security()))});
if (handle.is_synthetic())
synthetic_count_metric.increment();
}
const char *get_external_event_channel(const md::UserSymbol &user_symbol) {
auto &channel = _symbol_to_channel[user_symbol];
if (channel.empty())
channel = "sim_md";
return channel.c_str();
}
void on_ticker_sub(SecurityKey sk,
metrics::CounterMetric<int> &channel_count_metric) {
auto user_symbol = _md_consumer.get_handle(sk.value()).get_usersymbol();
for (auto &ch : _channels)
if (ch.user_symbol == user_symbol) {
auto it =
std::find_if(ch.securities.begin(), ch.securities.end(),
[sk](auto const &ev) { return ev.key() == sk; });
if (it == ch.securities.end()) {
// first time we are asked for this security, we will
// publish the trades for it next time next round
LOG(OrderMan, Warn,
"Starting to publish ticker updates on user symbol {} for "
"security key: {} (sec count for the symbol: {} )",
user_symbol.to_string(), sk.value(),
ch.securities.size() + 1);
ch.securities.push_back(sk);
}
return;
}
// this is a channel we are not subscribed to
channel_count_metric.increment();
LOG(OrderMan, Warn,
"Starting to publish ticker updates on user symbol {} for security "
"key: {}",
user_symbol.to_string(), sk.value());
// We do not want to get old trades so we drain the trades buffer at
// this point. This makes the first subscription to a channel
// consistent with subsquent ones in terms of late join semantics.
ChannelRecord c{
0, _md_consumer.get_channel(user_symbol), {}, user_symbol};
_trades_buffer.Clear();
c.cursor = c.channel.read_trades(_trades_buffer, c.cursor);
c.securities.push_back(sk);
_channels.push_back(c);
}
struct HandleRecord {
utils::TimeStamp last;
md::MDConsumerHandle handle;
const char *channel;
const char *external_event_channel;
std::unique_ptr<std::string> symbol;
};
boost::container::small_vector<HandleRecord, 100> _handles;
struct ChannelRecord {
int64_t cursor;
co2::MDConsumerChannel channel;
boost::container::small_vector<TickerUpdateEvent, 5> securities;
md::UserSymbol user_symbol;
};
boost::container::small_vector<ChannelRecord, 3> _channels;
google::protobuf::RepeatedPtrField<co2::TradeMessage> _trades_buffer;
std::vector<HandleRecord> _handles;
md::MDConsumer _md_consumer;
mercury::Stream<BookUpdateEvent> _book_update;
mercury::Stream<TickerUpdateEvent> _ticker_update;
trace::PerfLog &_perf_log;
metrics::MetricsContainer &_stats;
std::shared_ptr<metrics::HistogramMetric<30>> _loop_timer;
@@ -11,7 +11,7 @@ namespace orderman {
class ChildOrder {
public:
ChildOrder()
: _order(nullptr), _parent_order_services(nullptr), _active(false) {}
: _order(nullptr), _parent_order_services(nullptr), _child_order_services(nullptr), _active(false) {}
ChildOrder(Order &o, OrderServices &services)
: _order(&o), _parent_order_services(&services),
@@ -39,6 +39,11 @@ class ChildOrder {
bool is_active() const { return _active; }
void warm_up_activate() {
bts_assert(!_active);
_parent_order_services->warm_up_activate_child(*_order);
}
void activate(Price p, Quantity q, trace::CorID const &trace = trace::CorID()) {
bts_assert(_order);
bts_assert(!_active);
@@ -407,13 +407,18 @@ void OrderManager::init_order_services() {
// md subs are peculiar in that we try to make them on demand, so
// we publish an event announcing the subscription
std::function<void(SecurityKey)> on_md_sub = [this](SecurityKey k) {
publish(me->services_data.md_request_pub, k);
publish(me->services_data.md_request_pub, k,MDRequestType::BOOK);
};
std::function<void(SecurityKey)> on_ticker_sub = [this](SecurityKey k) {
publish(me->services_data.md_request_pub, k,MDRequestType::TICKER);
};
std::function<void(SecurityKey)> on_settlement_sub = [this](SecurityKey k) {
publish(me->services_data.settlement_request_pub, k);
};
me->services_data.md_subs =
std::make_unique<ContentSubscription<BookUpdateEvent>>(*this, on_md_sub);
me->services_data.ticker_subs =
std::make_unique<ContentSubscription<TickerUpdateEvent>>(*this, on_ticker_sub);
me->services_data.settlement_subs =
std::make_unique<ContentSubscription<SettlementUpdateEvent>>(
*this, on_settlement_sub);
@@ -5,6 +5,7 @@
#include "lib/logging/logging.h"
#include "lib/orderman/actors/order_manager/OrderManager.h"
#include "lib/orderman/actors/order_manager/OrderServicesData.h"
#include "lib/orderman/events/TickerUpdateEvent.h"
#include "lib/orderman/governor/Governor.h"
#include "lib/orderman/orderman_utils/EventStream.h"
#include "lib/orderman/orders/Order.h"
@@ -108,8 +109,8 @@ class OrderServices {
OrderServices(Order &owner)
: _busy(false), _pending_calls(), _owner(&owner), _data(),
_parent(nullptr), _child_update_cb([](auto const &) {}),
_book_updates(), _settlement_updates(), _time_change(), _sessions(),
_create_order_reply() {}
_book_updates(), _ticker_updates(), _settlement_updates(),
_time_change(), _sessions(), _create_order_reply() {}
void init(OrderServicesData &data, OrderServices &parent) {
_parent = &parent;
@@ -119,10 +120,12 @@ class OrderServices {
void init(OrderServicesData &data) {
_data = &data;
_book_updates.init(*data.md_subs);
_ticker_updates.init(*data.ticker_subs);
_settlement_updates.init(*data.settlement_subs);
_time_change.init(*data.time_subs);
for (auto &sd : data.sessions)
_sessions.emplace_back(*sd);
if (_sessions.empty())
for (auto &sd : data.sessions)
_sessions.emplace_back(*sd);
_create_order_reply.init(*data.order_creation_subs);
}
@@ -147,17 +150,19 @@ class OrderServices {
ss.queue_fix(mt, f, trace);
}
void log(trace::CorID cid, const char *entry_kind,
uint64_t uint64_1 = 0,const char *txt_1 = "",uint32_t uint32_1 = 0) {
void log(trace::CorID cid, const char *entry_kind, uint64_t uint64_1 = 0,
const char *txt_1 = "", uint32_t uint32_1 = 0) {
_data->log.log(cid, entry_kind, uint64_1, txt_1, uint32_1);
}
trace::ScopedPerfLogEntry log_scope(trace::CorID cid, const char *entry_kind,
uint64_t uint64_1 = 0,const char *txt_1 = "",uint32_t uint32_1 = 0)
{
return trace::ScopedPerfLogEntry(_data->log,cid,entry_kind,uint64_1,txt_1,uint32_1);
uint64_t uint64_1 = 0,
const char *txt_1 = "",
uint32_t uint32_1 = 0) {
return trace::ScopedPerfLogEntry(_data->log, cid, entry_kind, uint64_1,
txt_1, uint32_1);
}
void report_state(const api::orderman::OrderState &state,
trace::CorID const &trace = trace::CorID()) {
@@ -172,6 +177,7 @@ class OrderServices {
_owner->key().value());
unsubscribe_from_child_updates();
_book_updates.unsub_all();
_ticker_updates.unsub_all();
_settlement_updates.unsub_all();
_time_change.unsub_all();
_create_order_reply.unsub_all();
@@ -199,7 +205,13 @@ class OrderServices {
k, [this, cb](auto const &ev) { this->on_external_event(ev, cb); });
}
void unsubscribe_from_book(BookSubID sid) { _book_updates.unsub(sid); }
// Ticker
using TickerSubID = EventStream<TickerUpdateEvent>::SubID;
template <class CB> TickerSubID subscribe_to_ticker(SecurityKey k, CB &&cb) {
return _ticker_updates.sub(
k, [this, cb](auto const &ev) { this->on_external_event(ev, cb); });
}
void unsubscribe_from_ticker(TickerSubID sid) { _ticker_updates.unsub(sid); }
// Settlement
using SettlementSubID = EventStream<SettlementUpdateEvent>::SubID;
template <class CB>
@@ -223,8 +235,8 @@ class OrderServices {
// Execution Reports
using ExeReportSubID = EventStream<NewExecutionReportEvent>::SubID;
template <class CB>
void subscribe_to_exe_report(SessionServices &ss, OrderKey k, CB &&cb) {
ss.subscribe_to_exe_report(
ExeReportSubID subscribe_to_exe_report(SessionServices &ss, OrderKey k, CB &&cb) {
return ss.subscribe_to_exe_report(
k, [this, cb](auto const &ev) { this->on_external_event(ev, cb); });
}
void unsubscribe_from_exe_report(SessionServices &ss, ExeReportSubID sid) {
@@ -321,8 +333,13 @@ class OrderServices {
OrderServices &activate_child(Order &o, Price p, Quantity q,
trace::CorID const &trace) {
return _data->owner.activate_child(*this, o, p, q, trace);
return _data->owner.activate_child(*this, o, p, q, trace);
}
void warm_up_activate_child(Order &o) {
o.warm_up_activate(*_data,*this);
}
void update(Price p, Quantity q, trace::CorID const &trace) {
on_inter_order_call([p, q, trace](OrderServices &self) {
self._owner->on_update(p, q, trace);
@@ -350,6 +367,7 @@ class OrderServices {
OrderServices *_parent;
std::function<void(OrderStateUpdateEvent const &)> _child_update_cb;
EventStream<BookUpdateEvent> _book_updates;
EventStream<TickerUpdateEvent> _ticker_updates;
EventStream<SettlementUpdateEvent> _settlement_updates;
EventStream<mercury::TimerEvent, mercury::NoKey> _time_change;
boost::container::small_vector<SessionServices, 20> _sessions;
@@ -11,6 +11,7 @@
#include "lib/orderman/actors/ExecutionGateway.h"
#include "lib/orderman/events/ActivateOrderEvent.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/events/TickerUpdateEvent.h"
#include "lib/orderman/events/CancelOrderEvent.h"
#include "lib/orderman/events/CancelRejectEvent.h"
#include "lib/orderman/events/CrackOrderEvent.h"
@@ -83,6 +84,7 @@ class OrderServicesData {
Limiter limiter;
OrderFactory order_factory;
std::unique_ptr<mercury::ContentSubscription<BookUpdateEvent>> md_subs;
std::unique_ptr<mercury::ContentSubscription<TickerUpdateEvent>> ticker_subs;
std::unique_ptr<mercury::ContentSubscription<SettlementUpdateEvent>>
settlement_subs;
std::unique_ptr<
+13 -7
View File
@@ -2,23 +2,29 @@
#include <memory>
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/mercury/events/Event.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
namespace bts {
namespace orderman {
enum class MDRequestType { BOOK, TICKER };
class MDRequestEvent : public mercury::Event<MDRequestEvent> {
public:
public:
using SynopsisPolicy = mercury::CacheAll<MDRequestEvent>;
MDRequestEvent(SecurityKey k) : security_key(k) {}
MDRequestEvent &operator=(MDRequestEvent const&rhs)
{
MDRequestEvent(SecurityKey k, MDRequestType md_type)
: security_key(k), md_type(md_type) {}
MDRequestEvent &operator=(MDRequestEvent const &rhs) {
security_key = rhs.security_key;
md_type = rhs.md_type;
return *this;
}
SecurityKey security_key;
MDRequestType md_type;
};
}}
} // namespace orderman
} // namespace bts
@@ -122,6 +122,12 @@ class OrderStateUpdateEvent : public mercury::Event<OrderStateUpdateEvent> {
case api::orderman::OrderState::kOffset:
result.mutable_offset();
break;
case api::orderman::OrderState::kOco:
result.mutable_oco();
break;
case api::orderman::OrderState::kPricer:
result.mutable_pricer();
break;
case api::orderman::OrderState::kDynamic:
result.mutable_dynamic();
break;
@@ -0,0 +1,35 @@
#pragma once
#include <memory>
#include "lib/mercury/events/Event.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/utils/bts_chrono.h"
#include "lib/md/protos/co2.pb.h"
namespace bts {
namespace orderman {
class TickerUpdateEvent : public mercury::Event<TickerUpdateEvent> {
SecurityKey _key;
public:
using SynopsisPolicy = mercury::NoSynopsis;
SecurityKey key() const {return _key;};
struct Trade {
TimeStamp ts;
Price price;
Quantity quantity;
Quantity total_volume;
co2::AggressorSide aggressor_side;
};
boost::container::small_vector<Trade,5> trades;
TickerUpdateEvent(SecurityKey key) : _key(key), trades() {}
};
}
}
@@ -18,7 +18,8 @@
#include "lib/orderman/orders/book_algos/JoinOrder.h"
#include "lib/orderman/orders/book_algos/LeanOrder.h"
#include "lib/orderman/orders/book_algos/OffsetOrder.h"
#include "lib/orderman/orders/book_algos/TickOrder.h"
#include "lib/orderman/orders/book_algos/OCOOrder.h"
#include "lib/orderman/orders/book_algos/PricerOrder.h"
#include "lib/orderman/orders/book_algos/TopOrder.h"
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
@@ -148,7 +149,7 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
order_ptr.reset(new orderman::TimeBoxOrder());
break;
case CustomOrderDefinition::kTick:
order_ptr.reset(new orderman::TickOrder());
bts_assert(false);
break;
case CustomOrderDefinition::kLean:
order_ptr.reset(new orderman::LeanOrder());
@@ -167,7 +168,13 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
break;
case CustomOrderDefinition::kOffset:
order_ptr.reset(new orderman::OffsetOrder());
break;
break;
case CustomOrderDefinition::kOco:
order_ptr.reset(new orderman::OCOOrder());
break;
case CustomOrderDefinition::kPricer:
order_ptr.reset(new orderman::PricerOrder());
break;
case CustomOrderDefinition::kDynamic: {
order_ptr = me->dynamic_order_registry.build(
def.custom_def().dynamic().dynamic_order_type());
@@ -18,7 +18,6 @@ struct EventStream
void init(CS&csub)
{
bts_assert(!cs);
cs = &csub;
}
+14 -5
View File
@@ -17,8 +17,14 @@ using namespace api::orderman;
struct IcebergOrder::ActiveChild {
ActiveChild() {}
ChildOrder order;
api::orderman::OrderState state;
void clear() { state = api::orderman::OrderState(); }
OrderKey key;
void clear() {
state = api::orderman::OrderState();
key = null_order_key();
}
};
enum class IcebergOrder::WorkingSubStatus { WORKING, PAUSED };
@@ -55,7 +61,7 @@ void IcebergOrder::on_init_new(api::orderman::OrderDefinition const &def,
auto const &iceberg_def = def.custom_def().iceberg();
me->show_quantity = Quantity(iceberg_def.show_quantity());
me->pause_between_orders =
me->pause_between_orders =
config::get<config::OrdermanCfg>().default_iceberg_pause_secs();
auto child_def = def;
@@ -248,11 +254,14 @@ void IcebergOrder::send_next_child_order() {
auto remaining = me->state.quantity() - me->state.filled_quantity();
auto to_send = std::min(remaining, me->show_quantity.value());
me->child.order = me->child_builder.build();
me->child.key = me->child.order.key();
LOG(OrderMan, Info,
"Iceberg order {} creating new child order {} with size "
"{} price {}. Filled {} so far out of {}",
key().value(), me->child.order.key().value(), to_send, me->state.price(),
key().value(), me->child.key.value(), to_send, me->state.price(),
me->state.filled_quantity(), me->state.quantity());
me->child.order.activate(Price(me->state.price()), Quantity(to_send), trace::CorID());
@@ -261,7 +270,7 @@ void IcebergOrder::send_next_child_order() {
void IcebergOrder::on_child_order_update(OrderStateUpdateEvent const &ev) {
switch (me->state.status()) {
case ORDER_STATUS_WORKING:
if (ev.key == me->child.order.key()) {
if (ev.key == me->child.key) {
me->child.state = ev.rebuild_state();
switch (ev.status) {
@@ -313,7 +322,7 @@ void IcebergOrder::on_child_order_update(OrderStateUpdateEvent const &ev) {
LOG(OrderMan, Info,
"Iceberg order {} received update for non active child order {}."
"update state = {}",
key().value(), me->child.order.key().value(),
key().value(), me->child.key.value(),
utils::pb::to_string(ev.rebuild_state()));
}
break;
+139 -116
View File
@@ -58,6 +58,9 @@ struct NativeOrder::PImpl {
jetfix::FIXFragment cancel_encoding;
jetfix::FIXFragment::Bookmark<jetfix::fix::TransactTimeStr>
cancel_transact_time_bm;
OrderServices::ExeReportSubID exe_report_sid;
OrderServices::CancelRejectSubID cancel_reject_sid;
};
NativeOrder::NativeOrder() : me(new PImpl()) {}
@@ -272,135 +275,155 @@ void NativeOrder::on_init_from_exe_report(
NativeOrder::~NativeOrder() {}
void NativeOrder::on_activate(Price p, Quantity q,
bts::orderman::OrderServices &services,
trace::CorID const &trace) {
bool NativeOrder::_on_activate(Price p, Quantity q,
bts::orderman::OrderServices &services,
trace::CorID const &trace) {
auto scoped_log =
services.log_scope(trace, "NativeOrder::on_activate", key().value());
me->state.set_price(p.value());
me->state.set_quantity(q.value());
me->state.set_filled_quantity(0);
me->state.set_last_price(0);
me->state.set_avg_price(0);
me->session_services = &me->services->get_session_services(
get_native_state(me->state).routing_record().session());
me->price_encoding_precision =
me->services->price_encoding_precision(*me->session_services);
// is our gateway connected?
if (!me->services->is_gateway_connected(*me->session_services)) {
LOG(OrderMan, Warn,
"Attempt to send order to a disconnected gateway. "
"Dropping order {}",
key().value());
transition(ORDER_STATUS_REJECTED_BY_SPARK, "Exchange gateway is down");
return false;
}
// can we trade?
auto lqr = me->services->limiter().can_trade(
UserID(me->definition.user_id()),
AccountID(me->definition.account_id()));
if (!lqr.ok) {
LOG(OrderMan, Warn,
"Unauthorized order attempted. User: {} on "
"Account: {}. Msg: {} Order: {}. Dropping order",
me->definition.user(), me->definition.account(), lqr.error_msg,
key().value());
transition(ORDER_STATUS_REJECTED_BY_SPARK,
fmt::format("Unauthorized order: {}", lqr.error_msg));
return false;
}
// can we trade this security, with this size?
lqr = me->services->limiter().can_send_new_order(
AccountID(me->definition.account_id()), *me->sec, me->definition.side(),
q);
if (!lqr.ok) {
LOG(OrderMan, Warn,
"New order would breach limit. User: {} Account: "
"{} Msg: {} Order: {}. Dropping order",
me->definition.user(), me->definition.account(), lqr.error_msg,
key().value());
transition(ORDER_STATUS_REJECTED_BY_SPARK,
fmt::format("Order would breach limit: {}", lqr.error_msg));
return false;
}
// are we under the messaging threshold?
if (!me->services->governor(*me->session_services).can_send()) {
LOG(OrderMan, Warn,
"New order would cause breach of messaging "
"limits. Dropping order {}",
key().value());
transition(ORDER_STATUS_REJECTED_BY_SPARK,
"Messaging threshold breached");
return false;
}
// if we are here we are good.
// subscribe to usefull events
me->exe_report_sid = me->services->subscribe_to_exe_report(
*me->session_services, key(),
[this](auto const &r) { this->on_exe_report(r); });
me->cancel_reject_sid = me->services->subscribe_to_cancel_reject(
*me->session_services, key(),
[this](auto const &cr) { this->on_cancel_reject(cr); });
me->services->subscribe_to_gateway_status(
*me->session_services,
[this](auto const &gw) { this->on_gateway_status(gw); });
// send out order
auto &encoding = me->create_encoding;
switch (me->definition.custom_def().custom_case()) {
case CustomOrderDefinition::kLimit:
case CustomOrderDefinition::kStopLimit:
me->create_price_bm = encoding.overwrite(
me->create_price_bm,
securities::encode_exchange_price(*me->sec, p).value(),
me->price_encoding_precision, price_sz());
break;
case CustomOrderDefinition::kStop:
case CustomOrderDefinition::kMarket:
break;
default:
bts_assert(false);
break;
}
me->create_quantity_bm =
encoding.overwrite(me->create_quantity_bm, q.value(), quantity_sz());
auto const &ts = services.get_time_str();
me->create_transact_time_bm = encoding.overwrite(
me->create_transact_time_bm, &ts.value()[0], ts.value().size());
return true;
}
void NativeOrder::warm_up_activate(OrderServicesData &data,
OrderServices &services) {
bts_assert(!me->services);
me->services = &services;
init_order_services(data, services);
_on_activate(Price(0), Quantity(0), services, trace::CorID{});
me->services->unsubscribe_from_exe_report(*me->session_services,
me->exe_report_sid);
me->services->unsubscribe_from_cancel_reject(*me->session_services,
me->cancel_reject_sid);
me->services->unsubscribe_from_gateway_status(*me->session_services);
me->services = nullptr;
}
void NativeOrder::on_activate(Price p, Quantity q,
bts::orderman::OrderServices &services,
trace::CorID const &trace) {
bts_assert(!me->services);
me->services = &services;
switch (me->state.status()) {
case ORDER_STATUS_INACTIVE: {
case ORDER_STATUS_INACTIVE:
// this is a brand new order
if (_on_activate(p, q, services, trace)) {
me->services->queue_fix(*me->session_services,
jetfix::MessageTypes::NewOrderSingle,
me->create_encoding, trace);
me->state.set_price(p.value());
me->state.set_quantity(q.value());
me->state.set_filled_quantity(0);
me->state.set_last_price(0);
me->state.set_avg_price(0);
me->services->governor(*me->session_services).record_sent();
me->state.set_status(ORDER_STATUS_PENDING_WORKING);
me->session_services = &me->services->get_session_services(
get_native_state(me->state).routing_record().session());
me->price_encoding_precision =
me->services->price_encoding_precision(*me->session_services);
// is our gateway connected?
if (!me->services->is_gateway_connected(*me->session_services)) {
LOG(OrderMan, Warn,
"Attempt to send order to a disconnected gateway. "
"Dropping order {}",
LOG(OrderMan, Info, "Sent new order with key {} to gateway",
key().value());
transition(ORDER_STATUS_REJECTED_BY_SPARK, "Exchange gateway is down");
break;
// early return so we skip the expensive report_state() call
return;
}
// can we trade?
auto lqr = me->services->limiter().can_trade(
UserID(me->definition.user_id()),
AccountID(me->definition.account_id()));
if (!lqr.ok) {
LOG(OrderMan, Warn,
"Unauthorized order attempted. User: {} on "
"Account: {}. Msg: {} Order: {}. Dropping order",
me->definition.user(), me->definition.account(), lqr.error_msg,
key().value());
transition(ORDER_STATUS_REJECTED_BY_SPARK,
fmt::format("Unauthorized order: {}", lqr.error_msg));
break;
}
// can we trade this security, with this size?
lqr = me->services->limiter().can_send_new_order(
AccountID(me->definition.account_id()), *me->sec,
me->definition.side(), q);
if (!lqr.ok) {
LOG(OrderMan, Warn,
"New order would breach limit. User: {} Account: "
"{} Msg: {} Order: {}. Dropping order",
me->definition.user(), me->definition.account(), lqr.error_msg,
key().value());
transition(ORDER_STATUS_REJECTED_BY_SPARK,
fmt::format("Order would breach limit: {}", lqr.error_msg));
break;
}
// are we under the messaging threshold?
if (!me->services->governor(*me->session_services).can_send()) {
LOG(OrderMan, Warn,
"New order would cause breach of messaging "
"limits. Dropping order {}",
key().value());
transition(ORDER_STATUS_REJECTED_BY_SPARK,
"Messaging threshold breached");
break;
}
// if we are here we are good.
// subscribe to usefull events
me->services->subscribe_to_exe_report(
*me->session_services, key(),
[this](auto const &r) { this->on_exe_report(r); });
me->services->subscribe_to_cancel_reject(
*me->session_services, key(),
[this](auto const &cr) { this->on_cancel_reject(cr); });
me->services->subscribe_to_gateway_status(
*me->session_services,
[this](auto const &gw) { this->on_gateway_status(gw); });
// send out order
auto &encoding = me->create_encoding;
switch (me->definition.custom_def().custom_case()) {
case CustomOrderDefinition::kLimit:
case CustomOrderDefinition::kStopLimit:
me->create_price_bm = encoding.overwrite(
me->create_price_bm,
securities::encode_exchange_price(*me->sec, p).value(),
me->price_encoding_precision, price_sz());
break;
case CustomOrderDefinition::kStop:
case CustomOrderDefinition::kMarket:
break;
default:
bts_assert(false);
break;
}
me->create_quantity_bm =
encoding.overwrite(me->create_quantity_bm, q.value(), quantity_sz());
auto const &ts = services.get_time_str();
me->create_transact_time_bm = encoding.overwrite(
me->create_transact_time_bm, &ts.value()[0], ts.value().size());
me->services->queue_fix(*me->session_services,
jetfix::MessageTypes::NewOrderSingle,
me->create_encoding, trace);
me->services->governor(*me->session_services).record_sent();
me->state.set_status(ORDER_STATUS_PENDING_WORKING);
LOG(OrderMan, Info, "Sent new order with key {} to gateway",
key().value());
// early return so we skip the expensive report_state() call
return;
}
break;
// all cases below are for orders recovered from disk
case ORDER_STATUS_WORKING:
+37 -25
View File
@@ -1,9 +1,9 @@
#pragma once
#include <memory>
#include "lib/orderman/orders/Order.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/orderman/orders/Order.h"
#include "lib/tracing/PerfLog.h"
#include <memory>
namespace bts {
namespace orderman {
@@ -13,45 +13,57 @@ class CancelRejectEvent;
class GatewayStatusChangeEvent;
class ExecutionReport;
class NativeOrder : public Order
{
public:
class NativeOrder : public Order {
public:
NativeOrder();
~NativeOrder() override;
api::orderman::OrderDefinition const &definition() const override;
void on_init_new(api::orderman::OrderDefinition const&,
const PreActivationOrderServices&) override;
void on_init_recovered(api::orderman::OrderRecord const&,
void on_init_new(api::orderman::OrderDefinition const &,
const PreActivationOrderServices &) override;
void on_init_recovered(api::orderman::OrderRecord const &,
const PreActivationOrderServices &) override;
void on_activate(Price,Quantity,OrderServices &, trace::CorID const&) override ;
void on_update(Price , Quantity,trace::CorID const&) override ;
void on_crack(trace::CorID const&) override ;
void on_cancel(trace::CorID const&) override ;
void on_activate(Price, Quantity, OrderServices &,
trace::CorID const &) override;
void on_update(Price, Quantity, trace::CorID const &) override;
void on_crack(trace::CorID const &) override;
void on_cancel(trace::CorID const &) override;
void on_init_from_exe_report(ExecutionReport const&,const PreActivationOrderServices &);
private:
void on_init_from_exe_report(ExecutionReport const &,
const PreActivationOrderServices &);
protected:
void warm_up_activate(OrderServicesData &,OrderServices&) override;
private:
struct PImpl;
std::unique_ptr<PImpl> me;
bool _on_activate(Price, Quantity, bts::orderman::OrderServices &,
trace::CorID const &);
void on_exe_report(const NewExecutionReportEvent &);
void on_cancel_reject(const CancelRejectEvent&);
void on_gateway_status(const GatewayStatusChangeEvent&);
void report_state(trace::CorID const&trace = trace::CorID());
void on_cancel_reject(const CancelRejectEvent &);
void on_gateway_status(const GatewayStatusChangeEvent &);
void report_state(trace::CorID const &trace = trace::CorID());
void init_fix();
void add_exchange_order_id_to_fix();
void transition(api::orderman::OrderStatus new_state,std::string const&msg);
void transition(api::orderman::OrderStatus new_state,
std::string const &msg);
void transition(api::orderman::OrderStatus new_state);
void transition_to_error(api::orderman::NativeOrderState::SubError sub_error,
std::string const&msg);
std::string const &msg);
void update_working_position();
bool has_exchange_order_id() ;
bool has_exchange_order_id();
Quantity position_contribution();
bool is_old_exe_report( api::orderman::NativeOrderState &, const NewExecutionReportEvent &) const;
bool is_old_exe_report(api::orderman::NativeOrderState &,
const NewExecutionReportEvent &) const;
bool terminal_state(api::orderman::OrderStatus status) const;
api::orderman::NativeOrderState& get_native_state(api::orderman::OrderState &);
api::orderman::NativeOrderState &
get_native_state(api::orderman::OrderState &);
};
}}
} // namespace orderman
} // namespace bts
+6 -2
View File
@@ -53,14 +53,18 @@ class Order {
virtual void on_crack(trace::CorID const&) = 0;
virtual void on_cancel(trace::CorID const&) = 0;
protected:
virtual void warm_up_activate(OrderServicesData&,OrderServices&) {}
void init_order_services(OrderServicesData &);
void init_order_services(OrderServicesData &,OrderServices &parent);
private:
OrderKey _key;
OrderKey _parent_key;
std::unique_ptr<OrderServices> _services;
void init_order_services(OrderServicesData &);
void init_order_services(OrderServicesData &,OrderServices &parent);
friend class OrderFactory;
friend class OrderManager;
friend class OrderServices;
};
}
}
Loaded 100 of 187 files, more files were not shown because too many files have changed in this diff. Show more