Compare commits

...
381 Commits
Author SHA1 Message Date
Kevin Darby f0abb668a0 Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel_algos 2019-08-02 17:36:16 -04:00
Kevin Darby 6a0f45ed72 hedge 2019-08-02 17:28:57 -04:00
Kevin Darby 83adfb8361 Merged in prorata (pull request #239)
RTM: Prorata
2019-08-02 21:20:34 +00:00
Kevin Darby 9baa81b14f experimenting with reaction to fills 2019-08-02 11:44:46 -04:00
Kevin Darby a43e649c06 trying to work in fills and timers 2019-08-01 18:55:38 -04:00
Kevin Darby 6bf39b43f8 remove admin service from excel api 2019-08-01 16:53:29 -04:00
Kevin Darby 94e77ce2b8 fix merge 2019-08-01 09:56:54 -04:00
Kevin Darby 851a34449a incorporate ticker 2019-07-31 18:00:00 -04:00
Kevin Darby 5609b8d80d Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel_algos 2019-07-31 16:17:39 -04:00
Kevin Darby dffb9018da join works 2019-07-31 15:46:07 -04:00
Kevin Darby d4a97f4b0c Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-07-31 15:02:29 -04:00
Kevin Darby aec1d1ee6e fix snapshots 2019-07-30 23:41:47 -04:00
Kevin Darby fa4b4fc934 merge 2019-07-30 18:32:38 -04:00
Pedro Pinto ee94289f22 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2019-07-26 17:15:42 -04:00
Pedro Pinto 45ba5746a6 Added tool tip for ticks off Top algo parameter. 2019-07-26 17:15:23 -04:00
Michael Harding a327ffe7e7 Delay for limit removal 2019-07-26 17:15:00 -04:00
Eric Harding 2feef62b65 Merged in SP-141 (pull request #307)
RTM SP-141 await_cme_config() from all places that need it
2019-07-26 19:12:56 +00:00
Pedro Pinto 4b1afd914e Updated release notes. 2019-07-26 15:12:36 -04:00
Pedro Pinto 3048cabb56 Merge branch 'master' into SP-141 2019-07-26 15:04:56 -04:00
Pedro Pinto 3ad41adc48 Merge branch 'master' into SP-141 2019-07-26 14:49:07 -04:00
Eric Harding e2d0d8bbae Merged in SP-23-Dorman2 (pull request #303)
RTM: SP-23 Dorman2

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2019-07-26 18:44:22 +00:00
Pedro Pinto a6b803b4c3 Updated release notes. 2019-07-26 14:43:30 -04:00
Pedro Pinto 1d68aa7fa8 Merge branch 'master' into SP-23-Dorman2 2019-07-26 14:30:47 -04:00
Pedro Pinto d726018b15 Merge branch 'master' into SP-23-Dorman2 2019-07-26 14:30:01 -04:00
John Colvin 437158e7f3 Merged in SP-90-hide-workspaces (pull request #299)
RTM SP-90 hide workspaces

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-07-26 15:06:47 +00:00
Pedro Pinto c670d0012b Updated release notes. 2019-07-26 11:06:29 -04:00
Pedro Pinto 8751a8e496 Merge branch 'master' into SP-90-hide-workspaces 2019-07-26 11:05:46 -04:00
Karl Ellefsen 6756ba44eb Top_cover_TEST.txt edited online with Bitbucket 2019-07-25 17:58:20 +00:00
Pedro Pinto d8b8a5d710 Updated release notes. 2019-07-23 15:44:49 -04:00
Pedro Pinto 5536f5f585 Cover should always detach from hedges when completing. 2019-07-23 15:04:11 -04:00
John Colvin 7367673ba4 flip 'login/cancel' buttons to follow ok/cancel pattern 2019-07-23 13:53:45 -05:00
John Colvin 901988ec2b center-align loginview's reject message 2019-07-23 13:48:05 -05:00
John Colvin b676d22f1d rearrange loginview grid to reduce dead space; unify visual state transitions 2019-07-23 13:42:45 -05:00
John Colvin 9b7e877a0b Merge branch 'master' into SP-90-hide-workspaces 2019-07-23 13:40:15 -05:00
Eric Harding 16d5341285 Remove unused messages (warnings) 2019-07-23 11:17:15 -04:00
Eric Harding 2a16ac77fb Clean up some indentation warnings 2019-07-23 10:45:11 -04:00
Eric Harding 9eb76bdfbc Fix prorata build 2019-07-23 10:42:41 -04:00
Kevin Darby 49c584047a . 2019-07-20 00:23:19 -04:00
Kevin Darby 773ea38ffa template problem 2019-07-20 00:15:24 -04:00
Kevin Darby 81b7770151 template instantiation...oh man 2019-07-19 23:54:37 -04:00
Kevin Darby e480b8951f indentation 2019-07-19 22:42:42 -04:00
Kevin Darby 781cf3ea6d looking good, time for testing 2019-07-19 18:14:27 -04:00
Eric Harding a658ef7711 Add SplitterAlgo to the ladder/study options 2019-07-19 15:32:06 -04:00
Kevin Darby 5ae750b2f1 Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-07-19 11:33:03 -04:00
Eric Harding 23bbe82080 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SP-23-Dorman2 2019-07-19 11:26:20 -04:00
Eric Harding bd30c3f270 Add Splitter algo to the manual 2019-07-19 11:25:59 -04:00
Eric Harding 4d316034aa Fix sorting in manual (sections and pages sort separately). 2019-07-19 11:03:45 -04:00
Kevin Darby 93f86ecb94 working on the sinks 2019-07-18 22:43:47 -04:00
Kevin Darby f360b79d27 have to introduce a setupsink to better jive with our setup / launch framework 2019-07-18 17:23:08 -04:00
Eric Harding 6b582e224c SP-141 await_cme_config() from all places that need it 2019-07-18 17:10:12 -04:00
Kevin Darby 7bf3c51380 Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel_algos 2019-07-18 10:43:13 -04:00
Kevin Darby 1192182bc8 iterators are hard 2019-07-18 10:39:02 -04:00
Eric Harding e3a366a386 SP-23 code review feedback / code shuffling 2019-07-18 09:51:19 -04:00
Kevin Darby 419cac3e3d iterator 2019-07-17 18:33:53 -04:00
Kevin Darby 2e8dbd8478 working on this damn iterator 2019-07-17 18:29:01 -04:00
Kevin Darby 938db6b616 debugging the generated code 2019-07-17 18:24:49 -04:00
Kevin Darby d6bc9298cc the generated c++ builds 2019-07-17 17:03:28 -04:00
Pedro Pinto f0003ff3c6 Style and doc improvement in IAlgoOrder.h 2019-07-17 17:02:34 -04:00
Pedro Pinto df5f8f6638 Fixed clang build 2019-07-17 15:05:11 -04:00
Kevin Darby d7ea71de84 now shove this c++ back into the sdk 2019-07-17 14:27:22 -04:00
Eric Harding 3ff26f6bb0 SP-23 Add ReportStateGuard to AlgoWrapperOrder::subscribe_ticker 2019-07-17 13:42:07 -04:00
Kevin Darby c8badf5689 Merged in fix_span_error_message (pull request #306)
guard against UI setting cash and leverage factors for accounts I don't know about when span is off

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-17 14:51:23 +00:00
Kevin Darby 58533bf16f guard against UI setting cash and leverage factors for accounts I don't know about when span is off 2019-07-17 10:48:16 -04:00
Kevin Darby 2881e5ea32 Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel_algos 2019-07-17 10:37:53 -04:00
Pedro Pinto 6b3c3ded68 Fixed up release notes. 2019-07-17 10:24:51 -04:00
Pedro Pinto d65e00fade Merged in SP-132-make-algo-shutdown-orderly (pull request #305)
RTM. SP-132 make algo shutdown orderly
2019-07-17 14:23:09 +00:00
Pedro Pinto 53e787ab7f Merge branch 'master' into SP-90-hide-workspaces 2019-07-17 09:37:48 -04:00
Pedro Pinto 68e5d07aef Merge branch 'master' of bitbucket.org:bluetradesys/spark 2019-07-17 09:36:15 -04:00
Pedro Pinto 68345a181e Updated release notes. 2019-07-17 09:35:43 -04:00
Kevin Darby 69c121a19c Merged in fix_span (pull request #301)
fix crash where account name > 12 chars, tested with long account name

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-17 13:32:33 +00:00
Pedro Pinto 780ce7416f Merge branch 'master' into SP-132-make-algo-shutdown-orderly 2019-07-17 09:07:54 -04:00
Pedro Pinto 9fe338a62a Updated release notes. 2019-07-17 09:07:24 -04:00
Kevin Darby 4fd8530bc7 Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel_algos 2019-07-16 18:17:26 -04:00
Kevin Darby ffd5915c52 solidified concept of a dual for bts functions, disallowed algogen access to unmarked bts functions 2019-07-16 18:09:49 -04:00
Jake Norbut f027af66c8 Merged in ob_price_filter_tick_comparison (pull request #304)
RTM - Ob price filter tick comparison

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2019-07-16 21:43:23 +00:00
Jake Norbut 6b2708b160 PR suggestions 2019-07-16 16:34:59 -05:00
Jake Norbut 534474621f price filter should use new add_price tick-based method 2019-07-16 15:51:27 -05:00
Jake Norbut 554614ab72 add means to perform price comparsion based on tick equality 2019-07-16 15:50:44 -05:00
Pedro Pinto 2ede69a263 Updated release notes. 2019-07-16 16:21:31 -04:00
Eric Harding ffd7a9379f SP-23 Dorman tweak. Make this an error instead of cancel. 2019-07-16 16:05:44 -04:00
Pedro Pinto 9def94aceb Changed IB to cancel rather than error if it sees a child cancel.
This allows IB to remain agnostic of wheter the child cancel was a
simply a consequence of the IB itself shutting down, in which case it
would be ignored. If the IB instead errorred out than that error
superseeds the cancel and results in the IB reporting an error.
2019-07-16 15:55:28 -04:00
Pedro Pinto 0af30b3cc0 Changed cover to handle fills while canceling. 2019-07-16 15:32:38 -04:00
Eric Harding f3f34269d3 SP-148 Dorman: pause_until triggers "Unexpected fill decrease." 2019-07-16 14:00:35 -04:00
Eric Harding 595c2df65c SP-147 Splitter UI 2019-07-16 13:26:21 -04:00
Taha Afzal a91c2d9bc6 SP-146 (Limiter will not accept unresolved fills) 2019-07-16 11:20:15 -04:00
Eric Harding 40f6a1db08 Merge branch 'SP-132-make-algo-shutdown-orderly' of bitbucket.org:bluetradesys/spark into SP-23-Dorman2 2019-07-16 11:18:41 -04:00
Pedro Pinto 481c1fd2de Fixed gcc build. 2019-07-16 10:29:59 -04:00
Pedro Pinto e80fc807e0 Moved all reporting state calls to top level event handler.
Any CPU the algo order receives must originate from a top level event,
its simpler to consolidate all report guards in those events, instead
of chasing every call that comes out of the algo.
2019-07-16 10:20:30 -04:00
Pedro Pinto c8e054d079 Added algo wrapper documentation. 2019-07-16 10:10:46 -04:00
Pedro Pinto 9480e76d4b Cleaned up algo wrapper 2019-07-16 10:10:11 -04:00
Pedro Pinto 538411353a Improved cover to hedge while shutting down. 2019-07-16 08:48:36 -04:00
Kevin Darby 70f1552ec6 added Range<T> type so we don't have to convert arrays, hopefully not too slow 2019-07-15 18:41:04 -04:00
Eric Harding acf51e1178 Merge branch 'SP-132-make-algo-shutdown-orderly' of bitbucket.org:bluetradesys/spark into SP-23-Dorman2 2019-07-15 16:44:55 -04:00
Pedro Pinto da6998c391 Do not error out when receiving an udpate from an unknown child.
Because the child is presumably a detached child.
2019-07-15 16:43:39 -04:00
Eric Harding 2710cdd32b Merge branch 'SP-132-make-algo-shutdown-orderly' of bitbucket.org:bluetradesys/spark into SP-23-Dorman2 2019-07-15 16:33:59 -04:00
Pedro Pinto 60786b105c Handled terminate_error while shutting down. 2019-07-15 16:26:22 -04:00
Pedro Pinto f488248abf Fixed handling of child update while shutdown. 2019-07-15 16:03:36 -04:00
Eric Harding 8165d1c9d7 SP-143 Dorman sends profit orders as splitter 2019-07-15 16:02:33 -04:00
Kevin Darby f3114d1664 seems like we need a range type, too much array mapping 2019-07-15 15:04:02 -04:00
Eric Harding 46dce3ac2b SP-115 back out temporary change 2019-07-15 13:36:41 -04:00
Eric Harding e24d8bac7d SP-132 Fix for early return in algo shut down sequence 2019-07-15 12:57:36 -04:00
Eric Harding c434374f74 SP-115 Dorman expects child cancel when paused 2019-07-15 11:28:12 -04:00
Eric Harding 5ba1299ddf Merge SP-132 2019-07-15 11:07:44 -04:00
Eric Harding ea39014307 SP-115 Dorman: handle unexpected child cancel 2019-07-15 10:59:36 -04:00
Pedro Pinto 830b590e5b Algos now go through an orderly shutdown phase.
During which they can still process child updates.
2019-07-15 10:59:13 -04:00
Kevin Darby 88494eb0fc generator looking good 2019-07-15 10:55:19 -04:00
Kevin Darby d5427f6c24 c++ boiler 2019-07-14 23:06:13 -04:00
Kevin Darby 106788c8c8 coming together 2019-07-14 17:20:11 -04:00
Kevin Darby 6968312a04 adding cpp boiler 2019-07-14 16:23:03 -04:00
Kevin Darby 4c9dc9d83b xl issues with arrays causing trouble 2019-07-13 22:15:35 -04:00
Kevin Darby ff193b13f0 typeo 2019-07-12 18:22:47 -04:00
Kevin Darby fc9582850b ranges rolled and unrolled 2019-07-12 18:22:12 -04:00
Eric Harding 1d275b942d SP-136 Splitter should not loop 2019-07-12 17:18:21 -04:00
Kevin Darby c9c93b3cf0 Merged in fix_span1 (pull request #300)
fix crash when acct names are > 12 chars

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-12 18:58:13 +00:00
Kevin Darby f1ae9030d7 Merge branch 'excel_algos' of bitbucket.org:bluetradesys/spark into excel_algos 2019-07-12 13:37:01 -04:00
Kevin Darby fc5439e92e margin crash fix 2019-07-12 13:34:20 -04:00
Eric Harding c40eef7b03 SP-131 2019-07-12 12:04:35 -04:00
Eric Harding 2bafc12502 SP-87 one more splitter test 2019-07-12 11:04:42 -04:00
Kevin Darby 908b50a92a array functions fucking me 2019-07-11 23:57:50 -04:00
Kevin Darby eb7aa17f2e vector generation 2019-07-11 18:30:57 -04:00
Kevin Darby 9e5d43f44a fix crash where account name > 12 chars, tested with long account name 2019-07-11 17:53:48 -04:00
Kevin Darby f92722c849 gen 2019-07-11 17:51:29 -04:00
Eric Harding 389359dbfc SP-87 Splitter w/ exposed < target. 2019-07-11 16:47:26 -04:00
Eric Harding 390208327b SP-115 Issues found in manual testing 2019-07-11 15:49:51 -04:00
Kevin Darby ebedd17129 Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel_algos 2019-07-11 14:59:19 -04:00
Kevin Darby 0f8ed5dacb range to vector mapping 2019-07-11 14:15:06 -04:00
Kevin Darby 5c1965f534 fix crash when acct names are > 12 chars 2019-07-11 14:07:31 -04:00
Pedro Pinto 8553229119 Merge branch 'master' into prod 2019-07-11 10:45:10 -04:00
Eric Harding 96b63ff713 Disable market data heartbeats on dev systems to simplify debugging 2019-07-11 10:44:38 -04:00
Pedro Pinto 4903c12533 _index.md edited online with Bitbucket 2019-07-11 14:23:13 +00:00
Jake Norbut 88f23530c6 Merged in SP-119 (pull request #298)
RTM - SP-119 Ladder settings NullRef

Approved-by: John Colvin <john.colvin@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-11 14:18:45 +00:00
Kevin Darby 14f4452599 . 2019-07-10 23:21:56 -04:00
Kevin Darby 22002c43cf getting there 2019-07-10 18:43:09 -04:00
Eric Harding c9d371fb77 SP-115 Bugfixes from manual testing 2019-07-10 17:41:15 -04:00
Eric Harding b173f80703 SP-56 Flatten Dorman Parameters 2019-07-10 16:29:18 -04:00
Eric Harding 5b6edc455f SP-121 Fix crash and add validation 2019-07-10 15:02:20 -04:00
Kevin Darby 7551e23877 spreadsheet 2019-07-10 00:06:27 -04:00
Kevin Darby 22c92c3e17 ranges... 2019-07-10 00:05:24 -04:00
Kevin Darby 613823e7c5 arrays starting to work 2019-07-09 23:22:18 -04:00
Eric Harding 0b7d18301d SP-56 Register Dorman Algo 2019-07-09 19:14:22 -04:00
Kevin Darby f0a9a2415d adding notion of bts_parameters 2019-07-09 18:31:05 -04:00
Kevin Darby 708e63e36e almost have C 2019-07-09 14:39:11 -04:00
Eric Harding 9fac15b473 Clean up macro in Dorman, remove invalid AlgoTestDriver assertion 2019-07-09 13:24:38 -04:00
Jake Norbut 76ff36e514 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SP-119 2019-07-09 11:54:46 -05:00
Jake Norbut 740c14c261 add LadderSettings santization on paths to VM instantiation 2019-07-09 11:52:41 -05:00
Eric Harding ceafab93ab SP-33 Trade Accumulation 2019-07-09 12:26:28 -04:00
Kevin Darby 53990297e8 parsing right along 2019-07-08 23:54:55 -04:00
Kevin Darby 9570be8bfa it builds, ship it. 2019-07-08 23:16:31 -04:00
John Colvin a8f86c38e4 SP-80 / SP-90 Do not show workspaces prior to user authentication 2019-07-08 17:30:29 -05:00
John Colvin 46cb249016 in lazy vis state transitions, hook up to the loaded event directly (awaiting a TaskCompletionSource is heavy handed, and not thread safe) 2019-07-08 17:30:29 -05:00
John Colvin 6b799bf93b [non-functional] normalize spacing/tabs in LoginView.xaml 2019-07-08 17:30:29 -05:00
Kevin Darby 0b15570d00 more parser 2019-07-08 18:22:12 -04:00
Jake Norbut 33a71f5f47 Merged in SP-105_account-in-ladder (pull request #294)
RTM - SP-105 selected account in ladder

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 22:21:46 +00:00
Pedro Pinto 2940601f4b Updated release notes 2019-07-08 18:15:31 -04:00
Pedro Pinto 1f7939f49a Merge branch 'SP-105_account-in-ladder' of bitbucket.org:bluetradesys/spark into SP-105_account-in-ladder 2019-07-08 18:14:45 -04:00
Pedro Pinto 48e273a607 Merge remote-tracking branch 'origin' into SP-105_account-in-ladder 2019-07-08 17:50:43 -04:00
John Colvin e35634020d Merged in SP-89-offset-algo (pull request #286)
RTM SP-89 offset algo

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 21:49:12 +00:00
Pedro Pinto 1ede053f59 Merged with master 2019-07-08 17:48:16 -04:00
Pedro Pinto 93824786c7 Updated release notes. 2019-07-08 17:32:33 -04:00
Pedro Pinto 25b8c0a728 Merged with master 2019-07-08 17:31:22 -04:00
Jake Norbut 0e4fe1b47e filter the SelectorProperty source for updates that actually have items; this keeps the currently selected account through server restarts 2019-07-08 16:29:19 -05:00
Jake Norbut cd744963ff Revert "don't revert to default selected setter when server reboots"
This reverts commit d5fdcc6796.
2019-07-08 16:21:46 -05:00
Kevin Darby fb9d877938 Merged in dme (pull request #293)
RTM add mic for DME

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 21:08:53 +00:00
Pedro Pinto 087b001510 Updated release notes. 2019-07-08 17:03:56 -04:00
Pedro Pinto 02a7c0ddca Merge branch 'master' into dme 2019-07-08 17:01:50 -04:00
Kevin Darby 6afa1d581d Merged in span (pull request #263)
RTM: Span

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 21:01:01 +00:00
Taha Afzal 5e04abe573 Combining the account lookup for loss_limitm and span 2019-07-08 16:50:30 -04:00
Pedro Pinto 3422a44c87 Updated release notes. 2019-07-08 16:49:32 -04:00
Jake Norbut d5fdcc6796 don't revert to default selected setter when server reboots 2019-07-08 15:37:38 -05:00
Kevin Darby f12217d76c Remove mapping of DME mic per Pedro 2019-07-08 20:12:47 +00:00
Pedro Pinto d85a914046 Merge branch 'master' into span 2019-07-08 15:58:06 -04:00
Pedro Pinto 4fdc890cb5 Merged in ticks_off (pull request #296)
RTM Added ticks_off to top algo server side.

Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
2019-07-08 19:55:01 +00:00
Pedro Pinto 3ed0bd9052 Merged with master 2019-07-08 15:54:39 -04:00
Jake Norbut dbdc915ff2 populating mini_ob (LadderMiniOrderBookSettings) in LadderSettings.Default 2019-07-08 14:24:36 -05:00
Jake Norbut 10476c6662 null ref check on LadderSettings.mini_ob.value 2019-07-08 14:22:55 -05:00
Kevin Darby 5fbc1397b5 messing with proj 2019-07-08 14:59:12 -04:00
Eric Harding 74a91a7d2e SP-117 typedef TradeVector type 2019-07-08 14:49:44 -04:00
Eric Harding 72a6689cd4 SP-117 Expose ticker to IAlgoOrder 2019-07-08 14:41:07 -04:00
Kevin Darby 05e09cb977 Merge branch 'master' of bitbucket.org:bluetradesys/spark into span 2019-07-08 14:33:37 -04:00
Kevin Darby 38bd2ef321 Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel_algos 2019-07-08 14:29:03 -04:00
Eric Harding dab4f5acbb Merged in SP-104-HCL (pull request #289)
[RTM] SP-104 HCL

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 18:28:44 +00:00
Eric Harding a20c53b23d Merged in SP-68-crash (pull request #292)
[RTM] SP-68 Fix crash on exit by not touching queue on windows.  insert_or_replace() is a no-op on windows anyway.

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 18:27:59 +00:00
Pedro Pinto 8206f457b5 Updated release notes. 2019-07-08 14:27:37 -04:00
Pedro Pinto d4e6d6293d Merge branch 'master' into SP-68-crash 2019-07-08 14:26:48 -04:00
Kevin Darby d1a2197ba4 Merged in fix_auth_tests2 (pull request #297)
RTM fix auth tests to respect difference betwen admin and trader login
2019-07-08 18:20:09 +00:00
Kevin Darby 369fb55e90 fix auth tests to respect difference betwen admin and trader login 2019-07-08 14:20:14 -04:00
Pedro Pinto 37dacff74d Fixed a bug in top update 2019-07-08 14:09:37 -04:00
Eric Harding fb08533488 SP-32 profit orders 2019-07-08 13:51:54 -04:00
Pedro Pinto d297b703c7 Merge branch 'master' into ticks_off 2019-07-08 13:36:53 -04:00
Kevin Darby 59c00203a9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into excel_algos 2019-07-08 13:10:23 -04:00
Kevin Darby 33f533af9b set span to default off, hook up admin boolean to enforce span behind config key 2019-07-08 13:08:18 -04:00
Kevin Darby 085d6402f4 Merged in SPARK-2524-keycode_fixes (pull request #290)
RTM fixed SPARK-2524, allowing admin logins to not require a keycode session

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 16:26:51 +00:00
Pedro Pinto f4395ef833 Updated release notes. 2019-07-08 12:26:27 -04:00
Pedro Pinto e7a9412124 Updated release notes. 2019-07-08 12:21:39 -04:00
Pedro Pinto da4ee38abd Merge branch 'master' into SPARK-2524-keycode_fixes 2019-07-08 12:20:50 -04:00
Kevin Darby 706e5bc73b excel 2019-07-08 11:22:53 -04:00
Pedro Pinto af39fac368 Merge branch 'master' into SPARK-2524-keycode_fixes 2019-07-08 11:18:39 -04:00
Michael Harding 4f997505f3 Enforce Margin Limit in Spark Admin 2019-07-08 11:12:55 -04:00
Jake Norbut fcbb66361d Merged in SP-103_no_reset_on_agg_side_change (pull request #288)
RTM - SP-103 don't reset last when aggressor side changes

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 14:46:17 +00:00
Pedro Pinto fa16321691 Updated release notes. 2019-07-08 10:45:56 -04:00
Eric Harding 4ecbfe5f5b SP-31 Dorman auto-scratch 2019-07-08 10:42:43 -04:00
Pedro Pinto ceb3542e15 Merge branch 'master' into SP-103_no_reset_on_agg_side_change 2019-07-08 10:30:25 -04:00
Jake Norbut 32e55d07ee Merged in SP-110_volume_tooltip_nit (pull request #295)
RTM - display tooltip again when re-entering same cell where tooltip was previouly closed

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 14:28:34 +00:00
Pedro Pinto e079acf91f Updated release notes. 2019-07-08 10:28:18 -04:00
Pedro Pinto fecedb203a Merge branch 'master' into SP-110_volume_tooltip_nit 2019-07-08 10:26:59 -04:00
Jake Norbut 6ac05a47e1 Merged in SP-103_bars_not_req (pull request #291)
RTM - SP-103 bars not required to show last text

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-08 14:15:36 +00:00
Pedro Pinto 36ed80dc87 Updated release notes. 2019-07-08 10:15:14 -04:00
Kevin Darby 6cf395199e working on visitor 2019-07-06 23:25:51 -04:00
Kevin Darby 1707c3268f excel formula grammar, antlr4 integration. Successful parse tree 2019-07-05 18:24:41 -04:00
John Colvin f5bbc4245f SP-111 / SP-113 Add ticks off parameter to top algo UI 2019-07-05 16:12:32 -05:00
Eric Harding 7e1463683f SP-30 base Dorman algo atop SplitterAlgo 2019-07-05 16:04:39 -04:00
Pedro Pinto 96fc54ff0f Added ticks_off to top algo server side. 2019-07-05 14:35:18 -04:00
Kevin Darby 25b69e4f7d fix small bug and add manual test 2019-07-05 13:29:22 -04:00
Kevin Darby d871ffca6d temporarily tie span enforcement to config while michael works 2019-07-05 12:59:41 -04:00
Jake Norbut 213567bcdb display tooltip again when re-entering same cell where tooltip was previouly closed 2019-07-05 11:49:34 -05:00
Kevin Darby b7e5c3a374 Merge branch 'span' of bitbucket.org:bluetradesys/spark into span 2019-07-05 12:30:10 -04:00
Kevin Darby ae4a8bbf46 more span 2019-07-05 12:28:58 -04:00
Taha Afzal 850386c1d4 Margin enforced in the limiter 2019-07-05 12:05:16 -04:00
Kevin Darby 2d4e94a636 Types.fs edited online with Bitbucket 2019-07-05 15:39:35 +00:00
Kevin Darby 9b73ad2486 playing with excel parsing 2019-07-05 11:06:19 -04:00
Pedro Pinto e447096733 Routed excess liquidity to OrderManager. 2019-07-05 11:00:42 -04:00
Jake Norbut cd68b95a3e add a mechanism for ignoring InlineUserProperty's force_override property 2019-07-05 08:19:05 -05:00
Kevin Darby 205eebb7da excel formulae 2019-07-04 23:43:22 -04:00
Kevin Darby ef4dc1fc65 more excel 2019-07-04 23:25:28 -04:00
Michael Harding ebad5b389f Fix Save button 2019-07-03 16:01:50 -04:00
John Colvin 160c49514a Merge remote-tracking branch 'origin/master' into SP-89-offset-algo 2019-07-03 14:38:50 -05:00
Kevin Darby 64430e4368 Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-07-03 15:15:16 -04:00
Michael Harding 4f7fcc5634 Move loss limits to the top of the limits page 2019-07-03 14:54:40 -04:00
Michael Harding 500e5b5fe4 Polling and commas 2019-07-03 14:19:51 -04:00
Eric Harding 9e7da44cce Merge branch 'SP-87-Splitter' of bitbucket.org:bluetradesys/spark into SP-23-Dorman2 2019-07-03 14:16:45 -04:00
Eric Harding 7f2988ec57 SP-87 PR feedback 2/2 2019-07-03 14:09:07 -04:00
Eric Harding 08c2762500 SP-87 PR feedback 1/2 2019-07-03 14:09:07 -04:00
Pedro Pinto 509a33dc53 Merged 2019-07-03 13:42:17 -04:00
Pedro Pinto 4e6e97d90b Added unit test for splitter compute_desired_sizes. 2019-07-03 13:40:41 -04:00
Michael Harding 9d924fa4ba Margin table formatting 2019-07-03 10:14:01 -04:00
John Colvin 092e63e300 OffsetAlgo attach impl (take 2) 2019-07-03 00:07:23 -05:00
Kevin Darby 4654cd4739 changes in margin workflow for webui 2019-07-02 18:29:10 -04:00
Kevin Darby f2f06d76de PR feedback 2019-07-02 17:43:28 -04:00
Kevin Darby 6bc293a58e add mic for DME 2019-07-02 17:35:29 -04:00
Kevin Darby fdb462eda4 simplify margin server states 2019-07-02 17:34:46 -04:00
Kevin Darby cc579034b2 fix bug in margin service 2019-07-02 17:05:22 -04:00
John Colvin 4b76e91757 SP-89 (pr edit) - 'can_consume_attachment()' returns false for 'Offset' 2019-07-02 15:48:24 -05:00
Eric Harding 53ad85cce0 Merge branch 'SP-87-Splitter' of bitbucket.org:bluetradesys/spark into SP-23-Dorman2 2019-07-02 16:29:45 -04:00
Eric Harding 8d34279ab0 SP-68 Fix crash on exit by not touching queue on windows. insert_or_replace() is a no-op on windows anyway. 2019-07-02 16:26:34 -04:00
John Colvin 7973b927a2 SP-89 (pr edit) - fwd price updates to child; cache update qty 2019-07-02 15:22:54 -05:00
Kevin Darby 6997aed9bd fixed SPARK-2524, allowing admin logins to not require a keycode session 2019-07-02 15:05:23 -04:00
Michael Harding b5d7d98098 Add DayPnL to SA Margin tab 2019-07-02 15:04:55 -04:00
Jake Norbut 8f22a326ab last on volume foreground text is now white 2019-07-02 14:01:50 -05:00
Jake Norbut b2c3488c5e draw volume numbers when bars not present 2019-07-02 14:01:07 -05:00
Michael Harding 23f10eac70 Add accountPnl to emptyMargin 2019-07-02 14:10:27 -04:00
Eric Harding cdabf3b023 SP-87 - Report fills to parent 2019-07-02 13:35:25 -04:00
Eric Harding 8248bd7db8 Add descriptive comment to splitteralgo 2019-07-02 13:35:25 -04:00
Kevin Darby cb31269a70 added pnl impact to margin 2019-07-02 13:14:46 -04:00
Jake Norbut 5b75b8718a updating comment to reflect change 2019-07-02 11:14:01 -05:00
Eric Harding 5a256d4634 SP-104 PEBKAC buy with negative mult is nonsense 2019-07-02 11:55:55 -04:00
Jake Norbut db2de3e8c4 remove last_side; change StructLayout Size accordingly 2019-07-02 10:53:11 -05:00
Jake Norbut b9dcad9549 don't reset last trade size when aggressor changes 2019-07-02 10:52:21 -05:00
Jake Norbut ce104479e1 removing last_side; incrementing shm_version 2019-07-02 10:51:06 -05:00
Pedro Pinto d3ae9bc891 Merge branch 'SP-87-Splitter' of bitbucket.org:bluetradesys/spark into SP-87-Splitter 2019-07-02 11:43:47 -04:00
Pedro Pinto ac90a21314 Clang build fixes. 2019-07-02 11:43:36 -04:00
Eric Harding 2d56eedf3e Bugfix in synthetic_book_merge_tests 2019-07-02 10:37:47 -04:00
Eric Harding 049381908c update gitignore 2019-07-02 10:34:28 -04:00
Eric Harding 1104f4db1d SP-87 Reject attach in before_launch 2019-07-02 09:52:24 -04:00
John Colvin 0067e52155 SP-89 (pr edit) - wait 4 mkt 2 launch; attach impl 2019-07-02 00:07:17 -05:00
Kevin Darby 8ed6d297a9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into span 2019-07-01 18:01:34 -04:00
Kevin Darby 474401cddc new spreadsheet 2019-07-01 17:59:18 -04:00
Taha Afzal a06df5099a Merged in SP-24-loss-limit (pull request #285)
RTM SP-24 loss limit

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-01 21:44:41 +00:00
Pedro Pinto 8ddd5bdbb5 Merged with master 2019-07-01 17:06:00 -04:00
Taha Afzal 5cbe00beab Spread PNL is not counted towards the Account PNL 2019-07-01 16:33:56 -04:00
Kevin Darby 3ebcf9a270 example of join in excel 2019-07-01 16:21:38 -04:00
Eric Harding b8a9116ea6 SP-30 Dorman part 1 2019-07-01 16:08:28 -04:00
Jake Norbut 40cd826260 Merged in SP-79 (pull request #280)
RTM - SP-79 arrow indicator on last disappearing too fast

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-01 20:03:34 +00:00
Pedro Pinto 8bd63f11b1 Updated release notes. 2019-07-01 16:03:13 -04:00
Pedro Pinto 2198a8d6ed Merge remote-tracking branch 'origin' into SP-79 2019-07-01 16:02:15 -04:00
Pedro Pinto 8023dc7aa8 Merge branch 'master' into SP-79 2019-07-01 16:01:53 -04:00
Eric Harding 0dcdbd540e SP-87 Splitter algo 2019-07-01 15:47:56 -04:00
Eric Harding 226fcc279b Utils for manipulating dynamic algos 2019-07-01 15:46:20 -04:00
Eric Harding a6ad70fc05 SP-28 Algo unit test utils 2019-07-01 15:46:03 -04:00
John Colvin e2b0da8103 Merged in SP-78-miniob-settings (pull request #282)
RTM SP-78 In miniob settings changing the column width directly through the grid (i.e. not through the dialog) has no effect beyond the current popup.

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-01 19:36:01 +00:00
Pedro Pinto aea233d537 Updated release notes. 2019-07-01 15:35:07 -04:00
Pedro Pinto 5756c090f3 Merge remote-tracking branch 'origin' into SP-78-miniob-settings 2019-07-01 15:28:59 -04:00
John Colvin 5ab0acb0ef Merged in SP-81-private-synths (pull request #284)
RTM SP-81 private synths

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-07-01 19:28:00 +00:00
Pedro Pinto 3950e5b634 Updated release notes. 2019-07-01 15:27:29 -04:00
Pedro Pinto 7ce8f1ed97 Updated release notes 2019-07-01 14:57:00 -04:00
Michael Harding 5d53492ab5 Merged SP-38-span-admin into span 2019-07-01 14:53:52 -04:00
Michael Harding 90fd5bd382 Handle save errors better 2019-07-01 14:52:40 -04:00
Michael Harding beff060a4f Update unchanged margin values 2019-07-01 14:27:44 -04:00
Michael Harding bc3418734f Span Columns and Formatting 2019-07-01 13:22:27 -04:00
Michael Harding ab255c8777 update ui.fsproj 2019-07-01 12:51:17 -04:00
Michael Harding cc2bde1064 Merged span into SP-38-span-admin 2019-07-01 12:43:16 -04:00
Michael Harding 94d66a8e99 Add Margin tab to Spark Admin 2019-07-01 12:40:16 -04:00
Taha Afzal 92f08b89cc Added a sub test to the clip limits test to verify that spread clip limits are independent of futures 2019-07-01 09:01:46 -04:00
Taha Afzal 02769479ea SP-74 bundled into this (it was a small change) 2019-06-30 22:45:29 -04:00
John Colvin 4ada322c56 bring back OffsetOrder gui in ladder/ticket 2019-06-30 12:56:59 -05:00
John Colvin 7de7f6e496 remove old OffsetOrder.cpp/h, replace with new OffsetAlgo in CMakeDeps 2019-06-30 12:47:05 -05:00
John Colvin 0700e9e80d SP-89 Port Offset order to new api 2019-06-29 16:10:34 -05:00
Taha Afzal ccca85d1c5 loss limit manual test updated 2019-06-27 15:46:44 -04:00
Taha Afzal 60e933fca9 SOD also indexes its data on account_id, Cleaned up Limiter's account_data (there was an overlap, after introducing Loss limit related PNL data) 2019-06-27 15:20:12 -04:00
Taha Afzal e2aa6034ab merging master 2019-06-26 20:22:05 -04:00
Taha Afzal 8b4c64e8f4 PositionServiceServer not indixes the state based on account_id istead of names 2019-06-26 19:34:46 -04:00
John Colvin b5ea399e7e WorkspacesDialogView - remove 'Is Shared' checkbox 2019-06-26 15:22:14 -05:00
John Colvin 62baa6aa31 MEFExport UserTuple does not need a contract name 2019-06-26 14:19:49 -05:00
John Colvin 87433aa7c3 add comment for 'KnownUsers' prop 2019-06-26 13:41:37 -05:00
John Colvin 3252cab4bb SP-80 / SP-88 Login dialog should not expose user names (because they are private) 2019-06-26 13:22:02 -05:00
John Colvin fbe9b570a3 (forgot to run 'git add' again) 2019-06-26 12:43:48 -05:00
John Colvin 1bcb209961 remove unused delegate type, and worthless commented line 2019-06-26 11:42:47 -05:00
John Colvin b0726ad3ea just noticed some iobservables in OrderBookVM without OnDispatcher()s (adding them preemptively) 2019-06-26 11:40:10 -05:00
John Colvin e0ba27a0a9 SpreaderModel - remove unneccesary 'async/await' wrappers 2019-06-26 11:40:07 -05:00
John Colvin 345e45c5bf MEFExport UserTuple 2019-06-26 10:28:05 -05:00
John Colvin 490add84d9 replace magic consts w/ regular consts 2019-06-25 23:19:20 -05:00
John Colvin 94fc2c4248 refactor LoginModel.Exports -> LoginContainer.Exports 2019-06-25 23:18:43 -05:00
John Colvin 8a7e2db4ac SP-80 / SP-81 Synths should be private 2019-06-25 19:01:57 -05:00
John Colvin 71c576bbc0 SP-80 / SP-82 Algos should be private 2019-06-25 17:05:41 -05:00
John Colvin 2e4dff4d91 SP-76 In miniob settings, the miniob's width seems to have a minimum that does not reflect the currently displayed columns 2019-06-25 16:20:08 -05:00
John Colvin 73336df814 SP-78 In miniob settings changing the column width directly through the grid (i.e. not through the dialog) has no effect beyond the current popup. 2019-06-25 16:04:24 -05:00
Michael Harding 633edb43eb wait longer for the system to have products 2019-06-25 14:36:52 -04:00
Taha Afzal 29d14404e6 PositionService has access to AdminService to resolve accounts now 2019-06-24 17:15:04 -04:00
Taha Afzal 60b7966f2f Loss limit manual test 2019-06-24 15:33:47 -04:00
Jake Norbut 1059351334 adding the settings_subscription to be disposed; fixes swallowed NullRef; not directly related to this branch 2019-06-24 14:15:53 -05:00
Jake Norbut 179ee000a3 adding case for when the ladder is first brought up and the market has not yet moved 2019-06-24 14:02:24 -05:00
Jake Norbut 43129427f6 fix arrow disappearing from non-Last-based renders 2019-06-24 13:20:38 -05:00
Jake Norbut 7afe1e103d refactor string formatting out of LadderRender 2019-06-24 13:18:29 -05:00
Taha Afzal 7c6a26ebd9 All hooked up, on updates, loss limit is checked only on size increases 2019-06-23 19:25:06 -04:00
Taha Afzal b2766c283d Setting account id in PositionUnit 2019-06-22 22:05:32 -04:00
Pedro Pinto 6b5b4ea2b1 Merge branch 'SP-24-loss-limit' of bitbucket.org:bluetradesys/spark into SP-24-loss-limit 2019-06-22 16:30:39 -04:00
Pedro Pinto 6f7c6db918 Updated fill account_id in FillStore. 2019-06-22 16:29:55 -04:00
Taha Afzal a1ead3d1c1 cpp routed correctly now 2019-06-22 14:24:15 -04:00
Taha Afzal 1fc35ee3cd Merge branch 'SP-24-loss-limit' of bitbucket.org:bluetradesys/spark into SP-24-loss-limit 2019-06-21 18:43:31 -04:00
Taha Afzal a014dcc984 abstracted _get_handle in PositionServicePNLHelper::HandleIndex 2019-06-21 17:58:08 -04:00
Pedro Pinto 6e07a0c109 Fixed build. 2019-06-21 11:51:20 -04:00
Pedro Pinto e936b80dc6 Forwarded pnl updates to order manager. 2019-06-21 11:45:50 -04:00
Pedro Pinto 60ab67213f Ran clang format. 2019-06-20 15:57:09 -04:00
Pedro Pinto 4055a06601 Merged master 2019-06-20 15:28:27 -04:00
Kevin Darby e47af3d7bd update api docs 2019-06-20 10:34:06 -04:00
Kevin Darby c3c427742d Merge branch 'master' of bitbucket.org:bluetradesys/spark into span 2019-06-19 13:34:52 -04:00
Kevin Darby 132bee0d27 Merge branch 'prorata' of bitbucket.org:bluetradesys/spark into prorata 2019-06-19 12:41:48 -04:00
Michael Harding 83c432d8e2 Merged in SP-34-loss_limit_admin (pull request #268)
SP-34: Add Loss Limit to Spark Admin

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-06-18 21:25:47 +00:00
Michael Harding 6a4930767b SP-34: Add Loss Limit to Spark Admin 2019-06-18 16:32:14 -04:00
Kevin Darby 685e69adcc Merge branch 'key_codes' of bitbucket.org:bluetradesys/spark into span 2019-06-18 15:21:10 -04:00
Kevin Darby 2b7f5572db Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-06-18 14:40:23 -04:00
Kevin Darby e9f61dc3c0 added get_excess_liquidity_report rpc for adminui 2019-06-17 18:15:05 -04:00
Kevin Darby f329470165 added spreadsheet to help test span 2019-06-17 16:49:21 -04:00
Kevin Darby d7d319f1e3 fixed span timing 2019-06-17 10:43:27 -04:00
Taha Afzal cddadcb278 PositionService exposes new streaming calls pnl_data and pnl_account_data to get detailed and summary pnls 2019-06-16 22:02:16 -04:00
Kevin Darby 419833d398 slight timing issue with span 2019-06-14 18:10:41 -04:00
Kevin Darby 9883553fb5 MarginService 2019-06-14 11:21:09 -04:00
Kevin Darby be5d037ca9 span works again 2019-06-13 17:52:47 -04:00
Taha Afzal 21f5fd919d Changes in the API for Loss Limit and new parameter in the account to enforce that limit 2019-06-13 15:37:43 -04:00
Kevin Darby 159e8fba23 beginnings of span, should't be long 2019-06-12 18:17:09 -04:00
Kevin Darby 3254775eb9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-06-10 16:17:52 -04:00
Kevin Darby d4c2eb2bbc Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-06-10 14:56:13 -04:00
Kevin Darby 81bbafd3a1 Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-06-07 18:14:22 -04:00
Kevin Darby dfe008922b Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-06-07 16:01:15 -04:00
Kevin Darby d3de1c2c41 Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-06-07 14:24:43 -04:00
Kevin Darby 7428331504 Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-05-29 18:05:19 -04:00
Kevin Darby 5b818bf276 test for prorata order 2019-05-29 17:43:05 -04:00
Kevin Darby d8559276d0 test case for scratchalgo 2019-05-29 16:55:11 -04:00
Kevin Darby 3d11aba8ef fix build from merge 2019-05-29 15:35:47 -04:00
Kevin Darby c34d262d1a Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-05-29 15:24:46 -04:00
Kevin Darby 717c90867a fixed multiple child case, thanks Eric 2019-05-29 15:08:14 -04:00
Kevin Darby 90178908ba looking good; need John to help me figure out the hedge lablels 2019-05-28 22:02:21 -04:00
Kevin Darby 01956e92b6 added facility for simple guis of well known order type protos to be generated; worth it. 2019-05-28 21:25:39 -04:00
Kevin Darby b1e19294e6 making some headway on generic order ui 2019-05-28 17:50:35 -04:00
Kevin Darby 1567ca475d now moving to gui 2019-05-28 14:29:01 -04:00
Kevin Darby 938a572a54 rethinking the dynamictype -> native type for sake of gui
going to make a new type of DefaultOrderVM that uses reflection instead of converting dynamic -> native
in the orderman
2019-05-28 14:14:16 -04:00
Kevin Darby e8bc07360d got the dynamism and updates working 2019-05-28 13:52:18 -04:00
Kevin Darby 91989e6baf Quantity measure validation 2019-05-28 10:19:31 -04:00
Kevin Darby 81ae503aa2 Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-05-28 09:42:28 -04:00
Kevin Darby 0a33fb37b9 rata looking good 2019-05-27 22:53:56 -04:00
Kevin Darby 729986d53d need to test get_out function 2019-05-26 23:30:55 -04:00
Kevin Darby e19bb7c621 chase if worse 2019-05-25 23:45:17 -04:00
Kevin Darby ea232fbaf1 ScratchAlgo works 2019-05-25 23:27:54 -04:00
Kevin Darby 6fc3c0b601 the bug turned out to be in my brain 2019-05-25 17:48:54 -04:00
Kevin Darby 8aee612bfa Merge branch 'master' of bitbucket.org:bluetradesys/spark into prorata 2019-05-25 15:36:26 -04:00
Kevin Darby 5f125790cf dynamics can now have QuantityMeasure 2019-05-25 10:50:27 -04:00
Kevin Darby 117e107116 dynamics work again 2019-05-24 22:50:12 -04:00
Kevin Darby c8c6aea188 gui for haris orders 2019-05-24 15:50:46 -04:00
Kevin Darby e1f590127e ready to test 2019-05-23 23:55:26 -04:00
Kevin Darby 3a516df593 more prorata 2019-05-23 18:16:20 -04:00
Eric Harding 91bc135c61 Fix web ui build 2019-05-23 15:49:12 -04:00
Kevin Darby 5135a21dc2 ProRata and Scratch Algos 2019-05-23 15:27:41 -04:00
241 changed files with 14324 additions and 1409 deletions

No files matched your search

+36 -34
View File
@@ -1,34 +1,36 @@
nohup.out
build/
cmake-build-debug/
.idea/
.dir-locals.el
*.pyc
.vscode/*
!.vscode/*.json
.vs
obj/
*.csproj.user
*.vspx
*.psess
msbuild.log
*.orig
vcxproj.user
src/client/spark/Properties/launchSettings.json
compile_commands.json
.cquery_cached_index/
src/client/sparkcmd/Properties/launchSettings.json
node_modules
paket-files
project.lock.json
src/server/sparkd/orderman/perf_analysis/data
src/server/sparkd/orderman/perf_analysis/logs/
src/server/sparkd/orderman/perf_analysis/trace_data/
.fake
*.fsx.lock
*.code-workspace
src/lib/orderman/perf_analysis/logs/latency_data/
trace_data/
/src/lib/orderman/perf_analysis/last_pretty_log.csv
/src/lib/orderman/perf_analysis/log.csv
/doc/user/public/*
nohup.out
build/
cmake-build-debug/
.idea/
.dir-locals.el
*.pyc
.vscode/*
!.vscode/*.json
.vs
obj/
*.csproj.user
*.vspx
*.psess
msbuild.log
*.orig
vcxproj.user
src/client/spark/Properties/launchSettings.json
compile_commands.json
.cquery_cached_index/
src/client/sparkcmd/Properties/launchSettings.json
node_modules
paket-files
project.lock.json
src/server/sparkd/orderman/perf_analysis/data
src/server/sparkd/orderman/perf_analysis/logs/
src/server/sparkd/orderman/perf_analysis/trace_data/
.fake
*.fsx.lock
*.code-workspace
src/lib/orderman/perf_analysis/logs/latency_data/
trace_data/
/src/lib/orderman/perf_analysis/last_pretty_log.csv
/src/lib/orderman/perf_analysis/log.csv
/doc/user/public/*
.ionide/*
src/web/ui/.ionide/symbolCache.db
+3 -2
View File
@@ -44,6 +44,7 @@
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/build/ninja_debug/sdk/examples" : "${workspaceRoot}/src/sdk/examples",
"/home/bts/spark/": "${workspaceRoot}"
}
},
@@ -104,7 +105,7 @@
"request": "launch",
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
"args": [
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64.xll"
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
@@ -249,7 +250,7 @@
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
"args": [
"test",
"SingleWriterConcurrentVector"
"[algo]"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/server/",
+1
View File
@@ -59,6 +59,7 @@ else ()
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libACE-5.7.5.so ${CMAKE_BINARY_DIR}/server
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
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/excel_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
COMMENT "Creating server runtime layout"
DEPENDS sparkd data_files_dir)
@@ -1,11 +1,11 @@
+++
title = "BTS Basic Algos"
weight = 10
title = "Basic Algos"
weight = 1
sort_by = "weight"
insert_anchor_links = "right"
aliases = ["./bts_spark/Algos/"]
+++
## Lean
The goal of the lean order is to watch the opposite-inside market size and cross when that size goes below the threshold (this also implies a cross if that size goes to zero and the market trades away from you) If the market trades toward you, you can opt into several different behaviors
@@ -43,4 +43,13 @@ An OCO order creates a relationship between a child order and a 'stop' or 'other
Composing this order with Cover simulates scalping functionality
##### Parameters
* Stop Ticks - ticks below or above main order to place stop / other order
* Stop Ticks - ticks below or above main order to place stop / other order
## Splitter
The splitter is useful for pro-rata markets where you want to vary your exposure independent of the order quantity that you want to fill. The splitter will complete when the number of fills reaches or exceeds the order quantity but it will put exposed_qty total size on the market. Splitter favors order size over queue position.
If exposed_qty is set to 0 the splitter will continue to exist in the Order Book but will cancel all of its child orders. This makes it a useful building block for custom algos that need to implement a 'paused' state.
##### Parameters
* exposed_qty - The total quantity to expose on the market.
* max_order_qty - The maximum size of a single order.
+65 -3
View File
@@ -4,6 +4,67 @@ weight = 3
sort_by = "weight"
insert_anchor_links = "right"
+++
## 2.7.2
### User Visible Changes
- Changed the login dialog to avoid leaking workspace names from other users.
- Added new Splitter algo. From the manual: The splitter is useful
for pro-rata markets where you want to vary your exposure
independent of the order quantity that you want to fill. The
splitter will complete when the number of fills reaches or exceeds
the order quantity but it will put exposed_qty total size on the
market. Splitter favors order size over queue position.
### Internal Changes
- Made spark more resilient to lack of connectivity to the CME web site for downloading of templates.
## 2.7.1
### User Visible Changes
- Fixed some issues with the Cover algo, where in some circumstances
it would erroneously cancel launched hedges on completion.
## 2.7.0
### User Visible Changes
- Allowed algos to process "in-flight" fills while shutting down
- Changed the cover algo to continue to hedge while shutting down
### API Changes
- Changed the API so that algos do not fully terminate until all
their children are terminated. Algos that are in the process of
shutting down in response to a complete or cancel call will still
receive child updates, which they can use to calculate and report
an accurate fill count (during this shutdown stage algos cannot
create, cancel or update children). A new IAlgoServices method,
is_shutting_down(), can be used to determine if an algo is
shutting down.
## 2.6.2
### User Visible Changes
- Fixed an issue in the ladder where in some cases the mini order book could not be brought up from a badge
- Fixed a rare race condition that could lead to erroneous limit enforcement
- Fixed an issue with the margin limits where an error could occur if an account name with more than 12 chars was used
## 2.6.1
### User Visible Changes
- Fixed an issue in the ladder where an error could occur when trying to change ladder settings for the first time
## 2.6.0
### User Visible Changes
- In the ladder made last trade more distinctive when overlayed on the volumn column
- In the ladder, corrected display of volume tool tip
- In the ladder, changed the consolidation of last trade to consider only the price ignoring aggressor changes
- Added an option for the Top Algo to delete itself if within N ticks from top of book
- Address a rare issue that could lead to an error report on UI exit
- Added margin risk limits
- Added the "Offset" algo. Offset prices a child algo at a parameterizable distance from the top of the book
- Avoided switching the current account and order type on open ladders when limits are changed
### Internal Changes
- Ensure that admin logins are always allowed to login, regardless of keycode status.
## 2.5.0
### User Visible Changes
- Added loss limit risk check
- Made algos, synthetics and user names private so that multiple users can share a single system privately.
- Allowed the mini order book defaults for column widths to be changed from the mini order book
- Allowed the mini order book to shrink to the minimum size that will be needed to display the selected columns
- Improved accuracy of the directional arrow when displaying last trades in the volumn column
## 2.4.0
### User Visible Settings
- In the ladder, changed the last trade column to accumulate successive trades at the same price
@@ -163,7 +224,7 @@ insert_anchor_links = "right"
- Integrated stunnel into UI to make VPN setup unnecessary in unsecured environments.
## 1.5.7
### User Visible Changes
- New videos have been added to the manual describing the [Algo Studio](./bts_spark/Trading/index.md#algo-studio) and [Spreader](./bts_spark/Spreader/index.md)
- New videos have been added to the manual describing the [Algo Studio](./bts_spark/Trading/_index.md#algo-studio) and [Spreader](./bts_spark/Spreader/_index.md)
- Fixed a bug that occasionally would cause windows to be position outside the visible screen on startup
- Fixed a bug in the order book form that would occasionally cause the Recreate Order option to be unavailable
- Added a confirmation step to Cancel All, Cancel Buys and Cancel Sells commands
@@ -286,7 +347,7 @@ insert_anchor_links = "right"
- The zoom slider can be enabled from Ladder Form -> Settings button -> Left Pane tab -> check Zoom Slider checkbox
- The zoom functionality can also be performed via hotkey and mouse wheel combinations
- Zoom In = CTRL + Mouse wheel up
- Zoom Out = CTRL + Mouse wheel down
- Zoom Out = CTRL + Mouse wheel down␍
- Reset zoom = CTRL + Mouse wheel click or double click on the zoom slider
### Exchange simulator
- Improved the exchange simulator to track real market data more closely
@@ -391,7 +452,8 @@ insert_anchor_links = "right"
- This is configurable from a ladder's settings as well
### Customized settings in all windows
- All windows now contain a settings page where grid settings (font color, size, etc) are applied
- These settings can be saved on a per-window basis or as a Default for newly-launched windows␍
- These settings can be saved on a per-window basis or as a Default for newly-launched windows
## 1.3.1 Through 1.3.4
+6 -14
View File
@@ -7,12 +7,12 @@ using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using bts.api.core;
using mbts.ui.shell;
namespace mbts.spark {
[Export]
class AdminModel : IDisposable {
readonly string _user_name;
public string user_name => _user_name;
public readonly UserTuple user;
readonly BehaviorSubject<IReadOnlyDictionary<uint, string>>
_accounts_subject = new BehaviorSubject<IReadOnlyDictionary<uint, string>>(new Dictionary<uint, string>()),
@@ -24,14 +24,14 @@ namespace mbts.spark {
IDisposable _dispose;
[ImportingConstructor]
AdminModel(AdminService service, [Import("username")]string user_name) {
_user_name = user_name;
AdminModel(AdminService service, [Import]UserTuple user) {
this.user = user;
_dispose = service.subscribei_admin_data().Select(update => {
_wrapper_subject.OnNext(new AdminModelWrapper(update.all.TryFirst(), _user_name));
_wrapper_subject.OnNext(new AdminModelWrapper(update.all.TryFirst(), user.name));
IEnumerable<BTSUserImmutable> active_users =
from admin_data in update.all
from bts_user in admin_data.Users
where bts_user.Name == user_name && bts_user.HasUserData && bts_user.UserData.HasAccountPermissions
where bts_user.Name == user.name && bts_user.HasUserData && bts_user.UserData.HasAccountPermissions
select bts_user;
IEnumerable<BTSAccountImmutable> active_accounts =
@@ -62,14 +62,6 @@ namespace mbts.spark {
public IObservable<IReadOnlyList<AccountTuple>> on_account_tuples => on_accounts.Select(d => d.Select(kv => new AccountTuple(kv.Key, kv.Value)).ToList());
public IObservable<AdminModelWrapper> on_admin_data => _wrapper_subject.AsObservable();
public UserTuple? get_current_user() {
foreach (var kv in _user_map) {
// just for good measure, there should never be > 1 element in _user_map, and it should always match _user_name
if (kv.Value == _user_name)
return new UserTuple(kv.Key, kv.Value);
}
return null;
}
public AccountTuple? get_default_account() {
foreach (var kv in _account_map) {
return new AccountTuple(kv.Key, kv.Value);
+2 -1
View File
@@ -3,6 +3,7 @@ using mbts;
using mbts.config;
using mbts.spark;
using mbts.ui;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
@@ -22,7 +23,7 @@ namespace bts.ui {
IDisposable _dispose;
[ImportingConstructor]
public AboutDaemon(IBus bus, [Import("username")]string username) {
public AboutDaemon(IBus bus, [Import(LoginContainer.Exports.UserName)]string username) {
_user = username;
_bus = bus;
_dispose = bus.subscribe(SparkTags.About).OnDispatcher().Subscribe(_ => show_about());
+28 -2
View File
@@ -10,6 +10,7 @@ using System.Reactive.Linq;
using System.Windows.Threading;
using System.Windows;
using System.Threading.Tasks;
using mbts.ui.shell;
namespace bts.spark
{
@@ -20,7 +21,7 @@ namespace bts.spark
IDisposable _dispose;
[ImportingConstructor]
public ErrorReporter(ErrorReportService svc, IBus bus, [Import("username")] string username) {
public ErrorReporter(ErrorReportService svc, IBus bus, [Import(LoginContainer.Exports.UserName)] string username) {
_svc = svc;
_username = username;
_dispose = bus.subscribe(SparkTags.Support).OnDispatcher().Subscribe(msg => show_support_window(msg, "Report a problem"));
@@ -82,7 +83,6 @@ namespace bts.spark
}
}
//public static async Task<Result<T>>
public static async Task<(T value, bool success)> safe_execute<T>(this ErrorReporter error_reporter, Func<Task<Result<T>>> execute, Func<T, bool> get_success, Func<T, string> get_error_msg, string desc) {
try {
var result = await execute();
@@ -101,5 +101,31 @@ namespace bts.spark
void _report_error(string msg) =>
error_reporter.report_error($"Error {desc}: {msg}", AlgoStudioWindow.NAME, MessageBoxImage.Warning);
}
public static async Task<TResult> safe_execute<T, TResult>(
this ErrorReporter error_reporter,
Func<Task<Result<T>>> execute,
Func<T, bool> get_success,
Func<T, string> get_error_msg,
Func<T, bool, TResult> result_selector,
string desc) {
//
try {
var result = await execute();
if (!result.success) {
_report_error(result.error_message);
} else if (!get_success(result.value)) {
_report_error(get_error_msg(result.value));
} else {
return result_selector(result.value, true);
}
} catch (Exception ex) {
_report_error(ex.ToString());
}
return result_selector(default, false);
//
void _report_error(string msg) =>
error_reporter.report_error($"Error {desc}: {msg}", AlgoStudioWindow.NAME, MessageBoxImage.Warning);
}
}
}
+3 -2
View File
@@ -15,6 +15,7 @@ using System.Collections.Concurrent;
using System.Threading;
using bts.api.user;
using bts.md;
using mbts.ui.shell;
namespace bts {
[Export(typeof(IInitializable))]
@@ -27,8 +28,8 @@ namespace bts {
static uint get_naggle() => mbts.config.Configuration.get<md.RelayOptions>().naggle_ms;
[ImportingConstructor]
public MDDaemon(Lazy<IBus> bus, Lazy<AuthTokenImmutable> token, Lazy<SyntheticSecurityService> ss) :
base(get_naggle(), MDSourceOptions.DeferInit) {
public MDDaemon(Lazy<IBus> bus, Lazy<AuthTokenImmutable> token, Lazy<SyntheticSecurityService> ss, [Import(LoginContainer.Exports.UserID)]uint user_id) :
base(get_naggle(), MDSourceOptions.DeferInit, user_id) {
_bus = bus;
_token = token;
_ss = ss;
@@ -3,6 +3,7 @@ using bts.metrics;
using mbts;
using mbts.config;
using mbts.ui;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
@@ -23,7 +24,7 @@ namespace bts.perf {
Ping _pinger = new Ping();
[ImportingConstructor]
public ShellLatencyDaemon(ShellLatencyService service, [Import("username")] string user) {
public ShellLatencyDaemon(ShellLatencyService service, [Import(LoginContainer.Exports.UserName)] string user) {
_service = service;
_version = Configuration.get<bts.config.System>().version;
_username = user;
@@ -5,6 +5,7 @@ using mbts.co2;
using mbts.persist;
using mbts.spark;
using mbts.ui;
using mbts.ui.shell;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
@@ -18,7 +19,7 @@ namespace bts.shell
class MarketShellModule : IShellModule
{
[ImportingConstructor]
public MarketShellModule(IBus bus, MDConsumer consumer, UserSettingsHub settings_hub, OrdersModel orders_model, [Import("username")]string user_name) {
public MarketShellModule(IBus bus, MDConsumer consumer, UserSettingsHub settings_hub, OrdersModel orders_model, [Import(LoginContainer.Exports.UserName)]string user_name) {
_tsf = new TimeAndSalesWindowFactory(this, bus, consumer, settings_hub);
_mvf = new MarketViewWindowFactory(this, bus, settings_hub, orders_model);
}
@@ -1,6 +1,7 @@
using bts.api;
using bts.api.synthetic;
using bts.spark;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
@@ -14,12 +15,14 @@ namespace mbts.spark {
readonly IBus _bus;
readonly SyntheticSecurityService _service;
readonly ErrorReporter _error;
readonly uint _user_id;
[ImportingConstructor]
public SpreaderModel(IBus bus, SyntheticSecurityService service, ErrorReporter error) {
public SpreaderModel(IBus bus, SyntheticSecurityService service, ErrorReporter error, [Import(LoginContainer.Exports.UserID)]uint user_id) {
_bus = bus;
_service = service;
_error = error;
_user_id = user_id;
}
public IObservable<IReadOnlyDictionary<uint, ExecutionStrategyImmutable>> strategy => _bus.subscribe(SparkTags.SyntheticstrategiesTag);
@@ -39,29 +42,40 @@ namespace mbts.spark {
return (result, success);
});
public async Task<ExecutionStrategyImmutable?> lookup_strategy_async(SecurityImmutable security) =>
await lookup_strategy_async(security.Id);
public Task<ExecutionStrategyImmutable?> lookup_strategy_async(SecurityImmutable security) =>
lookup_strategy_async(security.Id);
public async Task<ExecutionStrategyImmutable?> lookup_strategy_async(uint id) {
var result = await _service.query_security_metadata_async(new MetadataQuery { SecurityKey = { id } });
if (result.success) {
var metadata = result.value.Data.FirstOrDefault(i => i.SecurityKey == id);
if (metadata != null) {
return metadata.Strategy;
}
public Task<ExecutionStrategyImmutable?> lookup_strategy_async(uint id) =>
_error.safe_execute(
() => _service.query_security_metadata_async(new MetadataQuery { SecurityKey = { id } }),
mdc => mdc.Data.Count == 1 && mdc.Data[0].SecurityKey == id,
mdc => $"did not find strategy for security id '{id}.'",
(mdc, success) => success ? mdc.Data[0].Strategy : (ExecutionStrategyImmutable?)null,
"lookup strategy");
public Task<uint?> create_synthetic(SecurityImmutable synthetic) {
if (synthetic.ContractCase != Security.ContractOneofCase.Synthetic)
return null;
if (synthetic.Synthetic.UserId != _user_id) {
var msynth = synthetic.CloneToMutable();
msynth.Synthetic.UserId = _user_id;
synthetic = new SecurityImmutable(msynth);
}
return null;
return _error.safe_execute(
() => _service.create_synthetic_async(synthetic),
r => r.Error == CreateResult.Types.Error.Ok,
r => r.Error.ToString(),
(r, success) => success ? r.Key : (uint?)null,
"creating synthetic");
}
public async Task<uint?> create_synthetic(SecurityImmutable? synthetic) {
var result = await _service.create_synthetic_async(synthetic.Value);
return _error.handle_error(result, "Error creating synthetic: {0}", r => r.Error, e => e == CreateResult.Types.Error.Ok, r => new uint?(r.Key), () => null).result;
}
public async Task<bool> save_strategy(uint synthetic, ExecutionStrategyImmutable strategy) {
var result = await _service.update_security_metadata_async(new Metadata { SecurityKey = synthetic, Strategy = strategy.CloneToMutable() });
return _error.handle_error(result, "Error saving execution strategy: {0}", r => r.Error, e => e == UpdateResult.Types.Error.Ok);
}
public Task<bool> save_strategy(uint synthetic, ExecutionStrategyImmutable strategy) =>
_error.safe_execute(
() => _service.update_security_metadata_async(new Metadata { SecurityKey = synthetic, Strategy = strategy.CloneToMutable() }),
r => r.Error == UpdateResult.Types.Error.Ok,
r => r.Error.ToString(),
(r, success) => success,
"saving strategy");
public async Task<bool> verify_name_is_unique(string name) {
var (result, success) = await strategy_tuple.FirstAsyncTask(SparkUtils.DefaultTimeOut);
@@ -134,7 +134,7 @@ namespace mbts.spark {
_synthetic.ContractSpec.PriceBreak = double.MaxValue;
_synthetic.ContractSpec.MainFraction = (uint)MathUtils.factor_out(_tick_factors.tick_base, 10);
_synthetic.ContractSpec.PriceDisplayFormat = (uint)_tick_factors.display_digits();
uint? id = await model.create_synthetic(_synthetic);
uint? id = await model.create_synthetic(new SecurityImmutable(_synthetic));
if (id.HasValue && id != 0) {
if (await model.save_strategy(id.Value, get_strategy())) {
var (smd, success) = await SparkUtils.get_security_md_async(bus, id.Value);
@@ -1,5 +1,4 @@
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.persist.algo;
@@ -110,38 +109,24 @@ namespace mbts.spark {
public void Dispose() =>
Disposable.dispose(ref _dispose);
async Task<AlgoData?> _create_definition(CompositeAlgoDefinitionImmutable definition) {
if (_try_get_user(out uint user_id)) {
return await _create_definition(definition, user_id);
} else {
_report_no_user_id(ActionMetadata.CreateDefinition);
}
return null;
}
async Task<AlgoData?> _create_definition(CompositeAlgoDefinitionImmutable definition) =>
await _create_definition(definition, _admin_model.user.id);
async Task<AlgoTemplateData?> _create_template(AlgoTemplateImmutable template) {
if (_try_get_user(out uint user_id)) {
var (value, success) = await _error_reporter.safe_execute(() => _service.create_template_async(
new TemplateCreateRequest { UserId = user_id, NewTemplate = template.CloneToMutable() }),
r => r.Success, r => r.ErrMsg, ActionMetadata.CreateTemplate);
if (success)
return _Utils.template_data(value.Record);
} else {
_report_no_user_id(ActionMetadata.CreateTemplate);
}
var (value, success) = await _error_reporter.safe_execute(() => _service.create_template_async(
new TemplateCreateRequest { UserId = _admin_model.user.id, NewTemplate = template.CloneToMutable() }),
r => r.Success, r => r.ErrMsg, ActionMetadata.CreateTemplate);
if (success)
return _Utils.template_data(value.Record);
return null;
}
async Task<AlgoTemplateData?> _update_template(AlgoTemplateRecordImmutable template) {
if (_try_get_user(out uint user_id)) {
var (value, success) = await _error_reporter.safe_execute(() => _service.update_template_async(
new TemplateUpdateRequest { Key = template.Key, UpdatedTemplate = template.AlgoTemplate.CloneToMutable() }),
r => r.Success, r => r.ErrMsg.ToString(), ActionMetadata.UpdateTemplate);
if (success)
return _Utils.template_data(template);
} else {
_report_no_user_id(ActionMetadata.UpdateTemplate);
}
var (value, success) = await _error_reporter.safe_execute(() => _service.update_template_async(
new TemplateUpdateRequest { Key = template.Key, UpdatedTemplate = template.AlgoTemplate.CloneToMutable() }),
r => r.Success, r => r.ErrMsg.ToString(), ActionMetadata.UpdateTemplate);
if (success)
return _Utils.template_data(template);
return null;
}
@@ -175,17 +160,6 @@ namespace mbts.spark {
void _report_no_user_id(string desc) =>
_error_reporter.report_error($"{desc} failed: did not find current user id");
bool _try_get_user(out uint id) {
var user = _admin_model.get_current_user();
if (user.HasValue) {
id = user.Value.id;
return true;
} else {
id = 0;
return false;
}
}
// used for error handling/reporting
interface IActionMetadata {
string desc { get; }
@@ -1,14 +1,11 @@
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman.algo;
using bts.persist.algo;
using bts.spark;
using mbts.spark.utilities;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
@@ -27,7 +24,12 @@ namespace mbts.spark {
_admin_model = admin_model;
_error_reporter = error_reporter;
this.ui_configuration = ui_configuration;
definitions = service.subscribei_algo_definitions().Filter(r => r.Metadata.IsActive).SelectKeyValue(_definition_data, d => d.key).Table().Cache();
definitions =
service.subscribei_algo_definitions()
.Filter(r => r.Metadata.IsActive && r.Metadata.CreatorUserId == _admin_model.user.id)
.SelectKeyValue(_definition_data, d => d.key)
.Table()
.Cache();
}
public IObservable<TableUpdate<AlgoData, AlgoKey>> definitions { get; }
@@ -39,15 +41,11 @@ namespace mbts.spark {
Observable.Return(TableUpdate<AlgoTemplateData, AlgoKey>.mk_empty(t => t.key));
public async Task<AlgoData?> save_definition(AlgoData definition_data) {
if (_try_get_user(out uint user_id)) {
var (value, success) = await _error_reporter.safe_execute(() => _service.save_algo_definition_async(
_with_key(new AlgoDefinitionUpdateRequest { UserId = user_id, Algo = _template(definition_data) }, definition_data.key)),
r => r.Success, r => r.ErrorMessage, "saving template");
if (success)
return _definition_data(new AlgoDefinitionRecordImmutable(value.Record));
} else {
_report_no_user_id("save template");
}
var (value, success) = await _error_reporter.safe_execute(() => _service.save_algo_definition_async(
_with_key(new AlgoDefinitionUpdateRequest { UserId = _admin_model.user.id, Algo = _template(definition_data) }, definition_data.key)),
r => r.Success, r => r.ErrorMessage, "saving template");
if (success)
return _definition_data(new AlgoDefinitionRecordImmutable(value.Record));
return null;
}
@@ -105,16 +103,5 @@ namespace mbts.spark {
void _report_no_user_id(string desc) =>
_error_reporter.report_error($"{desc} failed: did not find current user id");
bool _try_get_user(out uint id) {
var user = _admin_model.get_current_user();
if (user.HasValue) {
id = user.Value.id;
return true;
} else {
id = 0;
return false;
}
}
}
}
@@ -51,6 +51,9 @@ namespace mbts.spark {
readonly Property<AlgoFormats> _selected_format;
readonly EnumMaskInt32<AlgoEditingPermissions> _permissions_mask = new EnumMaskInt32<AlgoEditingPermissions>();
mbts.ui.spark.AlgoContentTemplateSelector.OrderKind IAlgoSelector.order_kind =>
new mbts.ui.spark.AlgoContentTemplateSelector.OrderKind(string.Empty, string.Empty);
int? _favorite_index;
Brush _badge_fill;
Snapshot? _committed;
@@ -339,6 +339,7 @@ namespace mbts.spark {
zoom_slider_visible = false,
zoom_factor = _default_zoom_factor,
net_change = NetChangeTerms.None,
mini_ob = new LadderMiniOrderBookSettings(bts.shell.orderbook.OrderBookStartupData.default_mini_grid.general),
};
rv.row_height = LadderRender.measure_row_height(rv);
return rv;
@@ -96,14 +96,28 @@ namespace bts.shell {
lp => new SettingsProfile<LadderSettings>(lp.id, lp.name, LadderSettings.sanitize(lp.value), lp.security_criteria))),
(us, lps) => us.with(ladder_profiles: lps.Values.Select(lp => new LadderSettingsProfile(lp.id, lp.name, lp.value, lp.security_criteria)).ToArray()));
state_settings?.sanitize();
/// When settings are adjusted outside of the settings dialog, the vm should raise the <see cref="LadderVM.settings_invalidated"/> event.
/// In response to that event, 'needs_resync' is set to true. This causes 'last_value' to be refreshed when the next caller requests the latest settings
/// (i.e., an IObserver subscribing to the settings_source, or the settings-dialog viewmodel).
bool initialized = false, needs_resync = false;
LadderSettings last_value = fit_to_sec(state_settings);
var settings_subject = new BehaviorSubject<LadderSettings>(last_value);
LadderVM vm = null;
var settings_subject = new Subject<LadderSettings>();
var settings_source = Observable.Create<LadderSettings>(o => {
ensure_synced();
if (last_value != null)
o.OnNext(last_value);
return settings_subject.Subscribe(s => {
if (s != null)
o.OnNext(s);
});
});
// vm
var vm = LadderVM.create(_bus, model, last_value ?? _settings_hub.current.ladder, settings_subject.Where(s => s != null));
vm = LadderVM.create(_bus, model, LadderSettings.sanitize(last_value ?? _settings_hub.current.ladder), settings_source);
vm.settings_invalidated += on_settings_invalidated;
vm.order_properties_vm.open_algo_popup_on_select = _ladder_conf.open_algo_popup_on_select;
vm.order_properties_vm.use_algo_param_grid = _ladder_conf.use_algo_param_grid;
vm.request_settings_dialog += open_settings_dialog;
bool initialized = false;
var settings_subscription = _settings_hub.subscribe_default_settings().Subscribe(on_user_settings);
vm.OnDisposed += disposing => {
Disposable.dispose(ref settings_subscription);
@@ -119,8 +133,25 @@ namespace bts.shell {
LadderSettings fit_to_sec(LadderSettings s) =>
_fit_to_sec(s, md.security.ContractCase);
void on_settings_invalidated() =>
needs_resync = true;
void ensure_synced() {
if (!needs_resync)
return;
var curr = vm.get_save_state();
if (settings_id == 0) {
last_value = curr;
} else if (!curr.Equals(last_value)) {
settings_id = 0;
last_value = curr;
}
needs_resync = false;
}
void on_user_settings(UserSettings us) {
if (initialized) {
ensure_synced();
if (settings_id == 0) {
// not on a profile, don't respond to default settings changes
return;
@@ -290,4 +321,4 @@ namespace bts.shell {
[ProtoMember(3)]
public uint settings_id;
}
}
}
@@ -355,8 +355,19 @@ namespace mbts.spark {
}
public LadderSettings get_result() {
var clone = SparkUtils.clone_proto(_current);
clone.mini_ob = new LadderMiniOrderBookSettings(mini_ob.create());
LadderSettings clone;
if (_current == null) {
clone = new LadderSettings {
mini_ob = new LadderMiniOrderBookSettings(mini_ob.create())
};
} else {
clone = SparkUtils.clone_proto(_current);
#pragma warning disable CS0612 // Type or member is obsolete
clone.mini_ob = new LadderMiniOrderBookSettings(_current.mini_ob.value != null ?
mini_ob.create().with(sorting_criterion: _current.mini_ob.value.sorting_criterion) :
mini_ob.create());
#pragma warning restore CS0612 // Type or member is obsolete
}
clone.badge_style = badge_style;
clone.alternate_order_size = alternate_order_size;
clone.favorite_algos = favorite_algos_vm.create();
@@ -241,7 +241,7 @@ namespace mbts.spark {
public IBus bus => _bus;
public event Action on_tick, request_settings_dialog;
public event Action on_tick, request_settings_dialog, settings_invalidated;
public MDConsumerHandle md => _model.md;
// we return only entries who have piqs and whose piqs are not uint.Max
@@ -394,6 +394,12 @@ namespace mbts.spark {
}
}
public void on_mini_orderbook_closed(bts.shell.orderbook.OrderBookStartupData ob_state) {
current_settings = SparkUtils.clone_proto(current_settings);
current_settings.mini_ob = new LadderMiniOrderBookSettings(ob_state.orderbook_grid.general);
settings_invalidated?.Invoke();
}
public void send_order((double price, Side? side, MouseButton button) args) {
if (order_properties_vm == null || args.button == MouseButton.Middle)
return;
@@ -51,7 +51,7 @@ namespace mbts.spark {
LadderVM.create_readonly(_bus,
new LadderModel(security_md.Value, _model, _model.mbo_service,
new Func<OrderSecurity, bool>(osec => _selected_order_tree.Contains(osec.order.Key))),
Observable.Return(LadderSettings.Default)) : null);
Observable.Return(LadderSettings.sanitize(LadderSettings.Default))) : null);
RaisePropertyChanged(nameof(value));
}
}
@@ -67,7 +67,8 @@ namespace mbts.spark {
NativePriceSize _last_trade;
Func<NativePriceSize, string> _fmt_size = MDDisplayHelper.get_formatter_nps(MarketSizeDisplay.get_default());
int _old_tooltip_row = int.MaxValue;
double _old_last_trade_price = 0.0;
NativePriceSize? _old_last_trade;
string _direction_arrow = string.Empty;
bool _tooltips_are_hooked_up = false;
ToolTip tooltip_view = new ToolTip();
IDisposable _parameterized_order_popup;
@@ -693,12 +694,14 @@ namespace mbts.spark {
// At any rate, we need this part hiding the tooltip so that we don't leave random tooltips around
// and we have to reset to old row so that when we go back to what could be our preivous row, the tooltip gets drawn again
//System.Diagnostics.Debug.WriteLine(" Tooltip - Volume is 0");
_old_tooltip_row = int.MaxValue;
_hide_tool_tip();
//_old_tooltip_row = Int32.MaxValue;
}
}
} else {
//System.Diagnostics.Debug.WriteLine(" Tooltip - not over Volume column");
_old_tooltip_row = int.MaxValue;
_hide_tool_tip();
}
}
@@ -861,21 +864,43 @@ namespace mbts.spark {
void _refresh_volume_at_price() {
if (!_settings.volume_column.general.show)
return;
// if the last trade has a new size then we need to explicitly
// mark the volume container as dirty because there is a chance
// we have arrived here off a size change and not a volume change
// or a random change, like applying new settings
bool last_trade_size_changed = _old_last_trade.HasValue ? _last_trade.size != _old_last_trade.Value.size : true;
if (last_trade_size_changed && _settings.volume_column.show_ltq) {
_volume_container.mark_dirty();
}
var vapls = _volume_container.read_new_value();
if (vapls != null) {
Log.assert_d(_price_list != null);
var ltp = _last_trade.price;
var last_index = LadderRender.index_of(_price_list, ltp, _price_comparer);
string direction_arrow = string.Empty;
if (MathUtils.epsilon_equals(ltp, _old_last_trade_price)) {
// do nothing if in here b/c direction_arrow already string.empty
} else if (ltp > _old_last_trade_price) {
direction_arrow = "▲";
} else {
direction_arrow = "▼";
}
LadderRender.render_volumes(_volume_vis_tup.visual, _volume_vis_tup.glyph_context, _price_list, vapls, _settings.row_height, security.ContractSpec, _settings.volume_column, last_index, _last_trade, direction_arrow);
_old_last_trade_price = ltp;
var last_index = LadderRender.index_of(_price_list, _last_trade.price, _price_comparer);
LadderRender.render_volumes(_volume_vis_tup.visual, _volume_vis_tup.glyph_context, _price_list, vapls, _settings.row_height, security.ContractSpec, _settings.volume_column, last_index, _get_last_arrow_string());
_old_last_trade = _last_trade;
}
string _get_last_arrow_string() {
// if _old_last_trade doesn't have a value, this is the first time the ladder has been launched
// and so the direction should be drawn without an arrow since there is no basis for comparison
if (!_old_last_trade.HasValue) {
_direction_arrow = string.Empty;
// if the Lasts are different then we need to reformat the direction arrow in some fashion
} else if (_old_last_trade != _last_trade) {
var ltp = _last_trade.price;
if (MathUtils.epsilon_equals(ltp, _old_last_trade.Value.price)) {
// update on the same price, so size must be different
_direction_arrow = string.Empty;
} else if (ltp > _old_last_trade.Value.price) {
_direction_arrow = "▲";
} else {
_direction_arrow = "▼";
}
}
// regardless of if we've arrived here via a new Last (and new direction arrow)
// or we're reusing the old one, this format bit is the same
return $"{_direction_arrow} {_last_trade.size} ";
}
}
@@ -9,9 +9,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;
namespace mbts.spark {
@@ -156,7 +154,10 @@ namespace mbts.spark {
popup.SetBinding(System.Windows.Controls.Primitives.Popup.IsOpenProperty,
nameof(OrderBookVM.is_row_collection_populated)
);
popup.Closed += (sender, event_args) => (popup.DataContext as IDisposable)?.Dispose();
popup.Closed += (sender, event_args) => {
(_parent.DataContext as LadderVM)?.on_mini_orderbook_closed(obvm.create());
(popup.DataContext as IDisposable)?.Dispose();
};
popup.MouseUp += (sender, event_args) => {
if (event_args.ChangedButton == MouseButton.Middle) {
popup.IsOpen = false;
@@ -196,8 +196,7 @@ namespace mbts.spark {
ContractSpecificationImmutable spec,
VolumeColumnSettings settings,
int last_index,
NativePriceSize last_trade,
string direction_arrow
string last_size_and_direction
) {
if (!data.Any()) {
_clear_visual(dv);
@@ -216,16 +215,16 @@ namespace mbts.spark {
Brush total_brush = null;
if (settings.total.bars.result.HasValue) {
total_brush = new SolidColorBrush(settings.total.bars.result.Value);
} else if (components.Count == 0) {
} else if (components.Count == 0 && !settings.show_ltq) {
_clear_visual(dv);
return;
}
// inflate the max so that the graph never fills the available space
double max = data.Max(v => v.Volume) * 1.1;
Brush text_brush = settings.general.foreground.rgb_brush();
_render_cells(dv, row_height, data.Select(d => (d.Price, d)), settings.general, prices, spec, render_cell);
/// local methods
void render_cell(DrawingContext dc, int r, VolumeAtPriceLevelImmutable vap) {
Brush text_brush = settings.general.foreground.rgb_brush();
double y = row_height * r;
var last_trade_here = r == last_index;
@@ -259,7 +258,8 @@ namespace mbts.spark {
}
if (settings.show_ltq && last_trade_here) {
numbers_text.Clear();
numbers_text.Add($"{direction_arrow} {last_trade.size} ");
numbers_text.Add(last_size_and_direction);
text_brush = BTSCoreColors.white;
}
if (numbers_text.Count > 0) {
string str = string.Join("/", numbers_text);
@@ -14,7 +14,6 @@
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}">
@@ -1177,7 +1177,6 @@ namespace mbts.spark {
public class CommitColumn : FrameworkElementColumnBase<Columns> {
public CommitColumn() : base(Columns.Commit) {
id = key.ToString();
Header = "";
Width = 20;
}
@@ -331,8 +331,8 @@ namespace mbts.spark {
IDisposable connect_order_mod_vm() =>
Disposable.Create(
_selected_params_mon.subscribe_algos().Subscribe(order_mod_vm.algo_vm.load_many),
order_mod_vm.subscribe_is_dirty().Subscribe(_ => {
_selected_params_mon.subscribe_algos().OnDispatcher().Subscribe(order_mod_vm.algo_vm.load_many),
order_mod_vm.subscribe_is_dirty().OnDispatcher().Subscribe(_ => {
modify_selected_orders_async_cmd.RaiseCanExecuteChanged();
revert_selected_orders_cmd.RaiseCanExecuteChanged();
}),
@@ -340,10 +340,10 @@ namespace mbts.spark {
IDisposable connect_common_selected_params(OrderParamsVM vm) =>
Disposable.Create(
_selected_params_mon.subscribe_security().to_security_md(bus).OnDispatcher().Subscribe(newval => vm.security_md = newval),
_selected_params_mon.subscribe_price().Subscribe(vm.on_common_price),
_selected_params_mon.subscribe_size().Subscribe(vm.on_common_size),
_selected_params_mon.subscribe_account().Subscribe(newval => vm.selected_account = newval));
_selected_params_mon.subscribe_security().OnDispatcher().to_security_md(bus).OnDispatcher().Subscribe(newval => vm.security_md = newval),
_selected_params_mon.subscribe_price().OnDispatcher().Subscribe(vm.on_common_price),
_selected_params_mon.subscribe_size().OnDispatcher().Subscribe(vm.on_common_size),
_selected_params_mon.subscribe_account().OnDispatcher().Subscribe(newval => vm.selected_account = newval));
}
private void _row_collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
@@ -994,11 +994,13 @@ namespace mbts.spark {
var join = config.Configuration.get<bts.config.JoinAttachUIOptions>();
var lean = config.Configuration.get<bts.config.LeanAttachUIOptions>();
var top = config.Configuration.get<bts.config.TopAttachUIOptions>();
var offset = config.Configuration.get<bts.config.OffsetAttachUIOptions>();
_algo_type_conf = new Dictionary<CustomOrderDefinition.CustomOneofCase, (bool, bool)> {
{ CustomOrderDefinition.CustomOneofCase.Cover, (cover.show_price, cover.show_quantity) },
{ CustomOrderDefinition.CustomOneofCase.Join, (join.show_price, join.show_quantity) },
{ CustomOrderDefinition.CustomOneofCase.Lean, (lean.show_price, lean.show_quantity) },
{ CustomOrderDefinition.CustomOneofCase.Top, (top.show_price, top.show_quantity) }
{ CustomOrderDefinition.CustomOneofCase.Top, (top.show_price, top.show_quantity) },
{ CustomOrderDefinition.CustomOneofCase.Offset, (offset.show_price, offset.show_quantity) }
};
}
@@ -144,7 +144,7 @@ namespace bts.shell.orderbook {
pb.add_discrete_values(fs.symbols, o => o.security?.Symbol);
// numeric
pb.add_numeric(fs.qty, o => o.order.quantity());
pb.add_numeric(fs.price, o => o.order.price());
pb.add_price(fs.price, o => o.order.price(), o => o.security);
pb.add_numeric(fs.qty_filled, o => o.order.filled());
pb.add_numeric(fs.avg_price, o => o.order.avg_price());
pb.add_numeric(fs.last_price, o => o.order.last_price());
+38 -18
View File
@@ -26,7 +26,11 @@ namespace mbts.spark.utilities {
public readonly Func<CustomOrderDefinition, CustomOrderDefinition> get_child;
public readonly Action<CustomOrderDefinition, CustomOrderDefinition> assign_child;
public ChildAlgoData(string name, Func<IMessage, CustomOrderDefinition> get_child_from_impl, Action<IMessage, CustomOrderDefinition> assign_child_to_impl, Func<CustomOrderDefinition, CustomOrderDefinition> get_child, Action<CustomOrderDefinition, CustomOrderDefinition> assign_child) {
public ChildAlgoData(string name, Func<IMessage, CustomOrderDefinition> get_child_from_impl,
Action<IMessage, CustomOrderDefinition> assign_child_to_impl,
Func<CustomOrderDefinition, CustomOrderDefinition> get_child,
Action<CustomOrderDefinition, CustomOrderDefinition> assign_child)
{
this.name = name;
this.get_child = get_child;
this.assign_child = assign_child;
@@ -168,17 +172,14 @@ namespace mbts.spark.utilities {
// this will be the tag 50 on CME orders
// current user might be okay, but we might also want to get it
// from the store that serves the risk admin gui
ManualOrderIndicator = ManualOrderIndicator.Yes
ManualOrderIndicator = ManualOrderIndicator.Yes,
User = _admin_model.user.name,
UserId = _admin_model.user.id
};
if (account.HasValue) {
rv.Account = account.Value.name;
rv.AccountId = account.Value.id;
}
var user = _admin_model.get_current_user();
if (user.HasValue) {
rv.User = user.Value.name;
rv.UserId = user.Value.id;
}
return rv;
}
@@ -215,6 +216,7 @@ namespace mbts.spark.utilities {
public TriggerOrderDefinition trigger_order() => OrderDataHelper.Defaults.TriggerOrderDefinition();
public DynamicOrderDefinition dynamic_order() => OrderDataHelper.Defaults.DynamicOrderDefinition();
public SpreaderOrderDefinition spreader_order() => OrderDataHelper.Defaults.SpreaderOrderDefinition();
public SplitterOrderDefinition splitter_order() => OrderDataHelper.Defaults.SplitterOrderDefinition();
public SpreaderOrderDefinition spreader_order(SecurityImmutable security, ExecutionStrategyImmutable strategy) =>
new SpreaderOrderDefinition {
Security = security.CloneToMutable(),
@@ -245,8 +247,12 @@ namespace mbts.spark.utilities {
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;
case CustomOrderDefinition.CustomOneofCase.Splitter: rv.Splitter = splitter_order(); break;
case CustomOrderDefinition.CustomOneofCase.Dynamic: rv.Dynamic = dynamic_order(); break;
//case CustomOrderDefinition.CustomOneofCase.QueueHolder: rv.QueueHolder = queue_holder_order(); break;
default:
OrderTypeVMFactory.GenericOrderFactory.setup_generic_custom_order(rv, order_type);
break;
}
return rv;
}
@@ -272,6 +278,7 @@ namespace mbts.spark.utilities {
case CustomOrderDefinition.CustomOneofCase.Offset: return new OrderMetadata { KeyBinding = "O", Color = BTSColors.Orders.off_order };
case CustomOrderDefinition.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.trigger_order };
case CustomOrderDefinition.CustomOneofCase.Spreader: return new OrderMetadata { KeyBinding = "", Color = BTSColors.Orders.spreader_order };
// case CustomOrderDefinition.CustomOneofCase.Splitter: return new OrderMetadata { KeyBinding = "", Color = BTSColors.orange.rgb_color() };
case CustomOrderDefinition.CustomOneofCase.Dynamic: return new OrderMetadata { KeyBinding = "", Color = BTSColors.Orders.top_order };
case CustomOrderDefinition.CustomOneofCase.None: goto default;
//case CustomOrderDefinition.CustomOneofCase.QueueHolder: return new OrderMetadata { KeyBinding = "Q", Color = BTSColors.purple.rgb_color() };
@@ -592,9 +599,8 @@ namespace mbts.spark.utilities {
new AlgoStructureKey(defi.CustomCase)));
public static readonly CustomOrderDefinition.CustomOneofCase[]
// The following standard types are not yet ready for production use:
// CustomOrderDefinition.CustomOneofCase.Tick,
// CustomOrderDefinition.CustomOneofCase.Dynamic,
// exchange_order_types are the types which can be submitted on an exchange security
exchange_order_types = new[] {
CustomOrderDefinition.CustomOneofCase.Market,
CustomOrderDefinition.CustomOneofCase.Limit,
@@ -605,24 +611,32 @@ namespace mbts.spark.utilities {
CustomOrderDefinition.CustomOneofCase.Lean,
CustomOrderDefinition.CustomOneofCase.Join,
CustomOrderDefinition.CustomOneofCase.Cover,
CustomOrderDefinition.CustomOneofCase.Top
CustomOrderDefinition.CustomOneofCase.Top,
CustomOrderDefinition.CustomOneofCase.Offset,
CustomOrderDefinition.CustomOneofCase.Splitter,
CustomOrderDefinition.CustomOneofCase.Scratch,
CustomOrderDefinition.CustomOneofCase.Prorata,
},
// The following spreader types are not yet ready for production use:
// CustomOrderDefinition.CustomOneofCase.Dynamic,
spreader_order_types = new[] {
CustomOrderDefinition.CustomOneofCase.Spreader,
CustomOrderDefinition.CustomOneofCase.Iceberg,
CustomOrderDefinition.CustomOneofCase.Lean,
CustomOrderDefinition.CustomOneofCase.Join,
CustomOrderDefinition.CustomOneofCase.Cover,
CustomOrderDefinition.CustomOneofCase.Top
CustomOrderDefinition.CustomOneofCase.Cover,
CustomOrderDefinition.CustomOneofCase.Top,
CustomOrderDefinition.CustomOneofCase.Splitter,
CustomOrderDefinition.CustomOneofCase.Scratch,
CustomOrderDefinition.CustomOneofCase.Prorata,
},
attachable_order_types = new[] {
CustomOrderDefinition.CustomOneofCase.Iceberg,
CustomOrderDefinition.CustomOneofCase.Lean,
CustomOrderDefinition.CustomOneofCase.Join,
CustomOrderDefinition.CustomOneofCase.Cover,
CustomOrderDefinition.CustomOneofCase.Top
CustomOrderDefinition.CustomOneofCase.Top,
CustomOrderDefinition.CustomOneofCase.Offset,
CustomOrderDefinition.CustomOneofCase.Scratch,
CustomOrderDefinition.CustomOneofCase.Prorata,
},
outright_order_types = new[] {
CustomOrderDefinition.CustomOneofCase.Market,
@@ -718,7 +732,7 @@ namespace mbts.spark.utilities {
!b.HasChild);
public static bool top_equals(TopOrderDefinitionImmutable a, TopOrderDefinitionImmutable b) =>
a.WatchLevelTickOffset == b.WatchLevelTickOffset &&
a.TicksOff == b.TicksOff &&
(a.HasTopThreshold ?
b.HasTopThreshold && quantity_measure_equals(a.TopThreshold, b.TopThreshold) :
!b.HasTopThreshold) &&
@@ -909,7 +923,7 @@ namespace mbts.spark.utilities {
add_component(_get_hash(_get_tick_child(tick)));
break;
case CustomOrderDefinition.CustomOneofCase.Top:
add_component(def.Top.WatchLevelTickOffset.GetHashCode());
add_component(def.Top.TicksOff.GetHashCode());
add_component(def.Top.HasTopThreshold ? def.Top.TopThreshold.GetHashCode() : 0);
add_component(_get_top_child(def.Top).GetHashCode());
break;
@@ -1008,6 +1022,9 @@ namespace mbts.spark.utilities {
case bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Trigger: rv.Trigger = TriggerOrderDefinition(); break;
case bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = SpreaderOrderDefinition(); break;
case bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Dynamic: rv.Dynamic = DynamicOrderDefinition(); break;
default:
OrderTypeVMFactory.GenericOrderFactory.setup_generic_custom_order(rv, order_type);
break;
}
return rv;
}
@@ -1033,6 +1050,9 @@ namespace mbts.spark.utilities {
public static TriggerOrderDefinition TriggerOrderDefinition() => new TriggerOrderDefinition { Child = CustomOrderDefinition(), TriggerCondition = new TriggerCondition(), TriggerAction = new TriggerAction(), };
public static DynamicOrderDefinition DynamicOrderDefinition() => new DynamicOrderDefinition();
public static SpreaderOrderDefinition SpreaderOrderDefinition() => new SpreaderOrderDefinition { ExecutionStrategy = new ExecutionStrategy() };
public static SplitterOrderDefinition SplitterOrderDefinition() => new SplitterOrderDefinition();
}
}
}
@@ -105,13 +105,18 @@ namespace mbts.spark {
var inline_account_src = order_properties.AggregateLatest(
model.factory.subscribe_accounts(),
(last, change, cache) => convert_inline_account(change.New.account, cache),
(last, props, change) => convert_inline_account(props.account, change.New),
(last, props, change) => convert_inline_account(props.account, change.New, false),
() => new InlineUserProperty<AccountTuple>(),
new InlineOrderProperties(),
DictionaryHelper.empty<uint, string>());
// Note that with the .Where on the account_tuples, we will ignore any
// updates for which all accounts have been removed.
// This was a safer change than changing SelectorProperty's IEnumberable.OnNext
// so that it would not reset the selected item on server reboots; i.e. this
// change only affects this IObservable.
_accounts = backing(
nameof(selected_account),
model.factory.subscribe_account_tuples(),
model.factory.subscribe_account_tuples().Where(items => items.Any()),
inline_account_src,
a => new AccountTuple?(a),
() => accounts.Cast<AccountTuple?>().FirstOrDefault(),
@@ -263,9 +268,18 @@ namespace mbts.spark {
break;
}
}
InlineUserProperty<AccountTuple> convert_inline_account(InlineUserProperty<uint> account_id, IReadOnlyDictionary<uint, string> account_map) =>
//
// Regarding the 3rd parameter, force_override_from_inline_property:
// When using an InlineUserProperty, in the context of assigning a default vs selected item,
// there is somewhat of a conflict between the IObserver<InlineUserProperty<TItem>> and the
// IObserver<IEnumerable<TItem>> OnNext methods. This 3rd paramter provides yet another override
// to say if the force_override should come from the inline property or if the force_override should
// be disbled, by way of the immediate return in ViewModelBase._try_auto_select when force_override
// is false. Our intent is then accomplished because we are no longer attempting to set a default/
// selected item when the account_tuples potentially changes
InlineUserProperty<AccountTuple> convert_inline_account(InlineUserProperty<uint> account_id, IReadOnlyDictionary<uint, string> account_map, bool force_override_from_inline_property = true) =>
account_id.has_value && account_map.TryGetValue(account_id.value, out string name) ?
new InlineUserProperty<AccountTuple>(new AccountTuple(account_id.value, name), account_id.force_override) :
new InlineUserProperty<AccountTuple>(new AccountTuple(account_id.value, name), force_override_from_inline_property ? account_id.force_override : false) :
new InlineUserProperty<AccountTuple>();
}
@@ -71,6 +71,7 @@ namespace mbts.spark {
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef: param.AsOrderDef = new CustomOrderDefinition(); break;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: param.AsString = string.Empty; break;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: param.AsUint32 = 0u; break;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveQuantity: param.AsQuantity = new bts.spark.QuantityMeasure(); break;
}
break;
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Structure: param.AsStruct = new DynamicOrderStruct(); break;
@@ -87,5 +88,16 @@ namespace mbts.spark {
}
param.AsEnumerated.EnumValue = value ?? string.Empty;
}
internal static bts.spark.QuantityMeasureImmutable? get_quantity_value(DynamicOrderParam param)
{
return new bts.spark.QuantityMeasureImmutable(param?.AsQuantity);
}
internal static void set_quantity_value(DynamicOrderParam param, bts.spark.QuantityMeasureImmutable? value)
{
if (param == null)
return;
param.AsQuantity = value?.CloneToMutable();
}
}
}
@@ -1,6 +1,7 @@
using bts.api.orderman;
using Google.Protobuf.Reflection;
using mbts.ui.shell;
using bts.spark;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -25,7 +26,7 @@ namespace mbts.spark {
protected DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor)
: this(descriptor, DynamicOrderHelper.init_param(descriptor)) { }
protected DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor, DynamicOrderParam param) {
private DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor, DynamicOrderParam param) {
this.descriptor = descriptor;
_param = param;
}
@@ -116,4 +117,41 @@ namespace mbts.spark {
void _sync_elements() =>
elements.sync_with(_get_elements(descriptor));
}
class DynamicOrderQuantityVM : DynamicOrderParamVM, IDynamicOrderParamVM<QuantityMeasureImmutable?>
{
static readonly FieldDescriptor _field_desc = DynamicOrderParam.Descriptor.FindFieldByNumber(DynamicOrderParam.AsQuantityFieldNumber);
public DynamicOrderQuantityVM(DynamicOrderParamDescriptor descriptor)
: base(descriptor) { }
QuantityMeasureImmutable? IDynamicOrderParamVM<QuantityMeasureImmutable?>.value { get => quantity; set => quantity= value; }
public QuantityMeasureImmutable? quantity
{
get => DynamicOrderHelper.get_quantity_value(param);
set
{
var orig = quantity;
DynamicOrderHelper.set_quantity_value(param, value);
if (quantity != orig)
RaisePropertyChanged();
}
}
public IObservable<QuantityMeasureImmutable?> subscribe_value()
{
return Observable.Create<QuantityMeasureImmutable?>(o =>
{
PropertyChanged += on_prop_changed;
return Disposable.Create(() => PropertyChanged -= on_prop_changed);
void on_prop_changed(object sender, PropertyChangedEventArgs e) =>
o.OnNext(quantity);
});
}
protected override void on_param_changed()
{
RaisePropertyChanged(nameof(quantity));
}
}
}
@@ -19,6 +19,7 @@ namespace mbts.ui.spark
public DataTemplate UIntTemplate { get; set; }
public DataTemplate StringTemplate { get; set; }
public DataTemplate OrderDefTemplate { get; set; }
public DataTemplate QuantityMeasureTemplate { get; set; }
public DataTemplate EnumTemplate { get; set; }
@@ -39,6 +40,7 @@ namespace mbts.ui.spark
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: return UIntTemplate;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: return StringTemplate;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef: return OrderDefTemplate;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveQuantity: return QuantityMeasureTemplate;
default: return null;
}
}
@@ -5,6 +5,7 @@ using mbts.collections;
using mbts.spark.utilities;
using mbts.ui.utility;
using mbts.utils;
using bts.spark;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -46,10 +47,13 @@ namespace mbts.spark {
protected override void on_model_internal() =>
_sync_dynamic_params();
protected override IEnumerable<ChildAlgoData> get_selector_datum() =>
OrderDataHelper.get_child_algo_data(new CustomOrderDefinition {
protected override IEnumerable<ChildAlgoData> get_selector_datum()
{
var data = OrderDataHelper.get_child_algo_data(new CustomOrderDefinition {
Dynamic = model?.dynamic_helper?.create_def(dyn_type) ?? new DynamicOrderDefinition()
});
return data;
}
void _sync_dynamic_params() {
if (model != null) {
@@ -70,9 +74,14 @@ namespace mbts.spark {
switch (descriptor.ParamType.Primitive) {
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveBool: return mk<bool?, bool>(DynamicOrderParam.AsBoolFieldNumber, v => v, v => v.GetValueOrDefault());
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveDouble: return mk<double, double>(DynamicOrderParam.AsDoubleFieldNumber, v => v, v => v, double.NaN);
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt: return mk<uint?, uint>(DynamicOrderParam.AsInt32FieldNumber, v => v, v => v.GetValueOrDefault());
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt: return mk<int?, int>(DynamicOrderParam.AsInt32FieldNumber, v => v, v => v.GetValueOrDefault());
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: return mk<uint?, uint>(DynamicOrderParam.AsUint32FieldNumber, v => v, v => v.GetValueOrDefault());
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: return mk<string, string>(DynamicOrderParam.AsStringFieldNumber, v => v, v => v);
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveQuantity:
{
var qm = new DynamicOrderQuantityVM(descriptor);
return attached(qm, DynamicOrderHelper.get_quantity_value, DynamicOrderHelper.set_quantity_value, new QuantityMeasure { Ratio = 1.0, Absolute = 1 });
}
default: throw ExceptionBuilder.Argument.unexpected_case(descriptor.ParamType.Primitive, nameof(descriptor));
}
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated:
@@ -97,11 +106,19 @@ namespace mbts.spark {
mdef => mdef.OrderParams.TryGetValue(pvm.descriptor.ParamName, out DynamicOrderParam p) ? selector(p) : default_value,
(mdef, v) => assign_to_param(mdef.OrderParams.GetOrAdd(pvm.descriptor.ParamName, pn => new DynamicOrderParam()), v),
() => pvm.value,
v => pvm.value = v,
v => pvm.value = set_value_not_null_string(v),
pvm.subscribe_value(),
default_value);
return pvm;
}
T set_value_not_null_string<T>(T val)
{
if (typeof(T) == typeof(string) && val == null)
return (T)(object)string.Empty;
else
return val;
}
}
}
}
@@ -0,0 +1,56 @@
using bts.api;
using bts.api.orderman;
using bts.utils;
using Google.Protobuf;
using Google.Protobuf.Reflection;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
namespace mbts.spark
{
[Export]
class GenericOrderHelper
{
[ImportingConstructor]
private GenericOrderHelper(OrderService order_service)
{
}
internal static string get_enumerated_value(IMessage message, FieldDescriptor fd)
{
var ev = fd.Accessor.GetValue(message);
return ev.ToString();
}
internal static void set_enumerated_value(IMessage message, FieldDescriptor fd, string value)
{
if (message == null)
{
return;
}
var ev = fd.EnumType.Values.First(x => x.Name == value)?.Number;
if(ev.HasValue)
fd.Accessor.SetValue(message, ev.Value);
}
internal static bts.spark.QuantityMeasureImmutable? get_quantity_value(IMessage message, FieldDescriptor fd)
{
var qm = (bts.spark.QuantityMeasure)fd.Accessor.GetValue(message);
if(qm != null)
return new bts.spark.QuantityMeasureImmutable(qm);
else
return null;
}
internal static void set_quantity_value(IMessage message, FieldDescriptor fd, bts.spark.QuantityMeasureImmutable? value)
{
if (message == null)
return;
fd.Accessor.SetValue(message, value?.CloneToMutable());
}
}
}
@@ -0,0 +1,183 @@
using bts.api.orderman;
using Google.Protobuf.Reflection;
using mbts.ui.shell;
using bts.spark;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using Google.Protobuf;
namespace mbts.spark
{
interface IGenericOrderParamVM
{
FieldDescriptor descriptor { get; }
Google.Protobuf.IMessage message { get; set; }
}
interface IGenericOrderParamVM<T> : IGenericOrderParamVM
{
T value { get; set; }
IObservable<T> subscribe_value();
}
abstract class GenericOrderParamVM<TMutable> : DisposableViewModelBase, IGenericOrderParamVM
where TMutable : class, IMessage, new()
{
TMutable _message;
protected GenericOrderParamVM(FieldDescriptor descriptor)
{
_message = new TMutable();
this.descriptor = descriptor;
}
public FieldDescriptor descriptor { get; set; }
public Google.Protobuf.IMessage message
{
get => _message;
set
{
_message = (TMutable)value;
on_param_changed();
}
}
protected abstract void on_param_changed();
}
class GenericOrderParamVM<TMutable, TView, TModel> : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<TView>
where TMutable : class, IMessage, new()
{
readonly Func<TModel, TView> _convert;
readonly Func<TView, TModel> _convert_back;
TView _value;
public GenericOrderParamVM(FieldDescriptor field_descriptor, Func<TModel, TView> convert, Func<TView, TModel> convert_back) :
base(field_descriptor)
{
_convert = convert;
_convert_back = convert_back;
}
public TView value
{
get => _value;
set
{
this.descriptor.Accessor.SetValue(this.message, _convert_back(_value = value));
RaisePropertyChanged();
}
}
public IObservable<TView> subscribe_value() =>
Observable.Create<TView>(o =>
{
PropertyChanged += on_prop_changed;
return Disposable.Create(() => PropertyChanged -= on_prop_changed);
void on_prop_changed(object sender, PropertyChangedEventArgs e) =>
o.OnNext(value);
});
protected override void on_param_changed()
{
_value = _convert(ConvertUtils.try_convert<TModel>(this.descriptor.Accessor.GetValue(this.message)));
RaisePropertyChanged(nameof(value));
}
}
class GenericOrderEnumeratedVM<TMutable> : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<string>
where TMutable : class, IMessage, new()
{
public GenericOrderEnumeratedVM(FieldDescriptor descriptor)
: base(descriptor)
{
elements = new ObservableCollection<string>(_get_elements(descriptor));
}
public ObservableCollection<string> elements { get; }
public string selected_element
{
get => GenericOrderHelper.get_enumerated_value(message, descriptor);
set
{
var orig = selected_element;
GenericOrderHelper.set_enumerated_value(message, descriptor, value);
if (selected_element != orig)
RaisePropertyChanged();
}
}
string IGenericOrderParamVM<string>.value { get => selected_element; set => selected_element = value; }
IObservable<string> IGenericOrderParamVM<string>.subscribe_value() =>
Observable.Create<string>(o =>
{
PropertyChanged += on_prop_changed;
return Disposable.Create(() => PropertyChanged -= on_prop_changed);
void on_prop_changed(object sender, PropertyChangedEventArgs e) =>
o.OnNext(selected_element);
});
protected override void on_param_changed()
{
_sync_elements();
RaisePropertyChanged(nameof(selected_element));
}
static IEnumerable<string> _get_elements(FieldDescriptor descriptor)
{
if(descriptor.FieldType == Google.Protobuf.Reflection.FieldType.Enum)
{
return descriptor.EnumType.Values.Select(x => x.Name);
}
else
return Enumerable.Empty<string>();
}
void _sync_elements()
{
elements.sync_with(_get_elements(descriptor));
}
}
class GenericOrderQuantityVM<TMutable> : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<QuantityMeasureImmutable?>
where TMutable : class, IMessage, new()
{
public GenericOrderQuantityVM(FieldDescriptor descriptor)
: base(descriptor) { }
QuantityMeasureImmutable? IGenericOrderParamVM<QuantityMeasureImmutable?>.value { get => quantity; set => quantity= value; }
public QuantityMeasureImmutable? quantity
{
get => GenericOrderHelper.get_quantity_value(message, descriptor);
set
{
var orig = quantity;
GenericOrderHelper.set_quantity_value(message, descriptor, value);
if (quantity != orig)
RaisePropertyChanged();
}
}
public IObservable<QuantityMeasureImmutable?> subscribe_value()
{
return Observable.Create<QuantityMeasureImmutable?>(o =>
{
PropertyChanged += on_prop_changed;
return Disposable.Create(() => PropertyChanged -= on_prop_changed);
void on_prop_changed(object sender, PropertyChangedEventArgs e) =>
o.OnNext(quantity);
});
}
protected override void on_param_changed()
{
RaisePropertyChanged(nameof(quantity));
}
}
}
@@ -0,0 +1,67 @@
using bts.api.orderman;
using mbts.spark;
using mbts.ui.converters;
using mbts.ui.utilities;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace mbts.ui.spark
{
class GenericOrderParamTemplateSelector : DataTemplateSelector
{
public DataTemplate BoolTemplate { get; set; }
public DataTemplate DoubleTemplate { get; set; }
public DataTemplate IntTemplate { get; set; }
public DataTemplate UIntTemplate { get; set; }
public DataTemplate StringTemplate { get; set; }
public DataTemplate OrderDefTemplate { get; set; }
public DataTemplate QuantityMeasureTemplate { get; set; }
public DataTemplate EnumTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var vm = item as IGenericOrderParamVM;
if (vm != null)
return select_template(vm.descriptor);
return null;
}
public DataTemplate select_template(Google.Protobuf.Reflection.FieldDescriptor descriptor)
{
switch (descriptor.FieldType)
{
case Google.Protobuf.Reflection.FieldType.Bool:
return BoolTemplate;
case Google.Protobuf.Reflection.FieldType.Float:
case Google.Protobuf.Reflection.FieldType.Double:
return DoubleTemplate;
case Google.Protobuf.Reflection.FieldType.Int32:
case Google.Protobuf.Reflection.FieldType.Int64:
return IntTemplate;
case Google.Protobuf.Reflection.FieldType.UInt32:
case Google.Protobuf.Reflection.FieldType.UInt64:
return UIntTemplate;
case Google.Protobuf.Reflection.FieldType.String:
return StringTemplate;
case Google.Protobuf.Reflection.FieldType.Enum:
return EnumTemplate;
case Google.Protobuf.Reflection.FieldType.Message:
{
if(descriptor.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName)
return QuantityMeasureTemplate;
else if (descriptor.MessageType.FullName == typeof(bts.api.orderman.CustomOrderDefinition).FullName)
return OrderDefTemplate;
}
break;
default: return null;
}
return null;
}
}
}
@@ -0,0 +1,177 @@

using bts;
using bts.api.orderman;
using bts.collections;
using mbts.collections;
using mbts.spark.utilities;
using mbts.ui.utility;
using mbts.utils;
using bts.spark;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using Google.Protobuf;
using Google.Protobuf.Reflection;
using bts.api;
using AlgoType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
namespace mbts.spark
{
static partial class OrderTypeVMFactory
{
internal class GenericOrderFactory
{
static public IAlgoVM create_vm(AlgoKey key, AlgoType algo_type, string display_name, AlgoEditingPermissions editing_permissions)
{
var prop = typeof(CustomOrderDefinition).GetProperty(algo_type.ToString(),
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
var rt = prop.PropertyType;
var propi = typeof(CustomOrderDefinitionImmutable).GetProperty(algo_type.ToString(),
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
var rti = propi.PropertyType;
var vmt = typeof(GenericOrderVM<,>);
Type[] typeargs = { rt, rti };
var makevm = vmt.MakeGenericType(typeargs);
object vm = Activator.CreateInstance(makevm, key, algo_type, display_name, editing_permissions);
return (IAlgoVM)vm;
}
static public void setup_generic_custom_order(CustomOrderDefinition custom, CustomOrderDefinition.CustomOneofCase type)
{
var prop = typeof(CustomOrderDefinition).GetProperty(type.ToString(),
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
var rt = prop.PropertyType;
var rtt = Activator.CreateInstance(rt);
var ci = rt.GetProperties().Where(x => x.PropertyType == typeof(CustomOrderDefinition));
foreach(var cid in ci)
cid.SetValue(rtt, new CustomOrderDefinition { Limit = new LimitOrderDefinition() });
prop.SetValue(custom, rtt);
}
}
class GenericOrderVM<TMutable, TImmutable> : OrderTypeVMBase<TMutable, TImmutable>
where TMutable : class, IMessage, new() where TImmutable : struct, IImmutableMessage
{
readonly ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string> _parameters =
new ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string>(vm => vm.descriptor.Name);
readonly AlgoType _algo_type;
public GenericOrderVM(AlgoKey key, AlgoType algo_type, string display_name, AlgoEditingPermissions editing_permissions)
: base(key, display_name, editing_permissions)
{
_algo_type = algo_type;
_parameters = new ObservableKeyedCollection<GenericOrderParamVM<TMutable>, string>(vm => vm.descriptor.Name);
_resources.AddRange(
ObservableAutoWrapper.connect_item_hooks(_parameters, p =>
{
p.PropertyChanged += on_param_prop_changed;
return Disposable.Create(() =>
{
p.PropertyChanged -= on_param_prop_changed;
remove_property(p.descriptor.Name);
p.Dispose();
});
}, true));
void on_param_prop_changed(object sender, PropertyChangedEventArgs e) => raise_definition_changed();
_sync_generic_params();
sync_selectors();
}
public string algo_type => _algo_type.ToString();
public override string abbreviation =>
model == null ? display_name : model.factory.abbreviate_algo(new OneOf<CustomOrderDefinition.CustomOneofCase, string>(_algo_type));
public override AlgoDisplayGroup group => AlgoDisplayGroup.API;
public ObservableCollection<GenericOrderParamVM<TMutable>> parameters => _parameters;
protected override void on_model_internal()
{
_sync_generic_params();
}
void _sync_generic_params()
{
if (model != null)
{
Google.Protobuf.Reflection.MessageDescriptor desc = new TMutable().Descriptor;
_parameters.SyncWithDictionary(
desc.Fields.InDeclarationOrder().Where(_is_value_param_desc).ToDictionary(p => p.Name), _mk_param_vm);
}
}
static bool _is_value_param_desc(FieldDescriptor desc)
{
return (desc.FieldType != Google.Protobuf.Reflection.FieldType.Message ||
_is_quantity_measure_desc(desc));
}
static bool _is_quantity_measure_desc(FieldDescriptor desc)
{
return desc.FieldType == Google.Protobuf.Reflection.FieldType.Message &&
desc.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName;
}
protected override IEnumerable<ChildAlgoData> get_selector_datum()
{
var v = OrderDataHelper.get_child_algo_data(_algo_type);
return v;
}
GenericOrderParamVM<TMutable> _mk_param_vm(FieldDescriptor descriptor)
{
ExceptionAssert.Argument.NotNull(descriptor, nameof(descriptor));
if (descriptor.FieldType == FieldType.Message &&
descriptor.MessageType.FullName == typeof(bts.spark.QuantityMeasure).FullName)
{
var qm = new GenericOrderQuantityVM<TMutable>(descriptor);
return attached(qm, GenericOrderHelper.get_quantity_value, GenericOrderHelper.set_quantity_value, new QuantityMeasure { Ratio = 1.0, Absolute = 1 });
}
else if(descriptor.FieldType == FieldType.Enum)
{
var pvm = new GenericOrderEnumeratedVM<TMutable>(descriptor);
attached(pvm, GenericOrderHelper.get_enumerated_value, GenericOrderHelper.set_enumerated_value);
return pvm;
}
else if (descriptor.FieldType == FieldType.Bool)
return mk<bool?, bool>(descriptor, v => v, v => v.GetValueOrDefault());
else if (descriptor.FieldType == FieldType.Double || descriptor.FieldType == FieldType.Float)
return mk<double, double>(descriptor, v => v, v => v, double.NaN);
else if (descriptor.FieldType == FieldType.Int32)
return mk<int?, int>(descriptor, v => v, v => v.GetValueOrDefault());
else if (descriptor.FieldType == FieldType.UInt32)
return mk<uint?, uint>(descriptor, v => v, v => v.GetValueOrDefault());
else if (descriptor.FieldType == FieldType.String)
return mk<string, string>(descriptor, v => v, v => v);
else
return null;
}
GenericOrderParamVM<TMutable, TView, TModel> mk<TView, TModel>(FieldDescriptor fdesc, Func<TModel, TView> convert, Func<TView, TModel> convert_back, TView default_value = default)
{
var pvm = new GenericOrderParamVM<TMutable, TView, TModel>(fdesc, convert, convert_back);
return attached<TView, GenericOrderParamVM<TMutable, TView, TModel>>(pvm, get_from_param, assign_to_param, default_value);
TView get_from_param(IMessage param, FieldDescriptor fd) => convert(ConvertUtils.try_convert<TModel>(fdesc.Accessor.GetValue(param)));
void assign_to_param(IMessage param, FieldDescriptor fd, TView value) => fdesc.Accessor.SetValue(param, convert_back(value));
}
TParam attached<T, TParam>(TParam pvm, Func<IMessage, FieldDescriptor, T> selector, Action<IMessage, FieldDescriptor, T> assign_to_param, T default_value = default) where TParam : GenericOrderParamVM<TMutable>, IGenericOrderParamVM<T>
{
add_property<T>(
pvm.descriptor.Name,
mdef => mdef != null ? selector(mdef, pvm.descriptor) : default_value,
(mdef, v) => assign_to_param(mdef, pvm.descriptor, v),
() => pvm.value,
v => pvm.value = v,
pvm.subscribe_value(),
default_value);
return pvm;
}
}
}
}
@@ -92,6 +92,7 @@ namespace mbts.spark {
IReadOnlyObservableCollection<IAlgoVM> algos_view { get; }
IAlgoVM selected_algo { get; set; }
void load_algo_data(IReadOnlyDictionary<AlgoKey, AlgoData> algo_data, AlgoKey? default_algo);
mbts.ui.spark.AlgoContentTemplateSelector.OrderKind order_kind {get;}
}
static class OrderTypeVMExtensions {
@@ -57,6 +57,14 @@ namespace mbts.spark {
public static IAlgoVM iceberg(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions) => new IcebergOrderVM(key, display_name, editing_permissions);
public static IAlgoVM spreader(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions) => new SpreaderOrderVM(key, display_name, editing_permissions);
public static IAlgoVM dynamic(AlgoKey key, string display_name, string dynamic_type, AlgoEditingPermissions editing_permissions) => new DynamicOrderVM(key, dynamic_type, display_name, editing_permissions);
public static IAlgoVM splitter(AlgoKey key, string display_name, AlgoEditingPermissions permissions) =>
new SplitterAlgoVM(key, display_name, permissions);
public static IAlgoVM generic(AlgoKey key, string display_name,
AlgoType algo_type, AlgoEditingPermissions editing_permissions)
{
return GenericOrderFactory.create_vm(key, algo_type, display_name, editing_permissions);
}
public static IAlgoVM create(AlgoData def_data, AlgoEditingPermissions editing_permissions = default_editing_permissions, bool is_expanded = false) {
if (def_data.value.CustomCase == AlgoType.Composite) {
@@ -88,8 +96,9 @@ namespace mbts.spark {
case AlgoType.Iceberg: vm = iceberg(key, name, editing_permissions); break;
case AlgoType.Spreader: vm = spreader(key, name, editing_permissions); break;
case AlgoType.Dynamic: vm = dynamic(key, name, name, editing_permissions); break;
case AlgoType.Splitter: vm = splitter(key,name,editing_permissions); break;
case AlgoType.None: vm = new DefaultAlgoVM(); break;
default: throw new NotImplementedException();
default: vm = generic(key, name, algo_type, editing_permissions); break;
}
vm.is_expanded = is_expanded && vm.can_expand;
return vm;
@@ -950,6 +959,8 @@ namespace mbts.spark {
public string id { get; }
public string display_name { get; }
public mbts.ui.spark .AlgoContentTemplateSelector.OrderKind order_kind =>
new mbts.ui.spark.AlgoContentTemplateSelector.OrderKind(display_name, string.Empty);
public bool is_enabled { get; set; } = true;
public bool initialized { get; private set; }
@@ -0,0 +1,24 @@
using bts.api.orderman;
using mbts.ui.shell;
namespace mbts.spark
{
static partial class OrderTypeVMFactory {
class SplitterAlgoVM : OrderTypeVMBase<SplitterOrderDefinition, SplitterOrderDefinitionImmutable>
{
readonly Property<uint> _clip, _exposed;
public SplitterAlgoVM(AlgoKey id, string display_name, AlgoEditingPermissions editing_permissions)
: base(id, display_name, editing_permissions)
{
_clip = add_property<uint>(nameof(max_order_qty), SplitterOrderDefinition.MaxOrderQtyFieldNumber);
_exposed = add_property<uint>(nameof(exposed), SplitterOrderDefinition.ExposedQtyFieldNumber);
}
public uint max_order_qty { get => _clip.Value; set => _clip.set(value); }
public uint exposed { get => _exposed.Value; set => _exposed.set(value); }
}
}
}
@@ -5,13 +5,16 @@ using TopDefi = bts.api.orderman.TopOrderDefinitionImmutable;
namespace mbts.spark {
static partial class OrderTypeVMFactory {
class TopOrderVM : OrderTypeVMBase<TopDef, TopDefi> {
readonly Property<uint?> _ticks_off;
readonly Property<QuantityMeasureImmutable?> _top_threshold;
public TopOrderVM(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions)
: base(key, display_name, editing_permissions) {
_ticks_off = add_nullable_property<uint>(nameof(ticks_off), TopDef.TicksOffFieldNumber);
_top_threshold = add_property<QuantityMeasure, QuantityMeasureImmutable>(nameof(top_threshold), TopDef.TopThresholdFieldNumber, new QuantityMeasureImmutable(new QuantityMeasure { Ratio = 1.0, Absolute = 1 }));
}
public uint? ticks_off { get => _ticks_off.Value; set => _ticks_off.set(value); }
public QuantityMeasureImmutable? top_threshold { get => _top_threshold.Value; set => _top_threshold.set(value); }
}
}
@@ -820,6 +820,11 @@ namespace mbts.ui.shell {
void _try_auto_select() {
if (_is_user_selected_item) {
// if we have a user-selected item and the _desired_item for which we might want to set as the selected item
// isn't force_override, then no change
if (!_desired_item.force_override) {
return;
}
if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out TValue match)) {
_set_selected_item(match, SelectionReason.SettingsMandatedSelection);
return;
@@ -37,6 +37,51 @@ namespace mbts.spark.filters {
_predicates.Add(impl);
bool impl(TRow o) => max.CompareTo(selector(o)) >= 0;
}
}
// this is a copy of add_numeric except we do a tick_equals comparsion
// if the ticks are not equals, then it falls back to a regular double comparison
//
// if the SecurityImmutable? doesn't have a value
// then we fallback to double comparison
public void add_price(DoubleMinMaxState value, Func<TRow, double> selector, Func<TRow, SecurityImmutable?> si_selector) {
if (value == null)
return;
if (value.minimum.active) {
var min = value.minimum.value.Value;
if (value.maximum.active) {
var max = value.maximum.value.Value;
_predicates.Add(impl);
bool impl(TRow o) {
var v = selector(o);
var si = si_selector(o);
if (si.HasValue) {
return si.Value.ContractSpec.compare_tick_fallback_to_double(min, v) <= 0 &&
si.Value.ContractSpec.compare_tick_fallback_to_double(max, v) >= 0;
}
return min.CompareTo(v) <= 0 && max.CompareTo(v) >= 0;
}
} else {
_predicates.Add(impl);
bool impl(TRow o) {
var si = si_selector(o);
if (si.HasValue) {
return si.Value.ContractSpec.compare_tick_fallback_to_double(min, selector(o)) <= 0;
}
return min.CompareTo(selector(o)) <= 0;
}
}
} else if (value.maximum.active) {
var max = value.maximum.value.Value;
_predicates.Add(impl);
bool impl(TRow o) {
var si = si_selector(o);
if (si.HasValue) {
return si.Value.ContractSpec.compare_tick_fallback_to_double(max, selector(o)) >= 0;
}
return max.CompareTo(selector(o)) >= 0;
}
}
}
public void add_regex(string str, Func<TRow, string> selector) {
@@ -489,6 +489,8 @@
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
<RowDefinition Height="*"/>
@@ -503,9 +505,14 @@
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="If the order is at the top of book, the quantity remaining below which the order will be canceled."/>
</StackPanel>
<uag:QuantityMeasureControl x:Name="PART_TopThreshold_QuantityMeasureControl" Grid.Column="2" QuantityMeasure="{Binding top_threshold}"/>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<TextBlock>Ticks Off</TextBlock>
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="Maximum distance the order can be from the top of the book or 0 for any distance"/>
</StackPanel>
<c:TickUpDownUInt Grid.Row="2" Grid.Column="2" Value="{Binding ticks_off}" />
<uag:AlgoPickerNodeView
x:Name="PART_Child_AlgoPickerNodeView"
Grid.Row="2"
Grid.Row="4"
Grid.ColumnSpan="3"
ItemsSource="{Binding children_vm.child.algos_view}"
SelectedItem="{Binding children_vm.child.selected_algo}"
@@ -1007,6 +1014,37 @@
</Grid>
</DataTemplate>
</usp:AlgoContentTemplateSelector.SpreaderTemplate>
<usp:AlgoContentTemplateSelector.SplitterTemplate>
<DataTemplate>
<Grid x:Name="ContentRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Expose Quantity</TextBlock>
<c:TickUpDownUInt Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding exposed}"/>
<TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Max Order Quantity</TextBlock>
<c:TickUpDownUInt Grid.Row="1" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding max_order_qty}"/>
<uag:AlgoPickerNodeView
x:Name="PART_Child_AlgoPickerNodeView"
Grid.Row="3"
Grid.ColumnSpan="3"
ItemsSource="{Binding children_vm.child.algos_view}"
SelectedItem="{Binding children_vm.child.selected_algo}"
ContentTemplateSelector="{DynamicResource AlgoTemplateSelector}"
OrderKind="{x:Static usp:AlgoContentTemplateSelector.ChildKind}"
OrderKindTemplate="{StaticResource OrderKindTemplate}"
/>
</Grid>
</DataTemplate>
</usp:AlgoContentTemplateSelector.SplitterTemplate>
<usp:AlgoContentTemplateSelector.DynamicOrderTemplate>
<DataTemplate>
<Grid Grid.IsSharedSizeScope="True">
@@ -1133,6 +1171,30 @@
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.EnumTemplate>
<usp:DynamicOrderParamTemplateSelector.QuantityMeasureTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center"
Text="{Binding descriptor.ParamName, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
<uag:QuantityMeasureControl
Grid.Column="2" Focusable="False" HorizontalAlignment="Stretch"
QuantityMeasure="{Binding quantity, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
<uag:QuantityMeasureControl.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</uag:QuantityMeasureControl.CommandBindings>
<uag:QuantityMeasureControl.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</uag:QuantityMeasureControl.InputBindings>
</uag:QuantityMeasureControl>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.QuantityMeasureTemplate>
</usp:DynamicOrderParamTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
@@ -1153,5 +1215,175 @@
</Grid>
</DataTemplate>
</usp:AlgoContentTemplateSelector.DynamicOrderTemplate>
<usp:AlgoContentTemplateSelector.GenericOrderTemplate>
<DataTemplate>
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ItemsControl Grid.ColumnSpan="3" ItemsSource="{Binding parameters}" IsTabStop="False">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.Margin" Value="{StaticResource Margin.TP1}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplateSelector>
<usp:GenericOrderParamTemplateSelector>
<usp:GenericOrderParamTemplateSelector.BoolTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
<CheckBox
Grid.Column="2"
HorizontalAlignment="Stretch"
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
IsChecked="{Binding value, UpdateSourceTrigger=PropertyChanged}"
/>
</Grid>
</DataTemplate>
</usp:GenericOrderParamTemplateSelector.BoolTemplate>
<usp:GenericOrderParamTemplateSelector.DoubleTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
<c:TickUpDownDouble
Grid.Column="2"
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
/>
</Grid>
</DataTemplate>
</usp:GenericOrderParamTemplateSelector.DoubleTemplate>
<usp:GenericOrderParamTemplateSelector.IntTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
<c:TickUpDownInt
Grid.Column="2"
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
/>
</Grid>
</DataTemplate>
</usp:GenericOrderParamTemplateSelector.IntTemplate>
<usp:GenericOrderParamTemplateSelector.UIntTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
<c:TickUpDownUInt
Grid.Column="2"
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
/>
</Grid>
</DataTemplate>
</usp:GenericOrderParamTemplateSelector.UIntTemplate>
<usp:GenericOrderParamTemplateSelector.StringTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
<TextBox
Grid.Column="2"
HorizontalAlignment="Stretch"
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
Text="{Binding value, UpdateSourceTrigger=PropertyChanged}"
/>
</Grid>
</DataTemplate>
</usp:GenericOrderParamTemplateSelector.StringTemplate>
<usp:GenericOrderParamTemplateSelector.EnumTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
<ComboBox
Grid.Column="2"
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
ItemsSource="{Binding elements}"
SelectedItem="{Binding selected_element}"
/>
</Grid>
</DataTemplate>
</usp:GenericOrderParamTemplateSelector.EnumTemplate>
<usp:GenericOrderParamTemplateSelector.QuantityMeasureTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush3}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center"
Text="{Binding descriptor.Name, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
<uag:QuantityMeasureControl
Grid.Column="2" Focusable="False" HorizontalAlignment="Stretch"
QuantityMeasure="{Binding quantity, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
<uag:QuantityMeasureControl.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</uag:QuantityMeasureControl.CommandBindings>
<uag:QuantityMeasureControl.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</uag:QuantityMeasureControl.InputBindings>
</uag:QuantityMeasureControl>
</Grid>
</DataTemplate>
</usp:GenericOrderParamTemplateSelector.QuantityMeasureTemplate>
</usp:GenericOrderParamTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
<ItemsControl Grid.Row="2" Grid.ColumnSpan="3" ItemsSource="{Binding child_algo_selectors_view}" IsTabStop="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<uag:AlgoPickerNodeView
ItemsSource="{Binding algos_view}"
SelectedItem="{Binding selected_algo}"
CanEditStructure="{Binding DataContext.editing_permissions, Converter={StaticResource CanEditStructureConverter}, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
ContentTemplateSelector="{DynamicResource AlgoTemplateSelector}"
OrderKind="{Binding order_kind}"
OrderKindTemplate="{StaticResource OrderKindTemplate}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</usp:AlgoContentTemplateSelector.GenericOrderTemplate>
</usp:AlgoContentTemplateSelector>
</ResourceDictionary>
@@ -43,8 +43,10 @@ namespace mbts.ui.spark {
public DataTemplate GLBXIcebergOrderTemplate { get; set; }
public DataTemplate IcebergOrderTemplate { get; set; }
public DataTemplate SpreaderTemplate { get; set; }
public DataTemplate SplitterTemplate { get; set; }
public DataTemplate DynamicOrderTemplate { get; set; }
public DataTemplate DefaultOrderTemplate { get; set; }
public DataTemplate GenericOrderTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
if (item is IDynamicAlgoVMImpl) {
@@ -66,7 +68,9 @@ namespace mbts.ui.spark {
case CustomOrderDefinition.CustomOneofCase.GlbxIceberg: return GLBXIcebergOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Iceberg: return IcebergOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Spreader: return SpreaderTemplate;
case CustomOrderDefinition.CustomOneofCase.Splitter: return SplitterTemplate;
case CustomOrderDefinition.CustomOneofCase.Dynamic: return DynamicOrderTemplate;
default: return GenericOrderTemplate;
}
}
return null;
@@ -28,16 +28,27 @@ namespace mbts.ui.shell {
model.Dispose();
}
[Export("username")]
[Export(Exports.UserName)]
public string UserName { get; private set; }
[Export("userid")]
[Export(Exports.UserID)]
public uint UserId { get; private set; }
[Export]
public UserTuple UserTuple => new UserTuple(UserId, UserName);
[Export]
public AuthTokenImmutable Token { get; private set; }
[Export("workspace")]
public string Workspace { get; private set; }
public static class Exports {
public const string
UserName = "username",
UserID = "userid",
UserTuple = "usertuple";
}
}
public struct User {
@@ -49,8 +60,7 @@ namespace mbts.ui.shell {
public readonly UInt32 id;
}
public class LoginModel : IDisposable
{
public class LoginModel : IDisposable {
// Singleton to help bootstrap the system
private static LoginModel _instance = new LoginModel();
public static LoginModel instance { get { return _instance; } }
@@ -70,6 +80,11 @@ namespace mbts.ui.shell {
public bool HasFatalError { get; set; }
public User[] Users { get; private set; }
public bool try_get_user(string name, out User user) =>
Users.TryGetFirst(u => u.name == name, out user);
public bool user_exists(string name) =>
Users.Any(u => u.name == name);
// The final username
public (string, uint, AuthTokenImmutable) get_user() {
+257 -128
View File
@@ -1,136 +1,265 @@
<Window 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"
x:Class="mbts.ui.shell.LoginView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ui="clr-namespace:mbts.ui"
xmlns:uicore="clr-namespace:mbts.ui"
xmlns:converters="clr-namespace:mbts.ui.converters"
mc:Ignorable="d" MouseDown="Window_MouseDown"
Width="500" Height="309" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"
ui:StateManager.VisualState="{Binding VisualState}">
<Window.Resources>
<ResourceDictionary>
<converters:BooleanToVisibilityConverter x:Key="InverseBoolToVisibility" VisibilityForFalse="Visible" VisibilityForTrue="Collapsed" />
<converters:StringToBrushConverter x:Key="ErrorStringToBrush" />
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" VisibilityForNull="Hidden" />
</ResourceDictionary>
</Window.Resources>
<Window
x:Class="mbts.ui.shell.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:mbts.ui"
xmlns:uicore="clr-namespace:mbts.ui"
xmlns:shell="clr-namespace:mbts.ui.shell"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:converters="clr-namespace:mbts.ui.converters"
xmlns:autocomplete="clr-namespace:WPFTextBoxAutoComplete;assembly=WPFTextBoxAutoComplete"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
x:Name="_login_view"
Width="460"
Height="254"
WindowStyle="None"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
MouseDown="Window_MouseDown"
>
<ui:StateManager.Groups>
<ui:StateGroupsManager>
<ui:StateDefinitionCollection>
<ui:StateDefinition State="{Binding DataContext.login_status, Source={ui:DirectReference _login_view}, Converter={x:Static shell:LoginView.LoginStatusVisualStateConverter}}"/>
<ui:StateDefinition State="{Binding DataContext.SelectedWorkspace, Source={ui:DirectReference _login_view}, Converter={x:Static shell:LoginView.WorkspaceVisualStateConverter}}"/>
</ui:StateDefinitionCollection>
</ui:StateGroupsManager>
</ui:StateManager.Groups>
<Window.Resources>
<ResourceDictionary>
<sys:Double x:Key="LoginControlPaddingLeft">40</sys:Double>
<sys:Double x:Key="LoginControlPaddingTop">10</sys:Double>
<sys:Double x:Key="LoginControlPaddingRight">22</sys:Double>
<converters:BooleanToVisibilityConverter x:Key="InverseBoolToVisibility" VisibilityForFalse="Visible" VisibilityForTrue="Collapsed"/>
<converters:StringToBrushConverter x:Key="ErrorStringToBrush"/>
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" VisibilityForNull="Hidden"/>
</ResourceDictionary>
</Window.Resources>
<Window.Background>
<StaticResource ResourceKey="CommonBackground"/>
</Window.Background>
<Window.Background>
<StaticResource ResourceKey="CommonBackground"/>
</Window.Background>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="LoginState">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" />
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="IsHitTestVisible">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ErrorState">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="_error_control" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" />
</Storyboard>
</VisualState>
<VisualState x:Name="Loading">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetName="_loading_control" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" BeginTime="0:0:0.5" />
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="login_button" Storyboard.TargetProperty="IsEnabled" >
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="False" />
</BooleanAnimationUsingKeyFrames>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="cancel_button" Storyboard.TargetProperty="IsEnabled" >
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="LoginError">
<VisualState x:Name="BadPassword">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_password" Storyboard.TargetProperty="BorderBrush" >
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Brushes.Red}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="{x:Static GroupName, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
<VisualState Name="{x:Static Initializing, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}"/>
<VisualState Name="{x:Static Ready, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5"/>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="IsHitTestVisible">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState Name="{x:Static Error, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_error_control" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="_error_control" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5"/>
</Storyboard>
</VisualState>
<VisualState Name="{x:Static Loading, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_loading_control" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="_loading_control" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.5" BeginTime="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="_login_button" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetName="_cancel_button" Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.5"/>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_dialog_skirt" Storyboard.TargetProperty="IsHitTestVisible">
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
</BooleanAnimationUsingKeyFrames>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_dialog_skirt" Storyboard.TargetProperty="IsHitTestVisible">
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState Name="{x:Static Rejected, MemberType={ui:Nested shell:LoginView.VisualStateNames.LoginStatus}}">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_login_controls" Storyboard.TargetProperty="IsHitTestVisible">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
</BooleanAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_username" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Brushes.Red}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_password" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Brushes.Red}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="_reject_message" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup Name="{x:Static GroupName, MemberType={ui:Nested shell:LoginView.VisualStateNames.Workspace}}">
<VisualState Name="{x:Static Active, MemberType={ui:Nested shell:LoginView.VisualStateNames.Workspace}}"/>
<VisualState Name="{x:Static Inactive, MemberType={ui:Nested shell:LoginView.VisualStateNames.Workspace}}">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_resume_workspace_checkbox" Storyboard.TargetProperty="(ToggleButton.IsChecked)">
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
</BooleanAnimationUsingKeyFrames>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_resume_workspace_checkbox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderBrush="{x:Static uicore:BTSColors.form_border_brush}" BorderThickness="1">
<Grid ClipToBounds="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border BorderBrush="{x:Static uicore:BTSColors.form_border_brush}" BorderThickness="1">
<Grid ClipToBounds="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Login details -->
<Grid Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="_login_controls" Opacity="0" IsHitTestVisible="False">
<Grid.RowDefinitions>
<RowDefinition /> <!-- User -->
<RowDefinition /> <!-- Workspace -->
<RowDefinition /> <!-- Pass -->
<RowDefinition Height="Auto" /> <!-- message -->
<RowDefinition /><!-- buttons -->
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- Login details -->
<Grid
x:Name="_login_controls"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Opacity="0"
IsHitTestVisible="False"
>
<Grid.Margin>
<Thickness Left="{StaticResource LoginControlPaddingLeft}" Top="30" Right="{StaticResource LoginControlPaddingRight}"/>
</Grid.Margin>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="{Binding Source={StaticResource LoginControlPaddingTop}}"/>
<!-- User -->
<RowDefinition/>
<RowDefinition Height="{Binding Source={StaticResource LoginControlPaddingTop}}"/>
<!-- Workspace -->
<RowDefinition/>
<RowDefinition Height="{Binding Source={StaticResource LoginControlPaddingTop}}"/>
<!-- Pass -->
<RowDefinition/>
<RowDefinition Height="{Binding Source={StaticResource LoginControlPaddingTop}}"/>
<!-- message -->
<RowDefinition/>
<!-- buttons -->
</Grid.RowDefinitions>
<ComboBox ItemsSource="{Binding UserNames}" Grid.ColumnSpan="2" Margin="10" SelectedItem="{Binding Name, Mode=TwoWay}" Width="160" />
<ComboBox ItemsSource="{Binding Workspaces}" Grid.Row="1" Grid.ColumnSpan="2" Margin="10" SelectedItem="{Binding SelectedWorkspace, Mode=TwoWay}" Width="160" MaxWidth="160" />
<TextBox
x:Name="_username"
autocomplete:AutoCompleteBehavior.AutoCompleteItemsSource="{Binding KnownUsers}"
HorizontalAlignment="Stretch"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
>
</TextBox>
<!--Password-->
<PasswordBox
x:Name="_password"
Grid.Row="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Style="{DynamicResource PasswordBoxStyle1}"
PasswordChanged="_passwordChanged"
>
<PasswordBox.BorderBrush>
<LinearGradientBrush EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
<GradientStop Color="#ABADB3" Offset="0.05"/>
<GradientStop Color="#E2E3EA" Offset="0.07"/>
<GradientStop Color="#E3E9EF" Offset="1"/>
</LinearGradientBrush>
</PasswordBox.BorderBrush>
</PasswordBox>
<TextBlock
x:Name="_pw_mark"
Grid.Row="2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="4,0,0,0"
IsHitTestVisible="False"
Opacity="0.5"
Text="Password"
Visibility="{Binding IsFocused, Converter={StaticResource InverseBoolToVisibility}, ElementName=_password}"
/>
<PasswordBox Grid.Row="2" Margin="10" MinWidth="120" Grid.ColumnSpan="2" x:Name="_password" PasswordChanged="_passwordChanged" Style="{DynamicResource PasswordBoxStyle1}" Width="160">
<PasswordBox.BorderBrush>
<LinearGradientBrush EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
<GradientStop Color="#ABADB3" Offset="0.05"/>
<GradientStop Color="#E2E3EA" Offset="0.07"/>
<GradientStop Color="#E3E9EF" Offset="1"/>
</LinearGradientBrush>
</PasswordBox.BorderBrush>
</PasswordBox>
<TextBlock Text="Password" Grid.Row="2" Grid.ColumnSpan="2" Margin="15,10,10,10" Opacity="0.5"
IsHitTestVisible="False" HorizontalAlignment="Left" x:Name="_pw_mark"
Visibility="{Binding IsFocused, Converter={StaticResource InverseBoolToVisibility}, ElementName=_password}" />
<CheckBox x:Name="_resume_workspace_checkbox" Grid.Row="4" HorizontalAlignment="Left" IsChecked="{Binding resume_workspace}" IsEnabled="True" ToolTip="{Binding SelectedWorkspace}">
Load last workspace
</CheckBox>
<Button Content="Cancel" x:Name="cancel_button" Grid.Row="4" Grid.Column="0" MinWidth="60" Margin="10" IsCancel="True" Command="{Binding CancelCommand}" />
<Button Content="Login" x:Name="login_button" Grid.Row="4" Grid.Column="1" MinWidth="60" Margin="10" IsDefault="True" Command="{Binding LoginCommand}" />
<!-- Background="{StaticResource CommonBackground}"-->
<Border Grid.Row="3" Grid.ColumnSpan="2" Visibility="{Binding WarningMessage, Converter={StaticResource NullToVisibilityConverter}}">
<TextBlock Text="{Binding WarningMessage}" HorizontalAlignment="Center" Foreground="Firebrick" FontWeight="Bold" TextWrapping="Wrap" Margin="5" MaxWidth="160" />
</Border>
</Grid>
<!-- Fatal error display -->
<StackPanel Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="_error_control" Margin="10" Opacity="0" Visibility="{Binding ErrorMessage, Converter={StaticResource NullToVisibilityConverter}}">
<TextBlock Text="The application is currently unavailable." FontWeight="Bold" TextWrapping="Wrap" Margin="0,0,0,10" />
<TextBlock TextWrapping="Wrap">
<Run>Please call BTS Support at </Run>
<Run FontWeight="Bold">(919) 913-0860</Run>
<Run>or email</Run>
<Hyperlink CommandParameter="mailto:support@bluetradingsystems.com" Command="{Binding EmailCommand}">support@bluetradingsystems.com</Hyperlink>
</TextBlock>
<TextBlock Text="Error Details:" Margin="0,10,0,0" />
<TextBlock Text="{Binding ErrorMessage}" TextWrapping="Wrap" Margin="5" />
</StackPanel>
<!-- Loading -->
<StackPanel Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="_loading_control" IsHitTestVisible="False" Opacity="0">
<ui:BusySpinner Width="64" Height="64" />
<TextBlock Text="Loading..." HorizontalAlignment="Center" />
</StackPanel>
<!-- logo -->
<Image Grid.ColumnSpan="2" HorizontalAlignment="Left" Source="/Images/Icons/BTS.png" Width="220" Stretch="Uniform" SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="HighQuality" x:Name="_logo" Margin="32,0,0,0" />
<Button Grid.ColumnSpan="2" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5" Command="{Binding CancelCommand}" Style="{StaticResource RoundCloseButton}" />
<TextBlock
x:Name="_reject_message"
Grid.Row="6"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding WarningMessage}"
Foreground="Firebrick"
FontWeight="Bold"
TextWrapping="Wrap"
Visibility="Hidden"
/>
</Grid>
</Border>
</Window>
<!-- Fatal error display -->
<StackPanel
x:Name="_error_control"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Visibility="Hidden"
Opacity="0"
>
<TextBlock Text="The application is currently unavailable." FontWeight="Bold" TextWrapping="Wrap" Margin="0,0,0,10" />
<TextBlock TextWrapping="Wrap">
<Run>Please call BTS Support at </Run>
<Run FontWeight="Bold">(919) 913-0860</Run>
<Run>or email</Run>
<Hyperlink CommandParameter="mailto:support@bluetradingsystems.com" Command="{Binding EmailCommand}">support@bluetradingsystems.com</Hyperlink>
</TextBlock>
<TextBlock Text="Error Details:" Margin="0,10,0,0" />
<TextBlock Text="{Binding ErrorMessage}" TextWrapping="Wrap" Margin="5" />
</StackPanel>
<!-- Loading -->
<StackPanel
x:Name="_loading_control"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsHitTestVisible="False"
Opacity="0"
Visibility="Hidden"
>
<ui:BusySpinner Width="64" Height="64"/>
<TextBlock Text="Loading..." HorizontalAlignment="Center"/>
</StackPanel>
<!-- logo -->
<Image
x:Name="_logo"
RenderOptions.BitmapScalingMode="HighQuality"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="32,0,0,0"
Width="220"
Source="/Images/Icons/BTS.png"
Stretch="Uniform"
SnapsToDevicePixels="True"
/>
<Button Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5" Command="{Binding CancelCommand}" Style="{StaticResource RoundCloseButton}"/>
<c:DialogSkirt x:Name="_dialog_skirt" Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<c:DialogSkirt.RightItems>
<x:Array Type="{x:Type Button}">
<Button x:Name="_login_button" Width="60" Margin="10" IsDefault="True" IsHitTestVisible="True" Command="{Binding LoginCommand}">
Login
</Button>
<Button x:Name="_cancel_button" Width="60" Margin="10" IsCancel="True" IsHitTestVisible="True" Command="{Binding CancelCommand}">
Cancel
</Button>
</x:Array>
</c:DialogSkirt.RightItems>
</c:DialogSkirt>
</Grid>
</Border>
</Window>
@@ -1,15 +1,43 @@
using System;
using bts;
using mbts.ui.converters;
using mbts.ui.native;
using mbts.utils;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using mbts.ui.native;
using bts;
namespace mbts.ui.shell {
public partial class LoginView : Window
{
public partial class LoginView : Window {
public enum LoginStatus {
Initializing,
Ready,
Error,
Rejected,
LoggingIn
}
public static IValueConverter WorkspaceVisualStateConverter =>
ValueConverter.create<string, string>(workspace =>
!string.IsNullOrEmpty(workspace) && workspace != LoginViewModel.NEW_WORKSPACE_NAME ?
VisualStateNames.Workspace.Active :
VisualStateNames.Workspace.Inactive);
public static IValueConverter LoginStatusVisualStateConverter =>
ValueConverter.create<LoginStatus, string>(status => {
switch (status) {
case LoginStatus.Initializing: return VisualStateNames.LoginStatus.Initializing;
case LoginStatus.Ready: return VisualStateNames.LoginStatus.Ready;
case LoginStatus.LoggingIn: return VisualStateNames.LoginStatus.Loading;
case LoginStatus.Rejected: return VisualStateNames.LoginStatus.Rejected;
case LoginStatus.Error: return VisualStateNames.LoginStatus.Error;
default: throw ExceptionBuilder.Argument.unexpected_case(status, nameof(status));
}
});
public LoginView(LoginViewModel model) {
this.DataContext = model;
if (model is IRequestViewClose) {
@@ -65,6 +93,24 @@ namespace mbts.ui.shell {
return IntPtr.Zero;
}
}
public static class VisualStateNames {
public static class LoginStatus {
public const string GroupName = nameof(LoginStatus);
public static readonly string
Initializing = $"{GroupName}{nameof(Initializing)}",
Ready = $"{GroupName}{nameof(Ready)}",
Loading = $"{GroupName}{nameof(Loading)}",
Rejected = $"{GroupName}{nameof(Rejected)}",
Error = $"{GroupName}{nameof(Error)}";
}
}
public static class Workspace {
public const string GroupName = nameof(Workspace);
public static readonly string
Active = $"{GroupName}{VisualStates.Active}",
Inactive = $"{GroupName}{VisualStates.Inactive}";
}
}
}
}
@@ -2,23 +2,22 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using LoginStatus = mbts.ui.shell.LoginView.LoginStatus;
namespace mbts.ui.shell
{
namespace mbts.ui.shell {
public class LoginViewModel : ViewModelBase, IRequestViewClose
{
public const string NEW_WORKSPACE_NAME = "New workspace...";
private LoginModel _model;
private Dispatcher _dispatcher;
public LoginViewModel(LoginModel model, Dispatcher disp) {
_dispatcher = disp;
_model = model;
Workspaces = new ObservableCollection<string>();
LoginCommand = new DelegateCommand(OnLogin, _ => UserNames.Count > 0 && SelectedWorkspace != null);
LoginCommand = new DelegateCommand(OnLogin, _ => !string.IsNullOrEmpty(_name) && SelectedWorkspace != null);
CancelCommand = new DelegateCommand(OnCancel);
EmailCommand = new DelegateCommand(OnSendMail);
if (_model.HasFatalError) {
@@ -42,23 +41,29 @@ namespace mbts.ui.shell
void _model_Ready() {
_dispatcher.BeginInvoke(() => {
if (!_model.HasFatalError) {
this.UserNames.AddRange(_model.Users.Select(u => u.name));
this.VisualState = "LoginState";
login_status = LoginStatus.Ready;
load_username();
if (!string.IsNullOrEmpty(_name))
KnownUsers.Add(_name);
LoginCommand.RaiseCanExecuteChanged();
}
else {
ErrorMessage = _model.ErrorMessage;
VisualState = "ErrorState";
login_status = LoginStatus.Error;
}
});
}
private string _vstate = "Normal";
public string VisualState { get { return _vstate; } set { _vstate = value; RaisePropertyChanged("VisualState"); } }
LoginStatus _login_status;
public LoginStatus login_status { get => _login_status; private set => Set(ref _login_status, value); }
private ObservableCollection<string> _user_names = new ObservableCollection<string>();
public ObservableCollection<string> UserNames { get { return _user_names; } }
/// <see cref="KnownUsers"/> should only contain the user-name of the last login (if none exists, it should be empty).
public ObservableCollection<string> KnownUsers { get; } = new ObservableCollection<string>();
bool _resume_workspace = true;
public bool resume_workspace {
get => _resume_workspace;
set => Set(ref _resume_workspace, value);
}
private string _name;
public string Name {
get { return _name; }
@@ -81,13 +86,14 @@ namespace mbts.ui.shell
public string SelectedWorkspace { get; set; }
private async void update_workspaces(string name) {
Workspaces.Clear();
Workspaces.Add("New workspace...");
var user = _model.Users.First(u => u.name == name);
var to_add = await _model.workspaces_for(user);
this.SelectedWorkspace = Workspaces.First();
if (to_add != null && name == _name && Workspaces.Count == 1) {
Workspaces.AddRange(to_add);
load_last_workspace(name);
Workspaces.Add(NEW_WORKSPACE_NAME);
SelectedWorkspace = Workspaces.First();
if (_model.try_get_user(name, out var user)) {
var to_add = await _model.workspaces_for(user);
if (to_add != null && name == _name && Workspaces.Count == 1) {
Workspaces.AddRange(to_add);
load_last_workspace(name);
}
}
RaisePropertyChanged("SelectedWorkspace");
LoginCommand.RaiseCanExecuteChanged();
@@ -111,7 +117,7 @@ namespace mbts.ui.shell
var value = key.GetValue("login");
if (value != null) {
var n = value.ToString();
if (UserNames.Contains(n)) {
if (_model.user_exists(n)) {
Name = n;
}
}
@@ -134,15 +140,13 @@ namespace mbts.ui.shell
private async void OnLogin(object arg) {
save_selection();
string selected_workspace = SelectedWorkspace;
if (selected_workspace == "New workspace...") {
selected_workspace = null;
}
string selected_workspace = _resume_workspace && SelectedWorkspace != NEW_WORKSPACE_NAME ?
SelectedWorkspace : null;
var result = await _model.do_login(Name, selected_workspace, Password);
if (result.success) { VisualState = "Loading"; }
if (result.success) { login_status = LoginStatus.LoggingIn; }
if (!result.success && !string.IsNullOrEmpty(result.reason)) {
WarningMessage = result.reason;
VisualState = "BadPassword";
login_status = LoginStatus.Rejected;
}
else if (!string.IsNullOrEmpty(result.reason)) {
// Login was successful but we have a message for the user
@@ -1,14 +1,14 @@
using System;
using bts;
using bts.api;
using bts.spark;
using mbts.ui.native;
using mbts.ui.utility;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using mbts.ui.native;
using mbts.ui.utility;
using bts.api;
using bts.spark;
using bts;
namespace mbts.ui.shell {
[Export(typeof(ILauncher))]
@@ -23,7 +23,7 @@ namespace mbts.ui.shell {
[ImportingConstructor]
public LauncherVM(
ObservableCollection<IShellModule> modules,
[Import("username")]string username,
[Import(LoginContainer.Exports.UserName)]string username,
ErrorReporter errors, IBus bus) {
_user = username;
_modules = new ObservableAutoWrapper<ShellModuleVM, IShellModule>(modules, m => new ShellModuleVM(m, bus));
@@ -45,12 +45,7 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Margin="4" Text="{Binding SaveName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" />
<CheckBox Margin="4,0,4,0" Grid.Row="1" IsChecked="{Binding IsPublicWorkspace}" HorizontalAlignment="Left">Shared</CheckBox>
<Button Grid.Column="1" HorizontalAlignment="Right" Margin="4" Command="{Binding SaveWorkspace}" IsDefault="True" MinWidth="60">Save</Button>
</Grid>
</Grid>
@@ -128,6 +128,9 @@ namespace mbts.spark {
public static IEqualityComparer<double> tick_equality_comparer(this ContractSpecificationImmutable contract) =>
EqualityComparer.Create((double a, double b) => tick_equals(contract, a, b), x => MathUtils.RoundFractionalUnits(x, contract.LowTick));
public static int compare_tick_fallback_to_double(this ContractSpecificationImmutable contract, double x, double y) =>
tick_equals(contract, x, y) ? 0 : x.CompareTo(y);
public static double round_to_tick(this ContractSpecificationImmutable spec, double value) {
double intpart = (long)value;
double floatpart = value % 1;
@@ -253,15 +253,18 @@ namespace mbts.ui {
sd.StateChanged += on_state_changed;
return Disposable.Create(() => sd.StateChanged -= on_state_changed);
void on_state_changed(string oldval, string newval) => transition(newval);
async void transition(string state) {
void transition(string state) {
version++;
if (target.IsLoaded)
StateManager.GoToState(target, state);
else {
int v = version;
await target.LoadedAsync();
if (v == version)
StateManager.GoToState(target, state);
target.Loaded += on_loaded;
void on_loaded(object sender, RoutedEventArgs e) {
target.Loaded -= on_loaded;
if (v == version)
StateManager.GoToState(target, state);
}
}
}
});
+1 -2
View File
@@ -253,14 +253,13 @@ namespace mbts.co2 {
}
}
[StructLayout(LayoutKind.Sequential, Size = 712)]
[StructLayout(LayoutKind.Sequential, Size = 704)]
internal struct ColdEntry {
public int exchange_status;
// Last trade
public AtomicPriceSize last_trade;
public ulong total_volume;
public utils.LLDateTime last_trade_time;
private AggressorSide last_side;
//// Settlement
public UInt64 misc_timestamp;
@@ -3,4 +3,5 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("spark_core")]
[assembly: InternalsVisibleTo("sparkcmd")]
[assembly: InternalsVisibleTo("spark_test")]
[assembly: InternalsVisibleTo("spark_excel")]
+19 -19
View File
@@ -1,36 +1,33 @@
using bts.api;
using bts.api.synthetic;
using mbts;
using mbts.co2;
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Linq;
using SecurityKey = System.UInt32;
using System.Collections.Concurrent;
using System.Threading;
using bts.api.user;
using System.Collections.Generic;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using SecurityKey = System.UInt32;
namespace bts.md
{
namespace bts.md {
[Flags]
enum MDSourceOptions {
DeferInit = 1,
}
class MDSource : IObserver<List<SecurityAndHandle>>, IDisposable
{
{
const uint _no_user_id = 0;
protected readonly MDConsumer _consumer = new MDConsumer();
protected readonly Co2RelayControl _relay;
protected DateTime _version = DateTime.MinValue;
protected readonly ConcurrentDictionary<SecurityKey, SecurityMD> _exchange_sec_map = new ConcurrentDictionary<SecurityKey, SecurityMD>();
protected List<IDisposable> _dispose = new List<IDisposable>();
protected readonly MDSourceOptions _options;
readonly uint _user_id;
public MDSource(uint naggle_ms, MDSourceOptions options = 0)
public MDSource(uint naggle_ms, MDSourceOptions options = 0, uint user_id = _no_user_id)
{
_options = options;
_user_id = user_id;
_relay = new Co2RelayControl(_consumer, naggle_ms);
_relay.preload_securities((co2.MDRequest.Types.Flags)0xff).ignore();
_dispose.Add(_consumer.on_security.Subscribe(this));
@@ -54,12 +51,15 @@ namespace bts.md
public void OnCompleted() { }
public void OnError(Exception error) => Log.Md.error(error.ToString());
public void OnNext(List<SecurityAndHandle> values)
{
if (values.Count > 0)
{
foreach (var v in values)
{
public void OnNext(List<SecurityAndHandle> values) {
if (values.Count > 0) {
foreach (var v in values) {
if (_user_id != _no_user_id &&
v.security.ContractCase == Security.ContractOneofCase.Synthetic &&
v.security.Synthetic.UserId != _user_id &&
v.security.Synthetic.UserId != _no_user_id) {
continue;
}
var smd = new SecurityMD(new SecurityImmutable(v.security), v.handle);
_exchange_sec_map.AddOrUpdate(smd.security.Id, smd, (_, __) => smd);
}
@@ -93,4 +93,4 @@ namespace bts.md
}
}
}
}
+3 -1
View File
@@ -223,7 +223,7 @@ namespace bts.md
{
if (!timeout.HasValue)
{
timeout = TimeSpan.FromMilliseconds(10000);
timeout = TimeSpan.FromHours(24);
}
CancellationTokenSource src = new CancellationTokenSource(timeout.Value);
SecurityImmutable? security = await Task.Run<SecurityImmutable?>(async () =>
@@ -234,6 +234,8 @@ namespace bts.md
sec = find_security(filter);
if (sec == null)
await Task.Delay(500);
if(src.IsCancellationRequested)
break;
}
while (sec == null);
return sec;
Binary file not shown.
+300
View File
@@ -0,0 +1,300 @@
using System;
using System.Diagnostics;
using System.Text;
using bts.excel.extensions;
namespace bts.excel
{
/// <summary>
/// Defines a reference to some Cell instance, whether it exists or not.
/// </summary>
/// <remarks>
/// Static methods are provided for conversion of column names etc.
/// </remarks>
public sealed class CellRef : IComparable, IComparable<CellRef>
{
public CellRef(ExcelDna.Integration.ExcelReference rref) : this(rref.GetSheetName(),
(rref.ColumnFirst + 1, rref.RowFirst + 1) )
{
}
/// <summary>
/// Constructor for the class: CellRef
/// </summary>
/// <param name="sheetName">Name of the sheet where the cell belongs.</param>
/// <param name="coordinate">Coordinate (column, row) for the cell in the sheet.</param>
public CellRef(string sheetName, (int col1, int row1) coordinate)
{
SheetName = sheetName;
SimpleSheetName = simple_sheet_name(SheetName);
Coordinate = coordinate;
LocalAddress = ConvertCoordToAddress(coordinate);
}
static string simple_sheet_name(string sn)
{
int open = sn.IndexOf('[');
int close = sn.IndexOf(']', open + 1);
var ssn = sn.Substring(0, open) + sn.Substring(close + 1);
ssn = stringize_workbook(ssn);
return ssn;
}
public static string stringize_workbook(string ssn)
{
ssn = ssn.Replace("[", "").Replace("]", "_").Replace(".", "_");
return ssn;
}
public string named_range { get; set;}
/// <summary>
/// Maximum number of rows, to e compatible with Excel.
/// </summary>
private const int MaxRows = 1048576;
/// <summary>
/// Maximum number of columns, to be compatible with Excel.
/// </summary>
private const int MaxColumns = 16384;
// NOTE: Worksheet and workbook specifications and limits
// http://office.microsoft.com/en-gb/excel-help/excel-specifications-and-limits-HP010342495.aspx
//
// Worksheet size: 1,048,576 rows by 16,384 columns.
// * 1048575 is the highest number representable in 20 bits.
// * 16383 is the highest number representable in 14 bits. That leaves 2 spare bits in a 16-bit word.
// Column width: 255 characters (I'm not sure what the real significance of this is).
// Total number of characters that a cell can contain: 32,767 characters
// * 32766 is the highest number representable in 15 bits. That leaves 1 spare bit in a 16-bit word.
//
/// <summary>
/// Name of the sheet where the cell belongs.
/// </summary>
/// <remarks>
/// All cells belong to some Sheet instance, so this name must never be null or empty.
/// </remarks>
public string SheetName { get; }
public string SimpleSheetName { get; }
/// <summary>
/// Coordinate (column, row) for the cell in the sheet.
/// </summary>
public (int col1, int row1) Coordinate { get; }
public CellRef Offset(int columnOffset, int rowOffset)
{
return new CellRef(SheetName, (Column + columnOffset, Row + rowOffset));
}
public int Column => Coordinate.col1;
public int Row => Coordinate.row1;
public string LocalAddress { get; internal set; }
#region Address conversion
/// <summary>
/// Converts a column identifier, using letters, to a column number.
/// </summary>
/// <param name="columnName">Column identifier using letters.</param>
/// <returns>Column number.</returns>
/// <exception cref="ArgumentOutOfRangeException">When column number exceeds maximum.</exception>
public static int ConvertToColumnNumber(string columnName)
{
var number = 0;
var pow = 1;
for (var i = columnName.Length - 1; i >= 0; i--)
{
number += (columnName[i] - 'A' + 1) * pow;
pow *= 26;
}
if (number > MaxColumns)
{
throw new ArgumentOutOfRangeException(nameof(columnName),
"Column number from columnName exceeds maximum");
}
return number;
}
/// <summary>
/// Converts a column number to a column identifier using letters.
/// </summary>
/// <param name="columnNumber">Column number.</param>
/// <returns>Column identifier using letters.</returns>
/// <exception cref="ArgumentOutOfRangeException">When column number is invalid.</exception>
public static string ConvertToColumnName(int columnNumber)
{
if (columnNumber < 1 || columnNumber > MaxColumns)
{
throw new ArgumentOutOfRangeException(nameof(columnNumber),
"Invalid column number");
}
var dividend = columnNumber;
var columnName = string.Empty;
while (dividend > 0)
{
var modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo) + columnName;
dividend = (dividend - modulo) / 26;
}
return columnName;
}
/// <summary>
/// Converts an address, like "A1", to a cell coordinate.
/// </summary>
/// <param name="address">Cell address, like "A1".</param>
/// <returns>Coordinate of cell on sheet.</returns>
/// <exception cref="ArgumentNullException">When address argument is null.</exception>
/// <exception cref="ArgumentException">When address is invalid.</exception>
public static (int col1, int row1) ConvertAddressToCoord(string address)
{
if (address == null)
{
throw new ArgumentNullException(nameof(address));
}
address = address.Replace("$", string.Empty);
address = address.ToUpper();
var letters = new StringBuilder();
foreach (var c in address)
{
if (c < 'A' || c > 'Z')
{
break;
}
letters.Append(c);
}
if (letters.Length == 0 || letters.Length > 3)
{
throw new ArgumentException(
"Column name requires 1 to 3 letters",
nameof(address));
}
var numbers = address.Substring(letters.Length);
if (numbers.Length == 0)
{
throw new ArgumentException("Row number is required", nameof(address));
}
Debug.Assert(letters.Length + numbers.Length == address.Length);
var row1 = int.Parse(numbers);
var col1 = ConvertToColumnNumber(letters.ToString());
return (col1, row1);
}
/// <summary>
/// Convert cell coordinate to cell address, like "A1".
/// </summary>
/// <param name="coordinate">Coordinate of cell on sheet.</param>
/// <param name="absolute">Provide absolute address, like "$A$1".</param>
/// <returns>Cell address, like "A1", or optionally like "$A$1".</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static string ConvertCoordToAddress((int col1, int row1) coordinate, bool absolute = false)
{
if (coordinate.row1 < 1 || coordinate.row1 > MaxRows ||
coordinate.col1 < 1 || coordinate.col1 > MaxColumns)
{
throw new ArgumentOutOfRangeException(nameof(coordinate),
"Invalid coordinate");
}
var (col1, row1) = coordinate;
return absolute
? $"${ConvertToColumnName(col1)}${row1}"
: $"{ConvertToColumnName(col1)}{row1}";
}
#endregion
#region IComparable
public int CompareTo(CellRef other)
{
if (other == null) return -1;
return string.Compare(ToString(), other.ToString(),
StringComparison.Ordinal);
}
public int CompareTo(object obj)
{
if (obj.GetType() != GetType()) return -1;
return CompareTo((CellRef)obj);
}
#endregion
#region HashCode & Equals
/// <returns>Unique key</returns>
public override string ToString()
{
return to_name();
}
public string to_address()
{
if (!string.IsNullOrEmpty(SheetName))
return $"'{SheetName}'!{LocalAddress}";
else
return $"{LocalAddress}";
}
public string to_name()
{
if(!string.IsNullOrEmpty(named_range))
return named_range;
else
{
if (!string.IsNullOrEmpty(SheetName))
{
return $"{SimpleSheetName}_{LocalAddress}".ToLowerInvariant();
}
else
return $"{LocalAddress}".ToLowerInvariant();
}
}
public string r1_c1_include_sheet()
{
if (!string.IsNullOrEmpty(SheetName))
return $"'{SimpleSheetName}'!R{this.Row}C{this.Column}";
else
return $"R{this.Row}C{this.Column}";
}
public string r1_c1()
{
return $"R{this.Row}C{this.Column}";
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj != null && !(obj is CellRef))
throw new ArgumentException("Object must be of type CellRef.");
return GetHashCode().Equals(obj.GetHashCode());
}
#endregion
}
}
+127
View File
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using spark_excel.grammar;
using Antlr4.Runtime;
using ExcelDna.Integration;
using bts.excel.extensions;
namespace bts.excel
{
public interface IParameters
{
IReadOnlyDictionary<string, string> algo_params { get; }
void register_algo_parameter(string type, string name);
void unregister_algo_parameter(string name);
IReadOnlyDictionary<string, string> special_params { get; }
void register_special_parameter(string alias, string name);
void unregister_special_parameter(string name);
}
public interface IDefinitionVisitor
{
void add_def(string def);
void map_def(string def, string mapped);
List<string> get_defs();
IReadOnlyDictionary<string, string> order_setups {get;}
void register_order_setup(string setup, string attach);
void add_dual_definition(string code);
List<string> get_dual_definitions();
void add_dual_impl(string code);
List<string> get_dual_impls();
void register_algo_parameter(string type, string name);
}
public class CodeParts : IDefinitionVisitor, IParameters
{
List<string> _defs = new List<string>();
ConcurrentDictionary<string, string> _def_map = new ConcurrentDictionary<string, string>();
ConcurrentDictionary<string, string> _order_setups = new ConcurrentDictionary<string, string>();
List<string> _dual_defs = new List<string>();
List<string> _dual_blocks = new List<string>();
ConcurrentDictionary<string, string> _algo_params = new ConcurrentDictionary<string, string>();
ConcurrentDictionary<string, string> _special_params = new ConcurrentDictionary<string, string>();
public CodeParts()
{
}
public string function_name { get; set; }
public string function_impl { get; set; }
public void add_def(string def)
{
_defs.Add(def);
}
public void map_def(string def, string mapped)
{
_def_map[def] = mapped;
}
public List<string> get_defs()
{
var list = _defs.Distinct();
List<string> defs = new List<string>();
foreach (var d in list)
defs.Add(_def_map.TryGetValue(d, out string mapped) ? mapped : d);
return defs;
}
public IReadOnlyDictionary<string, string> order_setups => _order_setups;
public void register_order_setup(string setup, string attach)
{
_order_setups[attach] = setup;
}
public void add_dual_impl(string code)
{
_dual_blocks.Add(code);
}
public List<string> get_dual_impls()
{
return _dual_blocks;
}
public void add_dual_definition(string code)
{
_dual_defs.Add(code);
}
public List<string> get_dual_definitions()
{
return _dual_defs;
}
public IReadOnlyDictionary<string, string> algo_params => _algo_params;
public void register_algo_parameter(string type, string name)
{
_algo_params[name] = string.IsNullOrEmpty(type) ? "double" : type;
}
public void unregister_algo_parameter(string name)
{
_algo_params.TryRemove(name, out _);
}
public IReadOnlyDictionary<string, string> special_params => _special_params;
public void register_special_parameter(string alias, string name)
{
_special_params[alias] = name;
}
public void unregister_special_parameter(string name)
{
_special_params.TryRemove(name, out _);
}
}
}
@@ -0,0 +1,104 @@
using System;
namespace bts.excel
{
public class ErrorValue : IComparable, IComparable<ErrorValue>
{
private enum ErrorType
{
Null,
Div0,
Value,
Ref,
Name,
Num,
NA
}
public static ErrorValue Null => new ErrorValue(ErrorType.Null);
public static ErrorValue Div0 => new ErrorValue(ErrorType.Div0);
public static ErrorValue Value => new ErrorValue(ErrorType.Value);
public static ErrorValue Ref => new ErrorValue(ErrorType.Ref);
public static ErrorValue Name => new ErrorValue(ErrorType.Name);
public static ErrorValue Num => new ErrorValue(ErrorType.Num);
public static ErrorValue NA => new ErrorValue(ErrorType.NA);
private const string HashNull = "#NULL!";
private const string HashDiv0 = "#DIV/0!";
private const string HashValue = "#VALUE!";
private const string HashRef = "#REF!";
private const string HashName = "#NAME?";
private const string HashNum = "#NUM!";
private const string HashNA = "#N/A";
private readonly ErrorType _errorType;
public ErrorValue(string token)
: this(GetErrorType(token))
{ }
private ErrorValue(ErrorType errorType)
{
_errorType = errorType;
}
private static ErrorType GetErrorType(string token)
{
switch (token)
{
case HashNull: return ErrorType.Null;
case HashDiv0: return ErrorType.Div0;
case HashValue: return ErrorType.Value;
case HashRef: return ErrorType.Ref;
case HashName: return ErrorType.Name;
case HashNum: return ErrorType.Num;
case HashNA: return ErrorType.NA;
default: throw new Exception("Unrecongised error token");
}
}
public static bool IsErrorToken(string token)
{
switch (token)
{
case HashNull:
case HashDiv0:
case HashValue:
case HashRef:
case HashName:
case HashNum:
case HashNA:
return true;
default:
return false;
}
}
public override string ToString()
{
switch (_errorType)
{
case ErrorType.Null: return HashNull;
case ErrorType.Div0: return HashDiv0;
case ErrorType.Value: return HashValue;
case ErrorType.Ref: return HashRef;
case ErrorType.Name: return HashName;
case ErrorType.Num: return HashNum;
case ErrorType.NA: return HashNA;
default: throw new Exception("Unexpected condition");
}
}
public int CompareTo(ErrorValue other)
{
if (other == null) return -1;
return _errorType.CompareTo(other._errorType);
}
public int CompareTo(object obj)
{
if (obj.GetType() != GetType()) return -1;
return CompareTo(obj as ErrorValue);
}
}
}
@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using ExcelDna.Integration;
using Microsoft.Office.Interop.Excel;
namespace bts.excel
{
public static class RangeHelper
{
public static ExcelReference RangeToExcelReference(Range range)
{
return new ExcelReference(range.Row - 1,
range.Row - 1 + range.Rows.Count - 1,
range.Column - 1,
range.Column - 1 + range.Columns.Count - 1,
range.Worksheet.Name);
}
public static Range ReferenceToRange(ExcelReference xlref)
{
var app = ExcelDnaUtil.Application as Microsoft.Office.Interop.Excel.Application;
var refText = (string)XlCall.Excel(XlCall.xlfReftext, xlref, true);
var range = app.Range[refText];
return range;
}
}
// Select an ExcelReference (perhaps on another sheet) allowing changes to be made there.
// On clean-up, resets all the selections and the active sheet.
// Should not be used if the work you are going to do will switch sheets, amke new sheets etc.
public class ExcelSelectionHelper : XlCall, IDisposable
{
object oldSelectionOnActiveSheet;
object oldActiveCellOnActiveSheet;
object oldSelectionOnRefSheet;
object oldActiveCellOnRefSheet;
public ExcelSelectionHelper(ExcelReference refToSelect)
{
// Remember old selection state on the active sheet
oldSelectionOnActiveSheet = Excel(xlfSelection);
oldActiveCellOnActiveSheet = Excel(xlfActiveCell);
// Switch to the sheet we want to select
string refSheet = (string)Excel(xlSheetNm, refToSelect);
Excel(xlcWorkbookSelect, new object[] { refSheet });
// record selection and active cell on the sheet we want to select
oldSelectionOnRefSheet = Excel(xlfSelection);
oldActiveCellOnRefSheet = Excel(xlfActiveCell);
// make the selection
Excel(xlcFormulaGoto, refToSelect);
}
public void Dispose()
{
// Reset the selection on the target sheet
Excel(xlcSelect, oldSelectionOnRefSheet, oldActiveCellOnRefSheet);
// Reset the sheet originally selected
string oldActiveSheet = (string)Excel(xlSheetNm, oldSelectionOnActiveSheet);
Excel(xlcWorkbookSelect, new object[] { oldActiveSheet });
// Reset the selection in the active sheet (some bugs make this change sometimes too)
Excel(xlcSelect, oldSelectionOnActiveSheet, oldActiveCellOnActiveSheet);
}
}
public class ExcelEchoOffHelper : XlCall, IDisposable
{
object oldEcho;
public ExcelEchoOffHelper()
{
oldEcho = Excel(xlfGetWorkspace, 40);
Excel(xlcEcho, false);
}
public void Dispose()
{
Excel(xlcEcho, oldEcho);
}
}
public class ExcelCalculationManualHelper : XlCall, IDisposable
{
object oldCalculationMode;
public ExcelCalculationManualHelper()
{
oldCalculationMode = Excel(xlfGetDocument, 14);
Excel(xlcOptionsCalculation, 3);
}
public void Dispose()
{
Excel(xlcOptionsCalculation, oldCalculationMode);
}
}
}
@@ -0,0 +1,26 @@
using spark_excel.grammar;
namespace bts.excel
{
public interface IExpression
{
object evaluate();
}
internal class Expression<T> : IExpression
{
private readonly ExcelFormulaParser.ExprContext _context;
private readonly IExcelFormulaVisitor<T> _visitor;
public Expression(ExcelFormulaParser.ExprContext context, IExcelFormulaVisitor<T> visitor)
{
_context = context;
_visitor = visitor;
}
public object evaluate()
{
return _context.Accept(_visitor);
}
}
}
+286
View File
@@ -0,0 +1,286 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
using ExcelDna.Integration;
using spark_excel.grammar;
using Antlr4.Runtime;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel
{
internal class ExcelFormulaErrorListener : BaseErrorListener
{
public bool IsValid { get; private set; } = true;
public string offendingSymbol { get; private set;}
public int line { get; private set; }
public int charPositionInLine { get; private set; }
public string msg { get; private set; }
public RecognitionException exception { get; private set;}
public override void SyntaxError(IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
{
IsValid = false;
this.offendingSymbol = offendingSymbol.Text;
this.line = line;
this.charPositionInLine = charPositionInLine;
this.msg = msg;
this.exception = e;
}
}
public class Generator
{
readonly IParameters _globals = new CodeParts();
public IParameters globals => _globals;
public string bts_generate_algo_class(string algoname, IEnumerable<CodeParts> functions, string[] filepaths = null)
{
var ass = Assembly.GetExecutingAssembly();
var resources = ass.GetManifestResourceNames();
var res = $"{ass.GetName().Name}.ExcelAlgoTemplate.cpp";
using (Stream stream = ass.GetManifestResourceStream(res))
{
using (StreamReader reader = new StreamReader(stream))
{
string cpp = reader.ReadToEnd();
//cpp = cpp.Replace("#define __AlgoName__", "//#define __#AlgoName__");
cpp = cpp.Replace("#define __FunctionDefs__", "//#define __#FunctionDefs__");
cpp = cpp.Replace("#define __FunctionCalls__", "//#define __#FunctionCalls__");
cpp = cpp.Replace("#define __FunctionImpls__", "//#define __#FunctionImpls__");
cpp = cpp.Replace("#define __DualDefs__", "//#define __#DualDefs__");
cpp = cpp.Replace("#define __DualImpls__", "//#define __#DualImpls__");
cpp = cpp.Replace("#define __ParameterDefs__", "//#define __#ParameterDefs__");
cpp = cpp.Replace("#define __ParameterLaunches__", "//#define __#ParameterLaunches__");
cpp = cpp.Replace("#define __ParameterUpdates__", "//#define __#ParameterUpdates__");
cpp = cpp.Replace("#define __ParameterSetups__", "//#define __#ParameterSetups__");
cpp = cpp.Replace("#define __OrderSetups__", "//#define __#OrderSetups__");
cpp = cpp.Replace("#define __OrderAttaches__", "//#define __#OrderAttaches__");
cpp = cpp.Replace("__Remove__", string.Empty);
cpp = cpp.Replace("__AlgoName__", algoname);
string funcdefs = string.Join(Environment.NewLine, functions.Select(x => $" void {x.function_name}();"));
cpp = cpp.Replace("__FunctionDefs__", "//__FunctionDefs__" + Environment.NewLine + funcdefs);
string funccalls = string.Join(Environment.NewLine, functions.Select(x => " " + x.function_name + "();" ));
cpp = cpp.Replace("__FunctionCalls__", "//__FunctionCalls__" + Environment.NewLine + funccalls);
string funcimpls = string.Join(Environment.NewLine, functions.Select(x => x.function_impl.Replace("__AlgoName__", algoname) ));
cpp = cpp.Replace("__FunctionImpls__", "//__FunctionImpls__" + Environment.NewLine + funcimpls);
string dualdefs = string.Join(Environment.NewLine, functions.SelectMany(x => x.get_dual_definitions().Select( xx => xx.Replace("__AlgoName__::", "") ) ).Distinct());
cpp = cpp.Replace("__DualDefs__", "//__DualDefs__" + Environment.NewLine + dualdefs);
string dualimpls = string.Join(Environment.NewLine, functions.SelectMany(x => x.get_dual_impls().Select(xx => xx.Replace("__AlgoName__", algoname))).Distinct());
cpp = cpp.Replace("__DualImpls__", "//__DualImpls__" + Environment.NewLine + dualimpls);
//__ParameterDefs__
string globalparamdefs = string.Join(Environment.NewLine, globals.algo_params.Select( kv => " " + kv.Value + " " + kv.Key + ";"));
string localparamdefs = string.Join(Environment.NewLine, functions.SelectMany(x => x.algo_params).Select(kv => " " + kv.Value + " " + kv.Key + ";"));
cpp = cpp.Replace("__ParameterDefs__",
"//__ParameterDefs__" + Environment.NewLine + globalparamdefs + Environment.NewLine + localparamdefs);
//__ParameterLaunches__
{
List<string> launches = new List<string>();
foreach (var kv in globals.algo_params)
{
string launch = $@"
auto {kv.Key}_param = params.find(""{kv.Key}"");
if ( {kv.Key}_param == params.end() )
return OrderOpResult{{ ORDER_ERROR_INVALID_ORDER_DEFINITON,""{kv.Key} parameter must be defined""}};
me->{kv.Key} = {kv.Key}_param->second.{get_as(kv.Key, kv.Value)}();
";
launches.Add(launch);
}
string paramlaunch = string.Join(Environment.NewLine, launches);
cpp = cpp.Replace("__ParameterLaunches__", "//__ParameterLaunches__" + Environment.NewLine + paramlaunch);
}
//__ParameterUpdates__
{
List<string> updates = new List<string>();
foreach(var kv in globals.algo_params)
{
string update = $@"
auto {kv.Key}_param = params.find(""{kv.Key}"");
if ( {kv.Key}_param == params.end() )
{{
algo_services().terminate_error(""Can't find parameter {kv.Key} "");
return;
}}
me->{kv.Key} = {kv.Key}_param->second.{get_as(kv.Key, kv.Value)}();
";
updates.Add(update);
}
string paramupdates = string.Join(Environment.NewLine, updates);
cpp = cpp.Replace("__ParameterUpdates__", "//__ParameterUpdates__" + Environment.NewLine + paramupdates);
}
//__ParameterSetups__
{
List<string> setups = new List<string>();
setups.Add($@"
DynamicOrderDescriptor algo_desc;
algo_desc.set_dynamic_order_type(""{algoname}"");
");
foreach (var kv in globals.algo_params)
{
string setup = $@"
auto {kv.Key}_param = algo_desc.add_param_desc();
{kv.Key}_param->set_param_name(""{kv.Key}"");
{kv.Key}_param->mutable_param_type()->set_primitive({get_param_type_enum(kv.Key, kv.Value)});
";
setups.Add(setup);
}
setups.Add($@"
serv.register_dynamic_algo_order(algo_desc, []() {{ return std::make_unique<{algoname}>(); }});
");
string paramsetups = string.Join(Environment.NewLine, setups);
cpp = cpp.Replace("__ParameterSetups__", "//__ParameterSetups__" + Environment.NewLine + paramsetups);
}
//__OrderSetups__
{
var setups = functions.SelectMany(x => x.order_setups.Values.SelectMany(xx => xx.Split(new[]{'\r','\n'})));
string ordersetups = string.Join(Environment.NewLine, setups.Distinct());
cpp = cpp.Replace("__OrderSetups__", "//__OrderSetups__" + Environment.NewLine + ordersetups);
}
//__OrderAttaches__
{
var attach = functions.SelectMany(x => x.order_setups.Keys);
string orderattach = string.Join(Environment.NewLine, attach);
cpp = cpp.Replace("__OrderAttaches__", "//__OrderAttaches__" + Environment.NewLine + orderattach);
}
string cppname = string.Empty;
if(filepaths != null && filepaths.Length > 0)
try_write_file_fullpath(filepaths, cpp);
else
try_write_file(algoname + ".cpp", cpp);
return cppname;
}
}
}
static private string get_as(string paramname, string paramtype)
{
switch (paramtype.ToLower())
{
case "int":
return "as_int32";
case "uint":
return "as_uint32";
case "double":
return "as_double";
case "string":
return "as_string";
case "bool":
return "as_bool";
case "order":
case "orderdef":
return "as_order_def";
default:
throw new ArgumentException("unknown type " + paramname + " " + paramtype);
}
}
static private string get_param_type_enum(string paramname, string paramtype)
{
switch(paramtype.ToLower())
{
case "int":
return "DYNAMIC_ORDER_PRIMITIVE_INT";
case "uint":
return "DYNAMIC_ORDER_PRIMITIVE_UINT";
case "double":
return "DYNAMIC_ORDER_PRIMITIVE_DOUBLE";
case "string":
return "DYNAMIC_ORDER_PRIMITIVE_STRING";
case "bool":
return "DYNAMIC_ORDER_PRIMITIVE_BOOL";
case "order":
case "orderdef":
return "DYNAMIC_ORDER_PRIMITIVE_ORDER_DEF";
default:
throw new ArgumentException("unknown type " + paramname + " " + paramtype);
}
}
//[ExcelFunction(IsMacroType = true, IsThreadSafe = false)]
//we can't call this as a UDF or the array / selectspecial thing doesn't always work
public CodeParts bts_generate_function_code(ExcelReference rref, ref string function_name)
{
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, rref);
var antlrstream = new AntlrInputStream(formula.ToString());
var lexer = new ExcelFormulaLexer(antlrstream);
var tokens = new CommonTokenStream(lexer);
var parser = new ExcelFormulaParser(tokens);
var error_listener = new ExcelFormulaErrorListener();
//parser.RemoveErrorListeners();
parser.AddErrorListener(error_listener);
var parsetree = parser.main();
if (!error_listener.IsValid)
{
try_write_file(function_name, error_listener.offendingSymbol + error_listener.msg + error_listener.exception?.ToString());
throw new Exception(error_listener.offendingSymbol + error_listener.msg, error_listener.exception);
}
CodeParts defs = new CodeParts();
GeneratorVisitor visitor = new GeneratorVisitor(rref, defs, globals);
object val = visitor.VisitMain(parsetree);
if(string.IsNullOrEmpty(function_name))
function_name = new CellRef(rref).to_name();
string ret = string.Empty;
ret += Environment.NewLine;
ret += $"void __AlgoName__::{function_name}() {Environment.NewLine}{{{Environment.NewLine}";
defs.get_defs().ForEach(x => ret += " " + x + Environment.NewLine);
ret += " " + val.ToString() + " ;";
ret += $"{Environment.NewLine} }}";
defs.function_impl = ret;
defs.function_name = function_name;
try_write_file(function_name + ".function", ret);
return defs;
}
static string try_write_file(string filename, string text)
{
string fname = string.Empty;
try
{
fname = $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\{filename}";
using(System.IO.StreamWriter file = new System.IO.StreamWriter(fname))
{
file.WriteLine(text);
}
}
catch {}
return fname;
}
static void try_write_file_fullpath(string[] filename, string text)
{
try
{
foreach(string fname in filename)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(fname))
{
file.WriteLine(text);
}
}
}
catch { }
}
}
}
@@ -0,0 +1,366 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using spark_excel.grammar;
using Antlr4.Runtime;
using ExcelDna.Integration;
using bts.excel.extensions;
namespace bts.excel
{
public class GeneratorVisitor : ExcelFormulaBaseVisitor<object>
{
ExcelReference _orig_ref, _current_ref;
functions.lib.FunctionLibrary _library;
ExcelFormulaErrorListener _error_listener = new ExcelFormulaErrorListener();
IDefinitionVisitor _defs;
IParameters _params;
string _document_name;
public GeneratorVisitor(ExcelReference rref, IDefinitionVisitor defs, IParameters parameters, bool include_setups = true)
{
_current_ref = _orig_ref = rref;
_library = new functions.lib.FunctionLibrary(include_setups);
_defs = defs;
_params = parameters;
var cref = new CellRef(rref);
_document_name = (string)XlCall.Excel(XlCall.xlfGetDocument, 88, cref.SheetName);
}
#region Context visitors overrides
public override object VisitBoolean(ExcelFormulaParser.BooleanContext context)
{
var expr = context.BOOLEAN();
return expr.ToString().ToLower();
}
public override object VisitName(ExcelFormulaParser.NameContext context)
{
var name = context.GetText();
var lookup = name;
if(!lookup.Contains("!"))
lookup = "!" + lookup;
object value = XlCall.Excel(XlCall.xlfGetName, lookup);
if(value != null && !(value is ExcelError))
{
string[] par = value.ToString().Split(new[] {'=', 'R', 'C'}, StringSplitOptions.RemoveEmptyEntries);
int r = int.Parse(par[0]) - 1;
int c = int.Parse(par[1]) - 1;
var rref = new ExcelReference(r, r, c, c, _orig_ref.GetSheetName());
process_cell(rref, out _);
}
return name;
}
public override object VisitMain(ExcelFormulaParser.MainContext context)
{
return context.expr().Accept(this);
}
public override object VisitMulDiv(ExcelFormulaParser.MulDivContext context)
{
var expr = context.expr();
var arg1 = expr[0].Accept(this);
if (arg1 is ErrorValue)
return arg1;
var arg2 = expr[1].Accept(this);
if (arg2 is ErrorValue)
return arg2;
string code = $"{arg1} {context.op.Text} {arg2}";
return code;
}
public override object VisitAddSub(ExcelFormulaParser.AddSubContext context)
{
var expr = context.expr();
var arg1 = expr[0].Accept(this);
if (arg1 is ErrorValue)
return arg1;
var arg2 = expr[1].Accept(this);
if (arg2 is ErrorValue)
return arg2;
string code = $"{arg1} {context.op.Text} {arg2}";
return code;
}
public override object VisitPercentage(ExcelFormulaParser.PercentageContext context)
{
var expr = context.expr();
var arg1 = expr.Accept(this);
if (arg1 is ErrorValue)
return arg1;
return $"( {arg1} / 100.0 )";
}
public override object VisitRelational(ExcelFormulaParser.RelationalContext context)
{
var expr = context.expr();
var arg1 = expr[0].Accept(this);
if (arg1 is ErrorValue)
return arg1;
var arg2 = expr[1].Accept(this);
if (arg2 is ErrorValue)
return arg2;
return $"{arg1} {context.op.Text} {arg2}";
}
public override object VisitError(ExcelFormulaParser.ErrorContext context)
{
return new ErrorValue(context.GetText());
}
public override object VisitString(ExcelFormulaParser.StringContext context)
{
var str = context.GetText();
return $"std::string({str})";
}
public override object VisitUnary(ExcelFormulaParser.UnaryContext context)
{
var expr = context.expr();
if(context.sign.Text == "-")
return "-" + expr.Accept(this);
else
return expr.Accept(this);
}
public override object VisitFunction(ExcelFormulaParser.FunctionContext context)
{
var name = context.name.Text;
ExcelReference current = _orig_ref;
var expr = context.expr();
var args = new IExpression[expr.Length];
for (var i = 0; i < expr.Length; i++)
args[i] = new Expression<object>(expr[i], this);
return _library.call_function(name, args, _defs, current);
}
public override object VisitParenthesis(ExcelFormulaParser.ParenthesisContext context)
{
return context.expr().Accept(this);
}
public override object VisitNumber(ExcelFormulaParser.NumberContext context)
{
var num = context.GetText();
if(double.TryParse(num, out double res))
return res.ToString("0.0############");
return num;
}
public override object VisitConcatenate(ExcelFormulaParser.ConcatenateContext context)
{
var expr = context.expr();
var arg1 = "std::to_string(" + expr[0].Accept(this) + ")";
var arg2 = "std::to_string(" + expr[1].Accept(this) + ")";
return $"{arg1}+{arg2}";
}
public override object VisitRange(ExcelFormulaParser.RangeContext context)
{
var rng = context.rng();
return rng.Accept(this);
}
public override object VisitRangeList(ExcelFormulaParser.RangeListContext context)
{
var result = new List<RangeRef>();
foreach (var range in context.rng())
{
var obj = range.Accept(this);
switch (obj)
{
case ErrorValue err:
result.Add(null);
break;
case RangeRef rangeRef:
result.Add(rangeRef);
break;
case null:
result.Add(null);
break;
default:
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
}
}
return result;
}
public override object VisitRangeRef(ExcelFormulaParser.RangeRefContext context)
{
var obj = context.@ref().Accept(this);
switch (obj)
{
case ErrorValue _:
return obj;
case CellRef start:
var (column, row) = CellRef.ConvertAddressToCoord(context.end.Text);
var rng = new RangeRef(start, column, row);
if(rng.IsOneCell())
{
var rref = new ExcelReference(row - 1, row - 1, column - 1, column - 1, rng.SheetName);
process_cell(rref, out _);
}
else
{
var initdef = $@"
auto range_init = [](auto& initval, int nrows, int ncols)
{{
Range< std::decay_t<decltype((initval))> > rng(nrows, ncols);
rng(0,0) = initval;
return rng;
}};";
_defs.add_def(initdef);
var loaddef = $@"
auto range_set = [](auto& range, auto& val, int r, int c)
{{
range(r,c) = val;
}};";
_defs.add_def(loaddef);
var vec = rng.get_all_cells();
if(vec.Count > 0)
{
for(int i = 0; i < vec.Count; i++)
{
int r = (vec[i].Row-1) - (vec[0].Row-1);
int c = (vec[i].Column-1) - (vec[0].Column-1);
var rref = new ExcelReference(vec[i].Row-1, vec[i].Row-1, vec[i].Column-1, vec[i].Column-1, vec[i].SheetName);
var cellref = process_cell(rref, out _);
if(i == 0)
{
string init = $"auto&& {rng.to_name()} = range_init({cellref.ToString()}, {rng.NumRows()}, {rng.NumCols()}); " ;
_defs.add_def(init);
}
else
{
string next = $"range_set({rng.to_name()}, {cellref.ToString()}, {r}, {c}); ";
_defs.add_def(next);
}
}
}
}
return rng;
case null:
return null;
default:
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
}
}
public override object VisitCell(ExcelFormulaParser.CellContext context)
{
return context.@ref().Accept(this);
}
public override object VisitCellList(ExcelFormulaParser.CellListContext context)
{
var result = new List<CellRef>();
foreach (var @ref in context.@ref())
{
var obj = @ref.Accept(this);
switch (obj)
{
case ErrorValue _:
// TODO: Warn that error has been replaced with null here?
result.Add(null);
break;
case CellRef cellRef:
result.Add(cellRef);
break;
case null:
result.Add(null);
break;
default:
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
}
}
return result;
}
public override object VisitCellRef(ExcelFormulaParser.CellRefContext context)
{
var colRow = context.colRow.Text;
var (col1, row1) = CellRef.ConvertAddressToCoord(colRow);
var sheetname = context.sheet?.Text ?? _orig_ref.GetSheetName();
var rref = new ExcelReference(row1 - 1, row1 - 1, col1 - 1, col1 - 1, sheetname);
return process_cell(rref, out _);
}
private object process_cell(ExcelReference rref, out string def)
{
_current_ref = rref;
var cellref = new CellRef(rref);
string r1c1 = cellref.r1_c1();
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, rref);
var formstring = formula.ToString();
if (string.IsNullOrEmpty(formstring))
formstring = "0.0";
if (!formstring.StartsWith("="))
formstring = "=" + formstring;
var cellname = XlCall.Excel(XlCall.xlfGetDef, r1c1, cellref.SheetName);
bool named = false;
if (!(cellname is ExcelError))
{
named = true;
cellref.named_range = cellname.ToString();
}
var antlrstream = new AntlrInputStream(formstring);
var lexer = new ExcelFormulaLexer(antlrstream);
var tokens = new CommonTokenStream(lexer);
var parser = new ExcelFormulaParser(tokens);
parser.AddErrorListener(_error_listener);
var parsetree = parser.main();
if (!_error_listener.IsValid)
throw new Exception(_error_listener.offendingSymbol + _error_listener.msg, _error_listener.exception);
else
{
def = $"auto&& {cellref.ToString()} = {new GeneratorVisitor(rref, _defs, _params).VisitMain(parsetree)} ;";
_defs.add_def(def);
if (named && _params.algo_params.ContainsKey(cellref.named_range))
{
_defs.map_def(def, $"auto&& {cellref.named_range} = me->{cellref.named_range};");
}
if (named && _params.special_params.ContainsKey(cellref.named_range))
{
string realname = cellref.named_range;
_params.special_params.TryGetValue(cellref.named_range, out realname);
_defs.map_def(def, $"auto&& {cellref.named_range} = get_{realname}();");
}
}
return cellref;
}
#endregion
}
}
+179
View File
@@ -0,0 +1,179 @@
using System.Collections.Generic;
using System.Diagnostics;
namespace bts.excel
{
public class RangeRef
{
public RangeRef(CellRef start, CellRef end)
{
Start = start;
End = end;
}
public string named_range { get; set;}
public RangeRef(CellRef start, int endColumn, int endRow)
: this(start, new CellRef(start.SheetName, (endColumn, endRow)))
{ }
public RangeRef Offset(int columnOffset, int rowOffset)
{
var start = Start.Offset(columnOffset, rowOffset);
var end = End.Offset(columnOffset, rowOffset);
return new RangeRef(start, end);
}
public (int col, int row) compute_indexes(int col, int row)
{
return (col - Start.Column, row - Start.Row);
}
public CellRef Start { get; }
public CellRef End { get; }
public override string ToString()
{
return to_name();
}
public string SheetName => Start.SheetName;
public string SimpleSheetName => Start.SimpleSheetName;
public string to_address()
{
return string.Format("'{0}'!{1}:{2}",
SheetName,
CellRef.ConvertCoordToAddress((Start.Column, Start.Row)),
CellRef.ConvertCoordToAddress((End.Column, End.Row)));
}
public string to_name()
{
if (!string.IsNullOrEmpty(named_range))
return named_range;
else
{
return string.Format("{0}_{1}_{2}",
Start.SimpleSheetName,
CellRef.ConvertCoordToAddress((Start.Column, Start.Row)),
CellRef.ConvertCoordToAddress((End.Column, End.Row)));
}
}
public string r1_c1()
{
return $"{Start.r1_c1()}:{End.r1_c1()}";
}
public bool IsVector()
{
return IsSingleRow() || IsSingleColumn();
}
public bool IsOneCell()
{
return IsSingleRow() && IsSingleColumn();
}
internal List<CellRef> get_all_cells()
{
var result = new List<CellRef>();
var sheetName = Start.SheetName;
var numRows = NumRows();
var lastRow = Start.Row + numRows - 1;
for (var row = Start.Row; row <= lastRow; row++)
{
var numCols = NumCols();
var lastCol = Start.Column + numCols - 1;
for (var col = Start.Column; col <= lastCol; col++)
{
result.Add(new CellRef(sheetName, (col, row)));
}
}
return result;
}
internal CellRef[,] cell_matrix_col_row()
{
var numCols = NumCols();
var numRows = NumRows();
var result = new CellRef[numCols, numRows];
var sheetName = Start.SheetName;
var lastCol = Start.Column + numCols - 1;
var lastRow = Start.Row + numRows - 1;
int c = 0;
for (var col = Start.Column; col <= lastCol; col++)
{
int r = 0;
for (var row = Start.Row; row <= lastRow; row++)
{
result[c, r++] = new CellRef(sheetName, (col, row));
}
c++; //it's haunting me
}
return result;
}
internal List<CellRef> GetVector()
{
Debug.Assert(IsVector());
var result = new List<CellRef>();
if (IsSingleColumn())
{
var col = Start.Column;
var sheetName = Start.SheetName;
var numRows = NumRows();
var lastRow = Start.Row + numRows - 1;
for (var row = Start.Row; row <= lastRow; row++)
{
result.Add(new CellRef(sheetName, (col, row)));
}
}
else
{
Debug.Assert(IsSingleRow());
var row = Start.Row;
var sheetName = Start.SheetName;
var numCols = NumCols();
var lastCol = Start.Column + numCols - 1;
for (var col = Start.Column; col <= lastCol; col++)
{
result.Add(new CellRef(sheetName, (col, row)));
}
}
return result;
}
public bool IsSingleColumn()
{
Debug.Assert(Start.SheetName == End.SheetName);
return Start.Column == End.Column;
}
public bool IsSingleRow()
{
Debug.Assert(Start.SheetName == End.SheetName);
return Start.Row == End.Row;
}
public int NumRows()
{
Debug.Assert(Start.SheetName == End.SheetName);
return End.Row - Start.Row + 1;
}
public int NumCols()
{
Debug.Assert(Start.SheetName == End.SheetName);
return End.Column - Start.Column + 1;
}
}
}
@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using bts.excel.functions;
using ExcelDna.Registration;
using ExcelDna.Integration;
using System.Reflection;
namespace bts.excel.functions.lib
{
public interface IFunction
{
object function(IExpression[] args, IDefinitionVisitor dv, ExcelReference containing_cell);
}
public class FunctionLibrary
{
private readonly Dictionary<string, IFunction> _functions =
new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, SparkAlgoCodeAttribute> _dnafunctions =
new Dictionary<string, SparkAlgoCodeAttribute>(StringComparer.OrdinalIgnoreCase);
public FunctionLibrary(bool include_setups)
{
add_functions(Logical.functions);
add_functions(Mathematics.functions);
add_functions(Stat.functions);
if(include_setups)
add_function(nameof(sparkalgo_setup_order), new sparkalgo_setup_order());
var dna = ExcelRegistration.GetExcelFunctions(); string fn = string.Empty;
foreach (var xl in dna)
{
foreach(var att in xl.CustomAttributes)
{
if(att is SparkAlgoCodeAttribute ac)
_dnafunctions[xl.FunctionAttribute.Name] = ac;
}
}
}
public bool is_defined(string name) => _functions.ContainsKey(name) || _dnafunctions.ContainsKey(name);
public void add_function(string name, IFunction function)
{
_functions.Add(name, function);
}
public void add_functions(Dictionary<string, IFunction> delegates)
{
foreach (var f in delegates)
_functions.Add(f.Key, f.Value);
}
public object call_function(string name, IExpression[] args, IDefinitionVisitor dv, ExcelReference containing_cell)
{
object code = string.Empty;
if(string.IsNullOrEmpty(name) || string.IsNullOrWhiteSpace(name))
return code;
if (_functions.TryGetValue(name, out IFunction func))
code = func.function(args, dv, containing_cell);
else
code = bts_function_or_throw(name, args, dv, containing_cell);
return code;
}
object bts_function_or_throw(string name, IExpression[] args, IDefinitionVisitor dv, ExcelReference containing_cell)
{
SparkAlgoCodeAttribute att = null;
if (_dnafunctions.TryGetValue(name, out att))
{
if (!string.IsNullOrEmpty(att.dual_definition))
dv.add_dual_definition(att.dual_definition);
if (!string.IsNullOrEmpty(att.dual_impl))
dv.add_dual_impl(att.dual_impl);
if(!string.IsNullOrEmpty(att.replacement_call))
{
return att.replacement_call;
}
else
{
object[] param = args.Select(x => x.evaluate()).ToArray();
string function_call = $"{name} ( "; int z = 0;
foreach (var p in param)
function_call += p.ToString() + (z++ < param.Length - 1 ? ", " : "");
function_call += ")";
return modify_if_array_formula(function_call, containing_cell);
}
}
throw new NotSupportedException("Function " + name + " not currently supported by algogen");
}
internal bool is_array_formula(ExcelReference containing_cell)
{
bool is_array_formula = (bool)XlCall.Excel(XlCall.xlfGetCell, 49, containing_cell);
return is_array_formula;
}
internal object modify_if_array_formula(object code, ExcelReference containing_cell)
{
if(!is_array_formula(containing_cell))
return code;
using (var ech = new ExcelEchoOffHelper())
{
using (var calc = new ExcelCalculationManualHelper())
{
using (var sel = new ExcelSelectionHelper(containing_cell))
{
lock(this)
{
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, containing_cell);
var c = new CellRef(containing_cell);
var a = XlCall.Excel(XlCall.xlcSelectSpecial, 6);
ExcelReference array = (ExcelReference)XlCall.Excel(XlCall.xlfSelection);
code += $"({containing_cell.RowFirst - array.RowFirst},{containing_cell.ColumnFirst - array.ColumnFirst})";
}
}
}
}
return code;
}
}
}
@@ -0,0 +1,20 @@
using System;
namespace bts.excel
{
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class SparkAlgoCodeAttribute : Attribute
{
public string dual_definition;
public string dual_impl;
public string replacement_call;
public SparkAlgoCodeAttribute()
{
//nulls ok
}
}
}
@@ -0,0 +1,270 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using ExcelDna.Integration;
using bts.api;
using ExcelDna.Registration.Utils;
using System.Reactive.Linq;
using bts.md;
using MD = bts.excel.rtd.MDSubscribe;
namespace bts.excel
{
public class SparkAlgoFunctions
{
static ConcurrentDictionary<string, object> _cache = new ConcurrentDictionary<string, object>();
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_parameter([ExcelArgument(AllowReference = true)] object parameter, string type)
{
ExcelReference par = (ExcelReference)parameter;
string sheetname = (string)XlCall.Excel(XlCall.xlSheetNm, par);
var cellref = new CellRef(sheetname, (par.ColumnFirst + 1, par.RowFirst + 1));
string r1c1 = cellref.r1_c1();
var cellname = XlCall.Excel(XlCall.xlfGetDef, r1c1, sheetname);
if (!(cellname is ExcelError))
{
BTSRibbon.globals.register_algo_parameter(type ?? "double", cellname.ToString());
return true;
}
else
return $"{ErrorValue.Name}, please make algo parameters named ranges";
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_unregister_parameter(string parameter)
{
if(!string.IsNullOrEmpty(parameter))
{
BTSRibbon.globals.unregister_algo_parameter(parameter);
return true;
}
else
return $"{ErrorValue.Name}, no parameter named " + parameter;
}
//See ExcelAlgoTemplate.cpp __AlgoName__ class def
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_user_parameter([ExcelArgument(AllowReference = true)] object parameter)
{
return sparkalgo_register_special_parameter(parameter, "user");
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_account_parameter([ExcelArgument(AllowReference = true)] object parameter)
{
return sparkalgo_register_special_parameter(parameter, "account");
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_side_parameter([ExcelArgument(AllowReference = true)] object parameter)
{
return sparkalgo_register_special_parameter(parameter, "side");
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_price_parameter([ExcelArgument(AllowReference = true)] object parameter)
{
return sparkalgo_register_special_parameter(parameter, "price");
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_quantity_parameter([ExcelArgument(AllowReference = true)] object parameter)
{
return sparkalgo_register_special_parameter(parameter, "quantity");
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_tif_parameter([ExcelArgument(AllowReference = true)] object parameter)
{
return sparkalgo_register_special_parameter(parameter, "time_in_force");
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_register_special_parameter([ExcelArgument(AllowReference = true)] object parameter, string name)
{
ExcelReference par = (ExcelReference)parameter;
string sheetname = (string)XlCall.Excel(XlCall.xlSheetNm, par);
var cellref = new CellRef(sheetname, (par.ColumnFirst + 1, par.RowFirst + 1));
string r1c1 = cellref.r1_c1();
var cellname = XlCall.Excel(XlCall.xlfGetDef, r1c1, sheetname);
if (!(cellname is ExcelError))
{
if(string.IsNullOrEmpty(name))
name = cellname.ToString();
BTSRibbon.globals.register_special_parameter(cellname.ToString(), name);
return true;
}
else
return $"{ErrorValue.Name}, please make special parameters named ranges";
}
[ExcelFunction(IsMacroType = true)]
static public object sparkalgo_unregister_special_parameter(string parameter)
{
if (!string.IsNullOrEmpty(parameter))
{
BTSRibbon.globals.unregister_special_parameter(parameter);
return true;
}
else
return $"{ErrorValue.Name}, no special parameter named " + parameter;
}
[ExcelFunction]
[SparkAlgoCode]
static public object sparkalgo_take_snapshot(string tag, object data)
{
object val;
if(!_cache.TryGetValue(tag, out val))
{
_cache[tag] = val = data;
}
return val;
}
[ExcelFunction()]
[SparkAlgoCode(
dual_definition = @"
int sparkalgo_snapshot_switch(const std::string& name, int on_off);",
dual_impl = @"
int __AlgoName__::sparkalgo_snapshot_switch(const std::string& name, int on_off)
{
auto tag = name+""_switch"";
if(on_off)
return sparkalgo_take_snapshot(tag, on_off);
else
{
auto it = me->snapshots.find(tag);
if(it == me->snapshots.end())
return on_off;
else
return std::get<int>(it->second);
}
}"
)]
static public object sparkalgo_snapshot_switch(string tag, int on_off)
{
object val;
if (!_cache.TryGetValue(tag, out val) && on_off > 0)
{
_cache[tag] = val = on_off;
}
return val ?? 0;
}
[ExcelFunction(IsMacroType = true)]
static public void sparkalgo_reset_all_snapshots(object _)
{
_cache.Clear();
}
static public void sparkalgo_reset_all_snapshots_no_args()
{
_cache.Clear();
}
[ExcelFunction(IsMacroType = true)]
static public void sparkalgo_reset_snapshot(string tag)
{
_cache.TryRemove(tag, out object val);
}
[ExcelFunction(Category = "BTS", Description = "Resolve a security id from contract details")]
[SparkAlgoCode(
replacement_call = "sparkalgo_resolve_security_id()"
)]
public async static void sparkalgo_resolve_primary_security_id(
[ExcelArgument("Required. The specific contract, GCZ8 or ZNU8-ZNZ8, etc")] string contract,
[ExcelArgument("Required. The symbol, GC or ZN, etc")] string symbol,
[ExcelArgument("Optional. The type, Future or Spread, default = Future")] string security_type,
[ExcelArgument("Optional. The exchange, default = GLBX")] string exchange,
ExcelAsyncHandle handle)
{
exchange = string.IsNullOrEmpty(exchange) ? Security.ExchangeDataOneofCase.Glbx.ToString() : exchange;
Func<SecurityImmutable, bool> where = MD.md_where(contract, symbol, security_type, exchange, out Venue ven);
var md = bts.excel.rtd.MDSubscribe.market_data(ven);
try
{
SecurityImmutable? sec = await md.find_security_async(where, TimeSpan.FromSeconds(15));
if (sec != null && sec.HasValue)
handle.SetResult(sec.Value.Id);
else
handle.SetResult(0);
}
catch (Exception ex)
{
handle.SetException(ex);
}
}
[ExcelFunction(Category = "BTS", Description = "Resolve a security id from contract details")]
[SparkAlgoCode()]
public async static void sparkalgo_resolve_security_id(
[ExcelArgument("Required. The specific contract, GCZ8 or ZNU8-ZNZ8, etc")] string contract,
[ExcelArgument("Required. The symbol, GC or ZN, etc")] string symbol,
[ExcelArgument("Optional. The type, Future or Spread, default = Future")] string security_type,
[ExcelArgument("Optional. The exchange, default = GLBX")] string exchange,
ExcelAsyncHandle handle)
{
exchange = string.IsNullOrEmpty(exchange) ? Security.ExchangeDataOneofCase.Glbx.ToString() : exchange;
Func<SecurityImmutable, bool> where = MD.md_where(contract, symbol, security_type, exchange, out Venue ven);
var md = bts.excel.rtd.MDSubscribe.market_data(ven);
try
{
SecurityImmutable? sec = await md.find_security_async(where, TimeSpan.FromSeconds(15));
if (sec != null && sec.HasValue)
handle.SetResult(sec.Value.Id);
else
handle.SetResult(0);
}
catch (Exception ex)
{
handle.SetException(ex);
}
}
[ExcelFunction(Category = "BTS", Description = "Subscribe to BTS Spark full depth market data by security_id")]
[SparkAlgoCode(
dual_definition = @"
ExcelBookAccessor sparkalgo_md_book_id(SecurityKey sk);",
dual_impl = @"
ExcelBookAccessor __AlgoName__::sparkalgo_md_book_id(SecurityKey sk)
{
ExcelBookAccessor acc(me->last_book);
return acc;
}"
)]
public static object sparkalgo_md_book_id([ExcelArgument("Required. The security_id")] long security_id)
{
var param = new object[] { security_id };
return ObservableRtdUtil.Observe(nameof(sparkalgo_md_book_id), param,
() => MD.md_observable_id(Convert.ToUInt32(security_id)).Select(data =>
{
var x = data.FirstOrDefault();
if (x != null && x is IMDBook upd)
return rtd.BookMDFormatter.format(upd);
return x;
}));
}
[ExcelFunction(Category = "BTS")]
[SparkAlgoCode(
dual_impl = @"
OrderKey __AlgoName__::sparkalgo_generate_local_orderid()
{
static uint32_t id = 0;
return OrderKey( ++id );
}"
)]
public static object sparkalgo_generate_local_orderid(object dependency)
{
ExcelReference caller = XlCall.Excel(XlCall.xlfCaller) as ExcelReference;
CellRef rref = new CellRef(caller);
return rref.Row*1000 + rref.Column;
}
}
}
@@ -0,0 +1,40 @@
using System;
using spark_excel.grammar;
using Antlr4.Runtime;
using ExcelDna.Integration;
namespace bts.excel.functions.lib
{
public class sparkalgo_setup_order : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
CodeParts _parts = new CodeParts();
var gen = new GeneratorVisitor(containing_cell, _parts, BTSRibbon.globals, false);
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, containing_cell);
var antlrstream = new AntlrInputStream(formula.ToString());
var lexer = new ExcelFormulaLexer(antlrstream);
var tokens = new CommonTokenStream(lexer);
var parser = new ExcelFormulaParser(tokens);
var parsetree = parser.main();
var name = new CellRef(containing_cell).ToString();
var expr = gen.VisitMain(parsetree);
foreach (var dd in _parts.get_dual_definitions())
dv.add_dual_definition(dd);
foreach (var di in _parts.get_dual_impls())
dv.add_dual_impl(di);
string setup = string.Join(Environment.NewLine, _parts.get_defs());
setup += $"{Environment.NewLine} me->{name}_order_id = { expr.ToString() };";
dv.register_order_setup(setup, $"me->{name}_order_id = cache_attach(attached_order, st);");
dv.register_algo_parameter("OrderKey", name + "_order_id");
return "me->" + name + "_order_id";
}
}
}
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
namespace bts.excel.functions.lib
{
public static class Logical
{
public static Dictionary<string, IFunction> functions { get; }
static Logical()
{
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
{
{"AND", new And()},
{"IF", new If()},
{"IFERROR", new IfError()},
{"OR", new Or()},
};
}
}
public static class Mathematics
{
public static Dictionary<string, IFunction> functions { get; }
static Mathematics()
{
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
{
{"LN", new Ln()},
{"LOG", new Log()},
{"POWER", new Power()},
{"PI", new Pi()},
{"SQRT", new Sqrt()},
{"SUM", new Sum()},
{"SIN", new Sin()},
};
}
}
public static class Stat
{
public static Dictionary<string, IFunction> functions { get; }
static Stat()
{
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
{
{"AVERAGE", new Average()},
{"MAX", new Max()},
{"MIN", new Min()},
};
}
}
}
@@ -0,0 +1,32 @@
using System;
namespace bts.excel.functions.lib
{
public class And : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: AND(logical1, [logical2], ...)
// https://support.office.com/en-gb/article/AND-function-5f19b2e8-e1df-4408-897a-ce285a19e9d9
if (args.Length < 2)
{
throw new ArgumentException(
$"AND function requires at least 2 arguments, got {args.Length}",
nameof(args));
}
string code = "(";
foreach (var fx in args)
{
var arg = fx.evaluate();
if (arg is ErrorValue)
return arg;
code += arg + " && ";
}
code += "true)";
return code;
}
}
}
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel.functions.lib
{
public class Average : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: Average(number1,[number2],...)
// https://support.office.com/en-GB/article/AVERAGE-function-047bac88-d466-426c-a32b-8f33eb960cf6
if (args.Length < 1)
{
throw new ArgumentException(
$"Average function takes at least 1 arguments, got {args.Length}",
nameof(args));
}
string name = "avg_" + new Random().Next(0, int.MaxValue);
var first = args[0].evaluate();
if(first is CellRef)
{
string def = ($"auto {name} = 0.0;{Environment.NewLine}");
int count = 0;
foreach (var arg in args)
{
var argv = arg.evaluate();
if (arg is ErrorValue)
return arg;
count++;
def += $" {name} += {argv};{Environment.NewLine}";
}
if(count > 1)
def += $" {name} /= {count}";
dv.add_def(def);
return name;
}
else if (first is RangeRef)
{
string rng = first.ToString();
string loop = $@"
auto {rng}_avg = 0.0;
for(auto& {rng}_i : {rng} )
{rng}_avg += {rng}_i;
{rng}_avg /= {rng}.size();
";
dv.add_def(loop);
return rng + "_avg";
}
return name;
}
}
}
@@ -0,0 +1,35 @@
using System;
namespace bts.excel.functions.lib
{
public class If : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: IF(logical_test, value_if_true, [value_if_false])
// https://support.office.com/en-gb/article/IF-function-69aed7c9-4e8a-4755-a9bc-aa8bbff73be2
if (args.Length != 2 && args.Length != 3)
{
throw new ArgumentException(
"IF function takes 2 or 3 arguments",
nameof(args));
}
var arg = args[0].evaluate();
if (arg is ErrorValue)
return arg;
string code = string.Empty;
if (args.Length == 3)
{
code += $"( {arg} ? ({ args[1].evaluate() }) : ({args[2].evaluate()}) ) ";
}
else
{
code += $"( {arg} ? ({ args[1].evaluate() }) : false) ";
}
return code;
}
}
}
@@ -0,0 +1,32 @@
using System;
namespace bts.excel.functions.lib
{
public class IfError : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: IFERROR(value, value_if_error)
// https://support.office.com/en-gb/article/IFERROR-function-c526fd07-caeb-47b8-8bb6-63f3e417f611
if (args.Length != 2)
{
throw new ArgumentException(
"IFERROR function takes exactly 2 arguments",
nameof(args));
}
try
{
var arg = args[0].evaluate();
if (arg is ErrorValue)
return arg;
return arg;
}
catch (Exception)
{
return args[1].evaluate();
}
}
}
}
@@ -0,0 +1,26 @@
using System;
namespace bts.excel.functions.lib
{
public class Ln : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: LN(number)
// https://support.office.com/en-gb/article/LN-function-81fe1ed7-dac9-4acd-ba1d-07a142c6118f
if (args.Length != 1)
{
throw new ArgumentException(
$"LN function takes only 1 argument, got {args.Length}");
}
var arg = args[0].evaluate();
if (arg is ErrorValue)
return arg;
string code = $"log({arg})";
return code;
}
}
}
@@ -0,0 +1,40 @@
using System;
namespace bts.excel.functions.lib
{
public class Log : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: LOG(number, [base])
// https://support.office.com/en-gb/article/LOG-function-4e82f196-1ca9-4747-8fb0-6c4a3abb3280
switch (args.Length)
{
case 1:
var arg = args[0].evaluate();
if (arg is ErrorValue)
return arg;
string code = $"log10({arg})";
return code;
case 2:
var arg1 = args[0].evaluate();
if (arg1 is ErrorValue)
return arg1;
var arg2 = args[1].evaluate();
if (arg2 is ErrorValue)
return arg2;
string code2 = $"log({arg1}, {arg2})";
return code2;
default:
throw new ArgumentException(
$"LOG function takes only 1 or 2 arguments, got {args.Length}",
nameof(args));
}
}
}
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel.functions.lib
{
public class Max : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: MAX(number1, [number2], ...)
// https://support.office.com/en-GB/article/MAX-function-e0012414-9ac8-4b34-9a47-73e662c08098
if (args.Length < 1)
{
throw new ArgumentException(
$"MAX function takes at least 1 arguments, got {args.Length}",
nameof(args));
}
string code = $"std::max(\n";
foreach (var arg in args)
{
var argv = arg.evaluate();
if (argv is ErrorValue)
return argv;
code += $"{argv},";
}
code += $"{ args[args.Length-1].evaluate()} );\n";
return code;
}
}
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel.functions.lib
{
public class Min : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: MIN(number1, [number2], ...)
// https://support.office.com/en-GB/article/MIN-function-61635d12-920f-4ce2-a70f-96f202dcc152
if (args.Length < 1)
{
throw new ArgumentException(
$"MIN function takes at least 1 arguments, got {args.Length}",
nameof(args));
}
string code = $"std::min(\n";
foreach (var arg in args)
{
var argv = arg.evaluate();
if (argv is ErrorValue)
return argv;
code += $"{argv},";
}
code += $"{ args[args.Length - 1].evaluate()} );\n";
return code;
}
}
}
@@ -0,0 +1,25 @@
using System;
namespace bts.excel.functions.lib
{
public class Or : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: OR(logical1, [logical2], ...)
// https://support.office.com/en-gb/article/OR-function-7d17ad14-8700-4281-b308-00b131e22af0
string code = "(";
foreach (var fx in args)
{
var arg = fx.evaluate();
if (arg is ErrorValue)
return arg;
code += arg + " || ";
}
code += "false)";
return code;
}
}
}
@@ -0,0 +1,22 @@
using System;
namespace bts.excel.functions.lib
{
public class Pi : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: PI()
// https://support.office.com/en-gb/article/PI-function-264199d0-a3ba-46b8-975a-c4a04608989b
if (args.Length != 0)
{
throw new ArgumentException(
$"PI function takes no arguments, got {args.Length}",
nameof(args));
}
return Math.PI.ToString();
}
}
}
@@ -0,0 +1,30 @@
using System;
namespace bts.excel.functions.lib
{
public class Power : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: POWER(number, power)
// https://support.office.com/en-gb/article/POWER-function-d3f2908b-56f4-4c3f-895a-07fb519c362a
if (args.Length != 2)
{
throw new ArgumentException(
$"POWER function takes 2 arguments, got {args.Length}",
nameof(args));
}
var arg1 = args[0].evaluate();
if (arg1 is ErrorValue)
return arg1;
var arg2 = args[1].evaluate();
if (arg2 is ErrorValue)
return arg2;
return $"pow({arg1}, {arg2})";
}
}
}
@@ -0,0 +1,25 @@
using System;
namespace bts.excel.functions.lib
{
public class Sin : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: SIN(number)
// https://support.office.com/en-gb/article/SIN-function-654975c2-05c4-4831-9a24-2c65e4040fdf
if (args.Length != 1)
{
throw new ArgumentException(
$"Sin function takes only 1 argument, got {args.Length}");
}
var arg1 = args[0].evaluate();
if (arg1 is ErrorValue)
return arg1;
return $"sin({arg1})";
}
}
}
@@ -0,0 +1,25 @@
using System;
namespace bts.excel.functions.lib
{
public class Sqrt : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: SQRT(number)
// https://support.office.com/en-gb/article/SQRT-function-654975c2-05c4-4831-9a24-2c65e4040fdf
if (args.Length != 1)
{
throw new ArgumentException(
$"SQRT function takes only 1 argument, got {args.Length}");
}
var arg1 = args[0].evaluate();
if (arg1 is ErrorValue)
return arg1;
return $"sqrt({arg1})";
}
}
}
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.excel.functions.lib
{
public class Sum : IFunction
{
public object function(IExpression[] args, IDefinitionVisitor dv, ExcelDna.Integration.ExcelReference containing_cell)
{
// Syntax: Average(number1,[number2],...)
// https://support.office.com/en-GB/article/AVERAGE-function-047bac88-d466-426c-a32b-8f33eb960cf6
if (args.Length < 1)
{
throw new ArgumentException(
$"Sum function takes at least 1 arguments, got {args.Length}",
nameof(args));
}
string name = "sum_" + new Random().Next(0, int.MaxValue);
var first = args[0].evaluate();
if (first is CellRef)
{
string def = ($"auto {name} = 0.0;{Environment.NewLine}");
int count = 0;
foreach (var arg in args)
{
var argv = arg.evaluate();
if (arg is ErrorValue)
return arg;
count++;
def += $" {name} += {argv};{Environment.NewLine}";
}
dv.add_def(def);
return name;
}
else if (first is RangeRef)
{
string rng = first.ToString();
string loop = $@"
auto {rng}_sum = 0.0;
for(auto& {rng}_i : {rng} )
{rng}_sum += {rng}_i;
";
dv.add_def(loop);
return rng + "_sum";
}
return name;
}
}
}
@@ -0,0 +1,91 @@
using ExcelDna.Integration.CustomUI;
using ExcelDna.Integration;
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System;
using System.Collections.Generic;
namespace bts.excel
{
[ComVisible(true)]
public class BTSRibbon : ExcelRibbon
{
static readonly Generator _generator = new Generator();
static public IParameters globals => _generator.globals;
public override string GetCustomUI(string RibbonID)
{
return @"
<customUI xmlns='http://schemas.microsoft.com/office/2006/01/customui'>
<ribbon>
<tabs>
<tab id='tab1' label='BTS'>
<group id='group1' label='Algos'>
<button id='button1' label='Build Algo From Selection' onAction='ribbon_generate_code'/>
</group >
</tab>
</tabs>
</ribbon>
</customUI>";
}
public void ribbon_generate_code(IRibbonControl control)
{
//MessageBox.Show("Hello from control " + control.Id + " " + control.Context.ToString());
Excel.Application app = (Excel.Application)((dynamic)control.Context).Application;
app.GetType().InvokeMember("Run", System.Reflection.BindingFlags.InvokeMethod, null,
app, new object[]{ nameof(bts_generate_algo_code) }, new System.Globalization.CultureInfo(1033));
}
[ExcelCommand()]
static public void bts_generate_algo_code()
{
Excel.Application app = (Excel.Application)ExcelDnaUtil.Application;
var wind = new NativeWindow();
wind.AssignHandle(ExcelDnaUtil.WindowHandle);
//var r = app.ActiveCell.FunctionWizard();
try
{
string document_name = string.Empty;
List<CodeParts> functions = new List<CodeParts>();
// ExcelAsyncUtil.QueueAsMacro( () =>
MessageBox.Show(wind, "About to generate code, pretend you see a progress bar and click ok");
foreach(var cell in app.Selection as Excel.Range)
{
ExcelReference rref = RangeHelper.RangeToExcelReference(cell as Excel.Range);
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, rref);
if(formula == null || string.IsNullOrEmpty(formula.ToString()))
continue;
var cellref = new CellRef(rref);
document_name = (string)XlCall.Excel(XlCall.xlfGetDocument, 88, cellref.SheetName);
string fname = null;
var f = _generator.bts_generate_function_code(rref, ref fname);
functions.Add(f);
}
if(functions.Count > 0)
{
//@"C:\code\bts\spark\build\ninja_debug\sdk\examples\excel_plugin_example.cpp",
string[] filepath = new [] {
@"c:\code\bts\spark\src\sdk\examples\excel_plugin_example.cpp",
};
string fname = _generator.bts_generate_algo_class(CellRef.stringize_workbook( document_name ),
functions, filepath);
MessageBox.Show(wind, "Code Generation Succesful " + fname);
}
else
MessageBox.Show(wind, "Nothing to do! Please select one or more 'Algo Sinks' and try again ");
}
catch (Exception ex)
{
MessageBox.Show(wind, ex.ToString());
}
finally
{
wind.ReleaseHandle();
}
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using ExcelDna.Integration;
namespace bts.excel.extensions
{
public static class ExcelDNAExtensions
{
public static string GetSheetName(this ExcelReference rref)
{
string sheetname = (string)XlCall.Excel(XlCall.xlSheetNm, rref);
return sheetname;
}
}
}
@@ -2,6 +2,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using ExcelDna.Integration;
using ExcelDna.Integration.Rtd;
using ExcelDna.Registration.Utils;
@@ -11,14 +12,26 @@ using bts.excel;
using bts.excel.extensions;
using bts.excel.rtd;
using Newtonsoft.Json;
using bts.api;
using System.Threading.Tasks;
using bts.utils;
namespace bts.api.orderman
{
public partial class OrderService_Excel
public partial class OrderService_Excel
{
readonly static Lazy<user.AuthService> _users = new Lazy<user.AuthService>(() => Locator.locate<user.AuthService>(), true);
readonly static Lazy<admin.AdminService> _admin = new Lazy<admin.AdminService>(() => Locator.locate<admin.AdminService>(), true);
static AsyncLazy<core.Result<AdminData>> _admin_data = new AsyncLazy<core.Result<AdminData>>(() => _admin.Value.get_admin_data_async(new VoidMessage()));
class OrderDef
{
public Create create;
public string order_type;
public core.Result<Response>? response;
}
static ConcurrentDictionary<long, OrderDef> _orders = new ConcurrentDictionary<long, OrderDef>();
[ExcelFunction(Category = "BTS", Description = "Get All Accessible Accounts")]
async public static void spark_get_accounts(string login_token, ExcelAsyncHandle handle)
@@ -30,7 +43,7 @@ namespace bts.api.orderman
if (admin.success)
{
foreach (var acc in admin.value.Accounts)
if(acc.IsPresent)
if (acc.IsPresent)
accts.Add(acc.Name);
}
var res = Utilities.make_column_vector(accts.ToArray());
@@ -42,96 +55,148 @@ namespace bts.api.orderman
}
}
[ExcelFunction(Category = "BTS", Description = "Cancel an Order by Order ID", IsMacroType = true)]
public static object spark_orderservice_cancel_order(long order_id)
static Task<core.Result<Response>> cancel_order(long order_id)
{
var resp = _service.Value.cancel_order(new Cancel() { OrderKey = Convert.ToUInt32(order_id) });
return parse_response(resp);
var resp = _service.Value.cancel_order_async(new Cancel() { OrderKey = Convert.ToUInt32(order_id) });
return resp;
}
[ExcelFunction(Category = "BTS", Description = "Update an Order by Order ID", IsMacroType = true)]
public static object spark_orderservice_update_order(long order_id, double price, long quantity)
[ExcelFunction(Category = "BTS", Description = "Cancel an Order by Order ID")]
public static async void spark_orderservice_cancel_order(long order_id, ExcelAsyncHandle handle)
{
var resp = _service.Value.update_order(new Update() { OrderKey = Convert.ToUInt32(order_id), Price = price, Quantity = Convert.ToUInt32(quantity) });
return parse_response(resp);
}
try
{
var resp = await cancel_order(order_id);
handle.SetResult(parse_response(resp));
}
catch(Exception ex)
{
handle.SetException(ex);
}
}
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key", IsMacroType = true)]
public static object spark_orderservice_send_order_key(
[ExcelArgument("Required. Only send the order if > last")] long local_order_id,
[ExcelArgument("Required. Security key")] long security_key,
[ExcelArgument("Required. Side BUY or SELL")] string side,
[ExcelArgument("Required. User")] string user,
[ExcelArgument("Required. Account")] string account,
[ExcelArgument("Required. Price")] double price,
[ExcelArgument("Required. Quantity")] long quantity,
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params = null,
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null,
[ExcelArgument("OPTIONAL. (YES,NO) - Default YES")] string manual_order_indicator = null,
[ExcelArgument("OPTIONAL. (1,2,3,4) - Default 1")] string cti_code = null,
[ExcelArgument("OPTIONAL. (CUSTOMER,FIRM) - Default FIRM")] string customer_or_firm = null)
static Task<core.Result<Response>> update_order(long order_id, double price, long quantity, CustomOrderDefinition cust = null)
{
var resp = _service.Value.update_order_async(new Update()
{
OrderKey = Convert.ToUInt32(order_id),
Price = price,
Quantity = Convert.ToUInt32(quantity),
Custom = cust
});
return resp;
}
[ExcelFunction(Category = "BTS", Description = "Update an Order by Order ID")]
public static async void spark_orderservice_update_order(long order_id, double price, long quantity, ExcelAsyncHandle handle)
{
try
{
var resp = await update_order(order_id, price, quantity);
handle.SetResult(parse_response(resp));
}
catch (Exception ex)
{
handle.SetException(ex);
}
}
private async static Task<Create> create_order(long security_key, string user, string account, string side,
double price, long quantity, string time_in_force,
string order_type, string order_params)
{
if (local_order_id <= _local_order_id)
return "local_order_id must be greater than " + _local_order_id;
Create cr = new Create();
cr.NewOrder = new OrderDefinition()
{
SecurityKey = Convert.ToUInt32(security_key),
Side = EnumParser.ToEnum<api.Side>(side),
TimeInForce = time_in_force.ToEnum<TimeInForce>(TimeInForce.Day),
ManualOrderIndicator = manual_order_indicator.ToEnum<ManualOrderIndicator>(ManualOrderIndicator.Yes),
CtiCode = cti_code.ToEnum<CTICode>(CTICode.CtiCode1),
CustomerOrFirm = customer_or_firm.ToEnum<CustomerOrFirm>(CustomerOrFirm.Firm),
};
cr.Price = price;
cr.Quantity = Convert.ToUInt32(quantity);
var btsuser = resolve_user(user);
if(btsuser == null)
return "Could not resolve user: " + user;
cr.Quantity = Convert.ToUInt32(quantity);
var btsuser = await resolve_user(user);
if (btsuser == null)
throw new ArgumentException("Could not resolve user: " + user);
cr.NewOrder.User = btsuser.Name;
cr.NewOrder.UserId = btsuser.Id;
var btaccount = resolve_account(account);
if(btaccount == null)
return "Could not resolve account: " + account;
var btaccount = await resolve_account(account);
if (btaccount == null)
throw new ArgumentException("Could not resolve account: " + account);
cr.NewOrder.Account = btaccount.Name;
cr.NewOrder.AccountId = btaccount.Id;
if (string.IsNullOrEmpty(order_type))
order_type = "Limit";
cr.NewOrder.CustomDef = custom_def(order_type, order_params);
if(cr.NewOrder.CustomDef == null || cr.NewOrder.CustomDef.CustomCase == CustomOrderDefinition.CustomOneofCase.None)
return "Could not parse order_type and/or order_params info";
var resp = _service.Value.create_order(cr);
_local_order_id = local_order_id;
return parse_response(resp, true);
if (cr.NewOrder.CustomDef == null || cr.NewOrder.CustomDef.CustomCase == CustomOrderDefinition.CustomOneofCase.None)
throw new ArgumentException("Could not parse order_type and/or order_params info");
return cr;
}
static Tuple<Create, Task<core.Result<Response>>> send_order(Create cr)
{
var resp = Tuple.Create(cr, _service.Value.create_order_async(cr));
return resp;
}
private static object parse_response(Response resp, bool show_vertically = false)
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key")]
public static async void spark_orderservice_send_order_by_security_key(
[ExcelArgument("Required. Security key")] long security_key,
[ExcelArgument("Required. User")] string user,
[ExcelArgument("Required. Account")] string account,
[ExcelArgument("Required. Side BUY or SELL")] string side,
[ExcelArgument("Required. Price")] double price,
[ExcelArgument("Required. Quantity")] long quantity,
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force,
[ExcelArgument("OPTIONAL. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params,
ExcelAsyncHandle handle)
{
object[,] response_cells = new JsonFormatter(resp).format(false);
if(show_vertically)
try
{
Create cr = await create_order(security_key, user, account, side, price, quantity, order_type, order_params, time_in_force);
var order = send_order(cr);
var resp = await order.Item2;
handle.SetResult(parse_response(resp, true));
}
catch(Exception ex)
{
handle.SetException(ex);
}
}
private static object parse_response(core.Result<Response> resp, bool show_vertically = false)
{
object[,] response_cells = new JsonFormatter(resp.value).format(false);
if (show_vertically)
response_cells = bts.excel.Utilities.transpose_keep_dimension(response_cells);
return response_cells;
}
static Lazy<AdminData> _admin_data = new Lazy<AdminData>(() => _admin.Value.get_admin_data(new VoidMessage()), true);
static BTSUser resolve_user(string username)
{
var user = _admin_data.Value.Users.FirstOrDefault(x => username != null ? x.Name.ToLowerInvariant() == username.ToLowerInvariant() : true);
async static Task<BTSUser> resolve_user(string username)
{
var admin = await _admin_data.Value;
if (!admin.success)
return null;
var user = admin.value.Users.FirstOrDefault(x => username != null ? x.Name.ToLowerInvariant() == username.ToLowerInvariant() : true);
return user;
}
static BTSAccount resolve_account(string acctname)
async static Task<BTSAccount> resolve_account(string acctname)
{
var acc = _admin_data.Value.Accounts.FirstOrDefault(x => x.Name.ToLowerInvariant() == acctname.ToLowerInvariant());
var admin = await _admin_data.Value;
if(!admin.success)
return null;
var acc = admin.value.Accounts.FirstOrDefault(x => x.Name.ToLowerInvariant() == acctname.ToLowerInvariant());
return acc;
}
}
static CustomOrderDefinition custom_def(string order_type, string order_params)
{
var member = ReflectionExtensions.get_fields_or_properties(typeof(CustomOrderDefinition))
.Where(x => x.Name.ToLower() == order_type.ToLower()).FirstOrDefault();
.Where(x => x.Name.ToLower() == order_type?.ToLower()).FirstOrDefault();
if (member != null)
{
var mi = member.member_type();
@@ -144,42 +209,205 @@ namespace bts.api.orderman
return null;
}
static long _local_order_id = 0;
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key", IsMacroType = true)]
public static object spark_orderservice_send_order(
[ExcelArgument("Required. Only send the order if > last")] long local_order_id,
[ExcelArgument("Required. Exchange (GLBX,SIM)")] string exchange,
[ExcelArgument("Required. Security Desc (ZNZ9 or ZNZ9-ZNH0)")] string security_desc,
[ExcelArgument("Required. Side BUY or SELL")] string side,
[ExcelFunction(Category = "BTS", Description = "Setup Spark Order ")]
[SparkAlgoCode]
public async static void sparkalgo_setup_order(
[ExcelArgument("Required. Security Key")] long local_order_id,
[ExcelArgument("Required. Security Key")] long security_key,
[ExcelArgument("Required. User")] string user,
[ExcelArgument("Required. Account")] string account,
[ExcelArgument("Required. Side BUY or SELL")] string side,
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force,
[ExcelArgument("OPTIONAL. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params,
ExcelAsyncHandle handle)
{
if (_orders.ContainsKey(local_order_id))
handle.SetResult(local_order_id);
// throw new ArgumentException("use a new local order id per order");
Create cr = await create_order(security_key, user, account, side, double.NaN, 0, order_type, order_params, time_in_force);
_orders[local_order_id] = new OrderDef { create = cr, order_type = order_type, response = null};
handle.SetResult(local_order_id);
}
[ExcelFunction(Category = "BTS", Description = "Send or Modify or Cancel a Spark Order ")]
[SparkAlgoCode]
public async static void sparkalgo_send_or_update_order(
[ExcelArgument("Required. Local Order returned from sparkalgo_setup_order")] long local_order_id,
[ExcelArgument("Required. Only process inputs for the order if > 1")] long should_process_inputs,
[ExcelArgument("Required. Price")] double price,
[ExcelArgument("Required. Quantity")] long quantity,
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params = null,
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null,
[ExcelArgument("OPTIONAL. (YES,NO) - Default YES")] string manual_order_indicator = null,
[ExcelArgument("OPTIONAL. (1,2,3,4) - Default 1")] string cti_code = null,
[ExcelArgument("OPTIONAL. (CUSTOMER,FIRM) - Default FIRM")] string customer_or_firm = null)
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_param_update,
ExcelAsyncHandle handle)
{
if (local_order_id <= _local_order_id)
return "local_order_id must be greater than " + _local_order_id;
Venue ven;
if (!Utilities.parse_venue(exchange, out ven))
return "Can't Parse Exchange: " + (exchange ?? string.Empty);
uint key;
if (MDSubscribe.market_data(ven).lookup_security_key_from_desc(security_desc, out key))
try
{
return spark_orderservice_send_order_key(local_order_id, (long)key, side, user, account, price, quantity, order_type, order_params,
time_in_force, manual_order_indicator, cti_code, customer_or_firm);
if(should_process_inputs <= 0)
{
handle.SetResult(-1);
return;
}
if (_orders.TryGetValue(local_order_id, out OrderDef def))
{
if(!def.response.HasValue) //is new
{
def.create.Price = price;
def.create.Quantity = Convert.ToUInt32(quantity);
var resp = await send_order(def.create).Item2;
if(resp.success)
{
def.response = resp;
handle.SetResult(resp.value.OrderKey);
return;
}
else
{
handle.SetException(new Exception(resp.error_message));
return;
}
}
else //is working
{
if (quantity > 0)
{
CustomOrderDefinition cust = null;
if(!string.IsNullOrEmpty(order_param_update))
cust = custom_def(def.order_type, order_param_update);
if (def.create.Price != price || def.create.Quantity != Convert.ToUInt32(quantity) ||
(cust != null && !cust.Equals(def.create.NewOrder.CustomDef)))
{
var resp = await update_order(def.response.Value.value.OrderKey, price, quantity, cust);
if (resp.success && resp.value.ErrorCode == OrderError.Ok)
{
def.create.Price = price;
def.create.Quantity = Convert.ToUInt32(quantity);
}
handle.SetResult(resp.value.OrderKey);
}
else
handle.SetResult(def.response.Value.value.OrderKey);
return;
}
else
{
var resp = await cancel_order(def.response.Value.value.OrderKey);
if(resp.success)
_orders.TryRemove(local_order_id, out _); //take out of working so we don't try to update it
handle.SetResult(def.response.Value.value.OrderKey);
return;
}
}
}
handle.SetResult(-1);
}
else
catch (Exception ex)
{
return "Can't find security desc " + security_desc;
handle.SetException(ex);
}
}
enum ChildStatus { WORKING, COMPLETED, CANCELED };
class ChildState
{
public double price;
public uint quantity;
public uint total_fills;
public double avg_price;
public uint last_fills;
public double last_price;
public ChildStatus status;
};
[ExcelFunction(Category = "BTS", Description = "Receive Child Updates from Spark Order ")]
[SparkAlgoCode]
public static object sparkalgo_child_state([ExcelArgument("Required. Local Order from sparkalgo_setup_order")] long local_order_key,
bool display_headers)
{
Func<bts.api.orderman.OrderRecord, bool> myorder = (x =>
{
if(_orders.TryGetValue(local_order_key, out OrderDef ord))
{
if(ord.response.HasValue && ord.response.Value.success)
return ord.response.Value.value.OrderKey == x.Key;
}
return false;
});
var sub = _service.Value.subscribe_orders().SelectMany(x => x.all.Where(xx => myorder(xx)));
var obj = sub.Select(x =>
{
var ch = new ChildState ();
ch.price = x.State.Price;
ch.quantity = x.State.Quantity;
ch.total_fills = x.State.FilledQuantity;
ch.avg_price = x.State.AvgPrice;
ch.last_fills = 0;
ch.last_price = x.State.LastPrice;
switch(x.State.Status)
{
case OrderStatus.Inactive:
case OrderStatus.Working:
case OrderStatus.PendingWorking:
ch.status = ChildStatus.WORKING;
break;
case OrderStatus.Canceled:
case OrderStatus.Disconnected:
case OrderStatus.Error:
case OrderStatus.RejectedBySpark:
ch.status = ChildStatus.CANCELED;
break;
default:
ch.status = ChildStatus.COMPLETED;
break;
}
return Utilities.transpose_keep_dimension( DataParser.scatter_data(ch, display_headers) );
});
return ObservableRtdUtil.Observe(nameof(sparkalgo_child_state),
new object[] { local_order_key, display_headers },
() => obj);
}
static System.Timers.Timer _timer = new System.Timers.Timer(1000);
[ExcelFunction(Category = "BTS", Description = "Receive Child Updates from Spark Order ")]
[SparkAlgoCode]
public static void sparkalgo_timer(ExcelAsyncHandle handle)
{
_timer.Elapsed += (x, e) => handle.SetResult(System.DateTime.Now);
_timer.Enabled = true;
}
[ExcelFunction(Category = "BTS", Description = "Opposite side ")]
[SparkAlgoCode(
dual_definition = @"
bts::api::Side sparkalgo_other_side(bts::api::Side side);",
dual_impl = @"
bts::api::Side __AlgoName__::sparkalgo_other_side(bts::api::Side side)
{
return securities::other_side(side);
}"
)]
public static object sparkalgo_other_side(object side)
{
bts.api.Side the_side;
if (Enum.TryParse<bts.api.Side>("SIDE_" + side.ToString(), true, out the_side) ||
Enum.TryParse<bts.api.Side>(side.ToString(), true, out the_side))
{
switch (the_side)
{
case bts.api.Side.Buy:
return bts.api.Side.Sell.ToString();
case bts.api.Side.Sell:
return bts.api.Side.Buy.ToString();
default:
return side.ToString();
}
}
else
return ExcelErrorUtil.ToComError(ExcelError.ExcelErrorValue);
}
}
}
Binary file not shown.
+723
View File
@@ -0,0 +1,723 @@
/*
[The "BSD licence"]
Copyright (c) 2013 Sam Harwell
All rights reserved.
Redistribution and use
in source and binary forms, with or without
modification, are permitted provided that the
following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form
must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the
documentation and/or other materials provided with the distribution.
3. The name of the
author may not be used to endorse or promote products
derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** C 2011 grammar built from the C11 Spec */
grammar C;
primaryExpression:
Identifier
| Constant
| StringLiteral+
| '(' expression ')'
| genericSelection
| '__extension__'? '(' compoundStatement ')' // Blocks (GCC extension)
| '__builtin_va_arg' '(' unaryExpression ',' typeName ')'
| '__builtin_offsetof' '(' typeName ',' unaryExpression ')';
genericSelection:
'_Generic' '(' assignmentExpression ',' genericAssocList ')';
genericAssocList:
genericAssociation
| genericAssocList ',' genericAssociation;
genericAssociation:
typeName ':' assignmentExpression
| 'default' ':' assignmentExpression;
postfixExpression:
primaryExpression
| postfixExpression '[' expression ']'
| postfixExpression '(' argumentExpressionList? ')'
| postfixExpression '.' Identifier
| postfixExpression '->' Identifier
| postfixExpression '++'
| postfixExpression '--'
| '(' typeName ')' '{' initializerList '}'
| '(' typeName ')' '{' initializerList ',' '}'
| '__extension__' '(' typeName ')' '{' initializerList '}'
| '__extension__' '(' typeName ')' '{' initializerList ',' '}';
argumentExpressionList:
assignmentExpression
| argumentExpressionList ',' assignmentExpression;
unaryExpression:
postfixExpression
| '++' unaryExpression
| '--' unaryExpression
| unaryOperator castExpression
| 'sizeof' unaryExpression
| 'sizeof' '(' typeName ')'
| '_Alignof' '(' typeName ')'
| '&&' Identifier ; // GCC extension address of label
unaryOperator: '&' | '*' | '+' | '-' | '~' | '!';
castExpression:
'(' typeName ')' castExpression
| '__extension__' '(' typeName ')' castExpression
| unaryExpression
| DigitSequence ; // for
multiplicativeExpression:
castExpression
| multiplicativeExpression '*' castExpression
| multiplicativeExpression '/' castExpression
| multiplicativeExpression '%' castExpression;
additiveExpression:
multiplicativeExpression
| additiveExpression '+' multiplicativeExpression
| additiveExpression '-' multiplicativeExpression;
shiftExpression:
additiveExpression
| shiftExpression '<<' additiveExpression
| shiftExpression '>>' additiveExpression;
relationalExpression:
shiftExpression
| relationalExpression '<' shiftExpression
| relationalExpression '>' shiftExpression
| relationalExpression '<=' shiftExpression
| relationalExpression '>=' shiftExpression;
equalityExpression:
relationalExpression
| equalityExpression '==' relationalExpression
| equalityExpression '!=' relationalExpression;
andExpression:
equalityExpression
| andExpression '&' equalityExpression;
exclusiveOrExpression:
andExpression
| exclusiveOrExpression '^' andExpression;
inclusiveOrExpression:
exclusiveOrExpression
| inclusiveOrExpression '|' exclusiveOrExpression;
logicalAndExpression:
inclusiveOrExpression
| logicalAndExpression '&&' inclusiveOrExpression;
logicalOrExpression:
logicalAndExpression
| logicalOrExpression '||' logicalAndExpression;
conditionalExpression:
logicalOrExpression (
'?' expression ':' conditionalExpression
)?;
assignmentExpression:
conditionalExpression
| unaryExpression assignmentOperator assignmentExpression
| DigitSequence ; // for
assignmentOperator:
'='
| '*='
| '/='
| '%='
| '+='
| '-='
| '<<='
| '>>='
| '&='
| '^='
| '|=';
expression:
assignmentExpression
| expression ',' assignmentExpression;
constantExpression: conditionalExpression;
declaration:
declarationSpecifiers initDeclaratorList ';'
| declarationSpecifiers ';'
| staticAssertDeclaration;
declarationSpecifiers: declarationSpecifier+;
declarationSpecifiers2: declarationSpecifier+;
declarationSpecifier:
storageClassSpecifier
| typeSpecifier
| typeQualifier
| functionSpecifier
| alignmentSpecifier;
initDeclaratorList:
initDeclarator
| initDeclaratorList ',' initDeclarator;
initDeclarator: declarator | declarator '=' initializer;
storageClassSpecifier:
'typedef'
| 'extern'
| 'static'
| '_Thread_local'
| 'auto'
| 'register';
typeSpecifier: (
'void'
| 'char'
| 'short'
| 'int'
| 'long'
| 'float'
| 'double'
| 'signed'
| 'unsigned'
| '_Bool'
| '_Complex'
| '__m128'
| '__m128d'
| '__m128i'
)
| '__extension__' '(' ('__m128' | '__m128d' | '__m128i') ')'
| atomicTypeSpecifier
| structOrUnionSpecifier
| enumSpecifier
| typedefName
| '__typeof__' '(' constantExpression ')' // GCC extension
| typeSpecifier pointer;
structOrUnionSpecifier:
structOrUnion Identifier? '{' structDeclarationList '}'
| structOrUnion Identifier;
structOrUnion: 'struct' | 'union';
structDeclarationList:
structDeclaration
| structDeclarationList structDeclaration;
structDeclaration:
specifierQualifierList structDeclaratorList? ';'
| staticAssertDeclaration;
specifierQualifierList:
typeSpecifier specifierQualifierList?
| typeQualifier specifierQualifierList?;
structDeclaratorList:
structDeclarator
| structDeclaratorList ',' structDeclarator;
structDeclarator:
declarator
| declarator? ':' constantExpression;
enumSpecifier:
'enum' Identifier? '{' enumeratorList '}'
| 'enum' Identifier? '{' enumeratorList ',' '}'
| 'enum' Identifier;
enumeratorList: enumerator | enumeratorList ',' enumerator;
enumerator:
enumerationConstant
| enumerationConstant '=' constantExpression;
enumerationConstant: Identifier;
atomicTypeSpecifier: '_Atomic' '(' typeName ')';
typeQualifier: 'const' | 'restrict' | 'volatile' | '_Atomic';
functionSpecifier: (
'inline'
| '_Noreturn'
| '__inline__' // GCC extension
| '__stdcall'
)
| gccAttributeSpecifier
| '__declspec' '(' Identifier ')';
alignmentSpecifier:
'_Alignas' '(' typeName ')'
| '_Alignas' '(' constantExpression ')';
declarator: pointer? directDeclarator gccDeclaratorExtension*;
directDeclarator:
Identifier
| '(' declarator ')'
| directDeclarator '[' typeQualifierList? assignmentExpression? ']'
| directDeclarator '[' 'static' typeQualifierList? assignmentExpression ']'
| directDeclarator '[' typeQualifierList 'static' assignmentExpression ']'
| directDeclarator '[' typeQualifierList? '*' ']'
| directDeclarator '(' parameterTypeList ')'
| directDeclarator '(' identifierList? ')'
| Identifier ':' DigitSequence // bit field
| '(' typeSpecifier? pointer directDeclarator ')' ; // function pointer like: (__cdecl *f)
gccDeclaratorExtension:
'__asm' '(' StringLiteral+ ')'
| gccAttributeSpecifier;
gccAttributeSpecifier:
'__attribute__' '(' '(' gccAttributeList ')' ')';
gccAttributeList:
gccAttribute (',' gccAttribute)*
| ; // empty
gccAttribute:
~(
','
| '('
| ')'
) // relaxed def for "identifier or reserved word"
('(' argumentExpressionList? ')')?
| ; // empty
nestedParenthesesBlock: (
~('(' | ')')
| '(' nestedParenthesesBlock ')'
)*;
pointer:
'*' typeQualifierList?
| '*' typeQualifierList? pointer
| '^' typeQualifierList? // Blocks language extension
| '^' typeQualifierList? pointer ; // Blocks language extension
typeQualifierList:
typeQualifier
| typeQualifierList typeQualifier;
parameterTypeList: parameterList | parameterList ',' '...';
parameterList:
parameterDeclaration
| parameterList ',' parameterDeclaration;
parameterDeclaration:
declarationSpecifiers declarator
| declarationSpecifiers2 abstractDeclarator?;
identifierList: Identifier | identifierList ',' Identifier;
typeName: specifierQualifierList abstractDeclarator?;
abstractDeclarator:
pointer
| pointer? directAbstractDeclarator gccDeclaratorExtension*;
directAbstractDeclarator:
'(' abstractDeclarator ')' gccDeclaratorExtension*
| '[' typeQualifierList? assignmentExpression? ']'
| '[' 'static' typeQualifierList? assignmentExpression ']'
| '[' typeQualifierList 'static' assignmentExpression ']'
| '[' '*' ']'
| '(' parameterTypeList? ')' gccDeclaratorExtension*
| directAbstractDeclarator '[' typeQualifierList? assignmentExpression? ']'
| directAbstractDeclarator '[' 'static' typeQualifierList? assignmentExpression ']'
| directAbstractDeclarator '[' typeQualifierList 'static' assignmentExpression ']'
| directAbstractDeclarator '[' '*' ']'
| directAbstractDeclarator '(' parameterTypeList? ')' gccDeclaratorExtension*;
typedefName: Identifier;
initializer:
assignmentExpression
| '{' initializerList '}'
| '{' initializerList ',' '}';
initializerList:
designation? initializer
| initializerList ',' designation? initializer;
designation: designatorList '=';
designatorList: designator | designatorList designator;
designator: '[' constantExpression ']' | '.' Identifier;
staticAssertDeclaration:
'_Static_assert' '(' constantExpression ',' StringLiteral+ ')' ';';
statement:
labeledStatement
| compoundStatement
| expressionStatement
| selectionStatement
| iterationStatement
| jumpStatement
| ('__asm' | '__asm__') ('volatile' | '__volatile__') '(' (
logicalOrExpression (',' logicalOrExpression)*
)? (':' (logicalOrExpression (',' logicalOrExpression)*)?)* ')' ';';
labeledStatement:
Identifier ':' statement
| 'case' constantExpression ':' statement
| 'default' ':' statement;
compoundStatement: '{' blockItemList? '}';
blockItemList: blockItem | blockItemList blockItem;
blockItem: statement | declaration;
expressionStatement: expression? ';';
selectionStatement:
'if' '(' expression ')' statement ('else' statement)?
| 'switch' '(' expression ')' statement;
iterationStatement:
While '(' expression ')' statement
| Do statement While '(' expression ')' ';'
| For '(' forCondition ')' statement;
// | 'for' '(' expression? ';' expression? ';' forUpdate? ')' statement | For '(' declaration
// expression? ';' expression? ')' statement
forCondition:
forDeclaration ';' forExpression? ';' forExpression?
| expression? ';' forExpression? ';' forExpression?;
forDeclaration:
declarationSpecifiers initDeclaratorList
| declarationSpecifiers;
forExpression:
assignmentExpression
| forExpression ',' assignmentExpression;
jumpStatement:
'goto' Identifier ';'
| 'continue' ';'
| 'break' ';'
| 'return' expression? ';'
| 'goto' unaryExpression ';' ; // GCC extension
compilationUnit: translationUnit? EOF;
translationUnit:
externalDeclaration
| translationUnit externalDeclaration;
externalDeclaration:
functionDefinition
| declaration
| ';' ; // stray ;
functionDefinition:
declarationSpecifiers? declarator declarationList? compoundStatement;
declarationList: declaration | declarationList declaration;
Auto: 'auto';
Break: 'break';
Case: 'case';
Char: 'char';
Const: 'const';
Continue: 'continue';
Default: 'default';
Do: 'do';
Double: 'double';
Else: 'else';
Enum: 'enum';
Extern: 'extern';
Float: 'float';
For: 'for';
Goto: 'goto';
If: 'if';
Inline: 'inline';
Int: 'int';
Long: 'long';
Register: 'register';
Restrict: 'restrict';
Return: 'return';
Short: 'short';
Signed: 'signed';
Sizeof: 'sizeof';
Static: 'static';
Struct: 'struct';
Switch: 'switch';
Typedef: 'typedef';
Union: 'union';
Unsigned: 'unsigned';
Void: 'void';
Volatile: 'volatile';
While: 'while';
Alignas: '_Alignas';
Alignof: '_Alignof';
Atomic: '_Atomic';
Bool: '_Bool';
Complex: '_Complex';
Generic: '_Generic';
Imaginary: '_Imaginary';
Noreturn: '_Noreturn';
StaticAssert: '_Static_assert';
ThreadLocal: '_Thread_local';
LeftParen: '(';
RightParen: ')';
LeftBracket: '[';
RightBracket: ']';
LeftBrace: '{';
RightBrace: '}';
Less: '<';
LessEqual: '<=';
Greater: '>';
GreaterEqual: '>=';
LeftShift: '<<';
RightShift: '>>';
Plus: '+';
PlusPlus: '++';
Minus: '-';
MinusMinus: '--';
Star: '*';
Div: '/';
Mod: '%';
And: '&';
Or: '|';
AndAnd: '&&';
OrOr: '||';
Caret: '^';
Not: '!';
Tilde: '~';
Question: '?';
Colon: ':';
Semi: ';';
Comma: ',';
Assign: '=';
// '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '&=' | '^=' | '|='
StarAssign: '*=';
DivAssign: '/=';
ModAssign: '%=';
PlusAssign: '+=';
MinusAssign: '-=';
LeftShiftAssign: '<<=';
RightShiftAssign: '>>=';
AndAssign: '&=';
XorAssign: '^=';
OrAssign: '|=';
Equal: '==';
NotEqual: '!=';
Arrow: '->';
Dot: '.';
Ellipsis: '...';
Identifier: IdentifierNondigit ( IdentifierNondigit | Digit)*;
fragment IdentifierNondigit:
Nondigit
| UniversalCharacterName
; //| // other implementation-defined characters...
fragment Nondigit: [a-zA-Z_];
fragment Digit: [0-9];
fragment UniversalCharacterName:
'\\u' HexQuad
| '\\U' HexQuad HexQuad;
fragment HexQuad:
HexadecimalDigit HexadecimalDigit HexadecimalDigit HexadecimalDigit;
Constant:
IntegerConstant
| FloatingConstant
//| EnumerationConstant
| CharacterConstant;
fragment IntegerConstant:
DecimalConstant IntegerSuffix?
| OctalConstant IntegerSuffix?
| HexadecimalConstant IntegerSuffix?
| BinaryConstant;
fragment BinaryConstant: '0' [bB] [0-1]+;
fragment DecimalConstant: NonzeroDigit Digit*;
fragment OctalConstant: '0' OctalDigit*;
fragment HexadecimalConstant:
HexadecimalPrefix HexadecimalDigit+;
fragment HexadecimalPrefix: '0' [xX];
fragment NonzeroDigit: [1-9];
fragment OctalDigit: [0-7];
fragment HexadecimalDigit: [0-9a-fA-F];
fragment IntegerSuffix:
UnsignedSuffix LongSuffix?
| UnsignedSuffix LongLongSuffix
| LongSuffix UnsignedSuffix?
| LongLongSuffix UnsignedSuffix?;
fragment UnsignedSuffix: [uU];
fragment LongSuffix: [lL];
fragment LongLongSuffix: 'll' | 'LL';
fragment FloatingConstant:
DecimalFloatingConstant
| HexadecimalFloatingConstant;
fragment DecimalFloatingConstant:
FractionalConstant ExponentPart? FloatingSuffix?
| DigitSequence ExponentPart FloatingSuffix?;
fragment HexadecimalFloatingConstant:
HexadecimalPrefix HexadecimalFractionalConstant BinaryExponentPart FloatingSuffix?
| HexadecimalPrefix HexadecimalDigitSequence BinaryExponentPart FloatingSuffix?;
fragment FractionalConstant:
DigitSequence? '.' DigitSequence
| DigitSequence '.';
fragment ExponentPart:
'e' Sign? DigitSequence
| 'E' Sign? DigitSequence;
fragment Sign: '+' | '-';
DigitSequence: Digit+;
fragment HexadecimalFractionalConstant:
HexadecimalDigitSequence? '.' HexadecimalDigitSequence
| HexadecimalDigitSequence '.';
fragment BinaryExponentPart:
'p' Sign? DigitSequence
| 'P' Sign? DigitSequence;
fragment HexadecimalDigitSequence: HexadecimalDigit+;
fragment FloatingSuffix: 'f' | 'l' | 'F' | 'L';
fragment CharacterConstant:
'\'' CCharSequence '\''
| 'L\'' CCharSequence '\''
| 'u\'' CCharSequence '\''
| 'U\'' CCharSequence '\'';
fragment CCharSequence: CChar+;
fragment CChar: ~['\\\r\n] | EscapeSequence;
fragment EscapeSequence:
SimpleEscapeSequence
| OctalEscapeSequence
| HexadecimalEscapeSequence
| UniversalCharacterName;
fragment SimpleEscapeSequence: '\\' ['"?abfnrtv\\];
fragment OctalEscapeSequence:
'\\' OctalDigit
| '\\' OctalDigit OctalDigit
| '\\' OctalDigit OctalDigit OctalDigit;
fragment HexadecimalEscapeSequence: '\\x' HexadecimalDigit+;
StringLiteral: EncodingPrefix? '"' SCharSequence? '"';
fragment EncodingPrefix: 'u8' | 'u' | 'U' | 'L';
fragment SCharSequence: SChar+;
fragment SChar:
~["\\\r\n]
| EscapeSequence
| '\\\n' // Added line
| '\\\r\n' ; // Added line
ComplexDefine: '#' Whitespace? 'define' ~[#]* -> skip;
IncludeDirective:
'#' Whitespace? 'include' Whitespace? (
('"' ~[\r\n]* '"')
| ('<' ~[\r\n]* '>')
) Whitespace? Newline -> skip;
// ignore the following asm blocks:
/*
asm
{
mfspr x, 286;
}
*/
AsmBlock: 'asm' ~'{'* '{' ~'}'* '}' -> skip;
// ignore the lines generated by c preprocessor sample line : '#line 1 "/home/dm/files/dk1.h" 1'
LineAfterPreprocessing: '#line' Whitespace* ~[\r\n]* -> skip;
LineDirective:
'#' Whitespace? DecimalConstant Whitespace? StringLiteral ~[\r\n]* -> skip;
PragmaDirective:
'#' Whitespace? 'pragma' Whitespace ~[\r\n]* -> skip;
Whitespace: [ \t]+ -> skip;
Newline: ( '\r' '\n'? | '\n') -> skip;
BlockComment: '/*' .*? '*/' -> skip;
LineComment: '//' ~[\r\n]* -> skip;
Loaded 100 of 241 files, more files were not shown because too many files have changed in this diff. Show more