Compare commits

..
331 Commits
Author SHA1 Message Date
Eric Harding dd5c680155 work in progress 2019-04-29 11:18:11 -04:00
Eric Harding 5a1cc4cf2b Update release notes 2019-04-29 09:38:02 -04:00
Eric Harding 9be3a09749 Merged in SPARK-2179-PIQ (pull request #219)
SPARK-2179 PIQ
2019-04-29 13:36:49 +00:00
Eric Harding 61745d0ed7 Update release notes 2019-04-29 09:35:13 -04:00
Eric Harding db1610591e merge master 2019-04-29 09:34:31 -04:00
Eric Harding bb1571540a Merged in sparkcmd_commandline (pull request #218)
RTM: Sparkcmd commandline

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2019-04-29 13:13:23 +00:00
Eric Harding 342d89724e Update release notes 2019-04-29 09:13:09 -04:00
Michael Harding 07d3903d11 Merged in Documentation (pull request #215)
RTM Remove SDK docs from the manual
2019-04-29 13:12:35 +00:00
Jake Norbut d08b134d7f Merged in SPARK-2285 (pull request #214)
RTM - SPARK-2285 - add null check on r.newval

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2019-04-29 13:10:49 +00:00
Eric Harding 92f67d1d2d piq: Add security key to log message 2019-04-26 16:40:59 -04:00
Eric Harding 43a18941a4 Change debug assert to logged warning in PIQ 2019-04-26 16:18:37 -04:00
Jake Norbut b102b271b0 PR edit - use single_order instead; this will be gone from attach/detach 2019-04-26 10:27:07 -05:00
Jake Norbut b129bea129 PR edit - method signature all on one line 2019-04-26 10:27:07 -05:00
Eric Harding 38d79eb58b Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-2179-PIQ 2019-04-26 09:38:43 -04:00
Eric Harding dfd0cfc217 templatize mbo callbacks for older templates 2019-04-25 17:16:35 -04:00
Eric Harding a123ac2ed8 Turn on MBO background processing 2019-04-25 15:14:27 -04:00
Eric Harding ad4fd13189 MBO: Add seconary index to handle mbo change 2019-04-25 14:25:49 -04:00
Eric Harding 8a1180835c MBO diagnostics/logging 2019-04-25 11:06:39 -04:00
Eric Harding f65f34c2fe Limit returned MBOSnapshot size to avoid memory leaking 2019-04-24 14:12:50 -04:00
Eric Harding 34401960f9 Fix mbo bid sort order 2019-04-24 11:08:46 -04:00
Eric Harding 34b7c99c81 Toggle mbostats off by default 2019-04-24 11:05:26 -04:00
Eric Harding 044ef0b812 merge master 2019-04-24 10:15:07 -04:00
Eric Harding 9cc4b5aa87 Update unit/system tests command line parser 2019-04-24 10:12:08 -04:00
Eric Harding 821274f544 Merge branch 'SPARK-2179-PIQ' of bitbucket.org:bluetradesys/spark into PIQachu 2019-04-23 09:48:54 -04:00
Eric Harding ca6c0f984c mbo bugfixes 2019-04-23 09:11:30 -04:00
Eric Harding 80e337c599 sparkcmd should respect '-configserver' 2019-04-22 16:45:36 -04:00
Eric Harding 5b71918a1f Revert "Make sure every sparkcmd command respects -configserver"
This reverts commit dab95d7e91.
2019-04-22 16:41:48 -04:00
Eric Harding d2a9d21700 Handle incremental MBO messages from cme 2019-04-22 15:26:50 -04:00
Kevin Darby 16fee6e478 Merged in stunnel_win7 (pull request #217)
RTM: Stunnel Win7

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-04-22 17:57:27 +00:00
Eric Harding 3cf701a211 swap map for btree 2019-04-18 14:42:26 -04:00
Eric Harding 5392d05ec8 MBO perf work 2019-04-18 14:39:15 -04:00
Kevin Darby 7838bdf04f too much whitespace for even me 2019-04-18 14:16:08 -04:00
Kevin Darby 546553406b PR comments 2019-04-18 14:13:09 -04:00
Kevin Darby 02d0f6191c -added a manifest to our csproj that we'd meant to add anyway for win7 subprocess creation
-added a fallback for stunnel if we can't create a windows job object
2019-04-18 14:06:44 -04:00
Michael Harding 36722e27ad Remove SDK docs from the manual 2019-04-18 11:08:32 -04:00
Jake Norbut dcfbb6e62f need a new security order demuxer for the new piq observable 2019-04-18 08:58:56 -05:00
Jake Norbut ff2d150dc4 restricting orders with piqs to just ladders for now 2019-04-17 17:33:46 -05:00
Jake Norbut 6692797c3f changing from .Publish to .Replay b/c apparently Publish doesn't send old values i.e. previously working orders won't show up, but new ones will 2019-04-17 13:03:43 -05:00
Jake Norbut b059aedbce adding PlacementInQueue key to toggles so that we have a config option to enable/disable once deployed; defaults to enabled 2019-04-17 08:53:26 -05:00
Eric Harding 73c9be3bec SPARK-2197 bugfix 2019-04-16 15:47:11 -04:00
Jake Norbut bc35e773ce piq_column settings should never return null, which allevites doing the same in sanitize_ladder_settings and another time when dealing with WindowSpecifics 2019-04-16 14:19:43 -05:00
Jake Norbut 4c8d0a4217 add null check on r.newval 2019-04-16 14:13:06 -05:00
Jake Norbut 05abbc4a50 don't show uint.Max piqs during server restart 2019-04-16 12:03:33 -05:00
Eric Harding 95500187f0 Indicate our orders in diagnostic 2019-04-16 12:44:23 -04:00
Jake Norbut 8716099ac6 Removing OrderSecPiq struct since OrderSecurity does everything that did; we still may want to rename it down the line 2019-04-16 11:15:19 -05:00
Eric Harding d7850d6b70 MBO diagnostic api 2019-04-16 12:14:27 -04:00
Eric Harding 905c1cab2c SPARK-2206 tests 2019-04-16 12:14:27 -04:00
Jake Norbut 6181548902 piq background color now matches orders background color 2019-04-16 10:56:31 -05:00
Jake Norbut c32eada3ac the big enchilada - adding PIQ column to LadderGridHost 2019-04-16 10:49:03 -05:00
Jake Norbut 42feedfd36 adding piq column to settings 2019-04-16 10:33:48 -05:00
Jake Norbut d76c2a30ce price_vs_piq property on LadderVM; appropriate changes for LadderOrderVM constructor 2019-04-16 10:32:54 -05:00
Jake Norbut c312a880a7 adding MBOService for piq, left joining piq on orders 2019-04-16 10:29:29 -05:00
Jake Norbut b2ed88fb4a adding binding expression for new PIQColumnWidth property 2019-04-16 10:23:57 -05:00
Jake Norbut 567edafe18 including the piq here on this new 2019-04-16 10:23:29 -05:00
Jake Norbut 9a39d84739 tuple to OrderSecPiq in here too 2019-04-16 10:20:42 -05:00
Jake Norbut d9b4e21c61 changing from tuple to simple OrderSecPiq for sanity 2019-04-16 10:20:09 -05:00
Jake Norbut fa287b14ef OrderSecurity now also has a piq; maybe we change the class name? 2019-04-16 10:16:40 -05:00
Jake Norbut 096bf3338c adding PIQ 2019-04-16 10:15:06 -05:00
Jake Norbut a646f28831 added min_piq property; now stores (OrderRecordImmutable, uint?) instead of just OrderRecordImmutable 2019-04-16 10:14:05 -05:00
Jake Norbut 391397692c adding PIQ case option 2019-04-16 10:08:28 -05:00
Jake Norbut 8130d5a0f8 adding render_piq method 2019-04-16 10:06:32 -05:00
Jake Norbut d89f93c323 Adding MBOService as an export in gui 2019-04-15 11:05:40 -05:00
Eric Harding a376fe9e22 Fix pool_stats crash for the pool paupers 2019-04-12 09:56:28 -04:00
Eric Harding 7381088d6d Remove unnecessary error log 2019-04-11 11:07:49 -04:00
Eric Harding 5f9a421490 Additional logging for QueryServer::get_names error 2019-04-11 10:28:21 -04:00
Eric Harding f7fd81f3af Improve Servicetail 2019-04-10 14:47:50 -04:00
Eric Harding f6c0f393bd SPARK-2244 Suppress empty updates 2019-04-10 11:04:28 -04:00
Eric Harding adec1bdfd0 SPARK-678 Dont' crash on null array 2019-04-10 10:57:44 -04:00
Eric Harding a3de83dd04 SPARK-2244 Update position on timer tick 2019-04-10 10:24:33 -04:00
Eric Harding 5ba7cd561c SPARK-2244 remove parameterless ActiveOrder constructor 2019-04-10 10:16:18 -04:00
Eric Harding 5ff9f13e7e Update cmakedeps 2019-04-10 10:11:17 -04:00
Eric Harding f4b65b21bb merge master 2019-04-09 15:27:15 -04:00
Eric Harding c1789bb4a1 SPARK-2244 Helps to enable the service 2019-04-09 15:14:35 -04:00
Eric Harding 0cf4b5c2c8 SPARK-2224 3/3 basic piq stream (unblocks jake) 2019-04-09 15:07:10 -04:00
Eric Harding a101a267ed More consistent to call old previous for Change 2019-04-09 14:58:15 -04:00
Eric Harding d76b098c51 SPARK-2179 streaming piq 2/3 2019-04-09 10:00:26 -04:00
Eric Harding 06aa540110 SPARK-2248 Update api subscribe clients for this api change 2019-04-09 09:59:05 -04:00
Eric Harding 9fd7db55ca SPARK-2248 Update.h should have add/remove/change instead of just add/remove 2019-04-09 09:59:00 -04:00
Pedro Pinto 5b118d8fa0 Fixed build. 2019-04-08 09:56:46 -04:00
Pedro Pinto b570762054 Updated release notes. 2019-04-08 09:56:39 -04:00
Eric Harding b664c42cae SPARK-2179 streaming piq 1/2 2019-04-08 09:52:45 -04:00
Pedro Pinto a25c17306f Merge remote-tracking branch 'origin/stunnel' 2019-04-08 09:27:00 -04:00
Pedro Pinto b0c52f542a Updated release notes. 2019-04-08 09:23:21 -04:00
Pedro Pinto dabeb63eac Merge remote-tracking branch 'origin/SPARK-2179-spark-login' 2019-04-08 09:22:39 -04:00
Pedro Pinto f0d3cabd14 Updated release notes. 2019-04-08 09:19:50 -04:00
Pedro Pinto 418a1078c2 Updated release notes. 2019-04-08 09:17:20 -04:00
John Colvin 3d37dc481f SPARK-2192 NullRef when hitting enter in order ticket after typing in a qty then price 2019-04-06 14:39:39 -05:00
Pedro Pinto d24b7a3707 Possibly fixed SPARK-2225.
This bug is probably behind a number of different crashes.
2019-04-05 12:30:44 -04:00
Kevin Darby 12362d9719 Merge branch 'master' of bitbucket.org:bluetradesys/spark into stunnel 2019-04-04 13:06:10 -04:00
Kevin Darby 01128639ce improve error logging 2019-04-04 13:05:40 -04:00
Eric Harding 71d2e01187 SPARK-2197 Add some MBO stats logged every 5 minutes 2019-04-04 11:18:15 -04:00
Eric Harding c923be6ced SPARK 2206 add test command to print out PIQ data for all orders 2019-04-03 16:24:53 -04:00
Michael Harding 4cf9d544aa Update PnL test 2019-04-03 09:44:10 -04:00
Michael Harding 79f37f7130 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2019-04-03 09:43:10 -04:00
Kevin Darby 19c0b94803 unsigned... 2019-04-02 18:13:31 -04:00
Kevin Darby 57fc50ce61 typo 2019-04-02 18:12:05 -04:00
Kevin Darby 3cff5343a3 PR review and support of -multiple 2019-04-02 18:10:53 -04:00
Kevin Darby a6ec93e87e Merge branch 'master' of bitbucket.org:bluetradesys/spark into stunnel 2019-04-02 16:08:36 -04:00
Kevin Darby 25acf771ab changes to conditionally start stunnel if the config file is present 2019-04-02 16:05:05 -04:00
Kevin Darby 22cc946f52 Merged in ice_clang (pull request #206)
changes to compile ice market data with clang
2019-04-02 20:04:09 +00:00
Kevin Darby 6d0eb62712 changes to compile ice work with clang 2019-04-02 15:55:29 -04:00
Eric Harding 369b89b3a7 Update default symbol list for compat with deployed config 2019-04-02 15:15:12 -04:00
Eric Harding 8793c6c527 Replace ManyConsole with CommandLineParser 2019-04-02 15:00:02 -04:00
Eric Harding df14522433 SPARK-2204 QueryServer api 2019-04-02 13:07:42 -04:00
Eric Harding 086e90acbe Move queryserver to linux only 2019-04-02 11:16:00 -04:00
Jake Norbut 16acc4f5b0 Mass cancel messaging nits 2019-04-02 09:42:20 -05:00
John Colvin b8a94f2540 Merged in miniob-crash (pull request #205)
fix crash in miniob

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-04-02 14:11:57 +00:00
John Colvin 3af7e561b9 fix crash in miniob 2019-04-02 09:01:53 -05:00
Michael Harding 467dba98f8 Update Fills window test 2019-04-01 17:06:16 -04:00
Pedro Pinto f871dfe142 Merged. 2019-04-01 14:48:40 -04:00
Pedro Pinto d44de204ee Merge remote-tracking branch 'origin/ice_md' 2019-04-01 14:34:32 -04:00
Pedro Pinto 04beea61eb Updated release notes. 2019-04-01 14:34:03 -04:00
Pedro Pinto 2181b92970 Merged 2019-04-01 14:25:48 -04:00
Pedro Pinto 693c730f51 Update release notes. 2019-04-01 14:24:44 -04:00
Kevin Darby 0ccba5bb0b Merged in fix_sim (pull request #202)
RTM: Fix ladder highlighting and aggressor marking in sim

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-04-01 18:24:09 +00:00
Pedro Pinto 3458be59d7 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2019-04-01 14:17:52 -04:00
Pedro Pinto 7dc9aa0710 Uppdate release notes. 2019-04-01 14:17:02 -04:00
Pedro Pinto 88d6dd0f00 Merge remote-tracking branch 'origin/SPARK-2162-notificationservice-cleanup' 2019-04-01 14:11:34 -04:00
Eric Harding ce4c46da04 Add some logging to spark shell shutdown sequence 2019-04-01 14:03:09 -04:00
Pedro Pinto 59c1f6a511 Updated release notes 2019-04-01 12:58:44 -04:00
Pedro Pinto 938caf9a4f Updated release notes. 2019-04-01 12:55:51 -04:00
Pedro Pinto df4f061b6c Merged 2019-04-01 12:47:38 -04:00
Jake Norbut 8afee21df7 hooking up the yes/no filter to the column 2019-04-01 11:21:02 -05:00
Pedro Pinto f34014eb25 Merged SPARK-2198 again 2019-04-01 12:11:58 -04:00
Eric Harding f513f7d9f2 SPARK-2204 mbo api in QueryServer 2019-04-01 12:11:03 -04:00
Jake Norbut b07eec08b4 Yes No formatting in row 2019-04-01 10:36:25 -05:00
Pedro Pinto 964274f5c4 Updated release notes. 2019-04-01 10:02:45 -04:00
Pedro Pinto c16941977c Merge remote-tracking branch 'origin/SPARK-2189' 2019-04-01 09:55:38 -04:00
Pedro Pinto 6b19119ab2 Updated release notes 2019-04-01 09:54:11 -04:00
Pedro Pinto ea42086e96 Merge branch 'SPARK-2198' of bitbucket.org:bluetradesys/spark 2019-04-01 09:39:05 -04:00
Pedro Pinto 292873b189 Update release notes. 2019-04-01 09:37:01 -04:00
Pedro Pinto 8545a5ef38 Merge branch 'SPARK-2150' 2019-04-01 09:30:03 -04:00
Pedro Pinto 6a52fff3d5 Updated release notes. 2019-04-01 09:28:33 -04:00
John Colvin 4ec6fafbc2 Merged in SPARK-2160-shell-windows-offscreen (pull request #195)
RTM: SPARK-2160 shell windows come up off screen sometimes

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-04-01 13:11:20 +00:00
Pedro Pinto b08e2c2ee6 Updated release notes. 2019-04-01 09:08:58 -04:00
Pedro Pinto 8bdd033501 Merge branch 'Documentation' 2019-04-01 09:04:51 -04:00
Taha Afzal 76466e83f0 changing hedger addiiton log from error to info 2019-03-29 17:26:36 -04:00
Eric Harding 69f9f2791e SPARK-2203 2019-03-29 15:39:22 -04:00
Eric Harding ad6fc6d23b Cannot have a const pointer returned to the pool 2019-03-29 15:38:54 -04:00
Eric Harding a4575e3fa3 Fix windows build 2019-03-29 15:38:26 -04:00
Kevin Darby 2540dc0510 ops notes 2019-03-29 15:20:35 -04:00
Eric Harding dc1a80afd2 Make MBOReader available to MDConsumerHandle 2019-03-29 14:07:48 -04:00
Eric Harding e1cfa7bf0b Fix build break 2019-03-29 14:06:25 -04:00
Eric Harding 2020221e65 MBOProducer cleanup and const correctness 2019-03-29 14:02:29 -04:00
Eric Harding ce438e93a6 Revert IntPrice regression (unit test failure) 2019-03-29 14:00:26 -04:00
Kevin Darby f0132c84b6 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_sim 2019-03-29 12:57:37 -04:00
Jake Norbut 57ca9596c8 changing column name 2019-03-29 11:05:15 -05:00
Eric Harding bad5bc3097 SPARK-2202 MBOProcessor producer side 2019-03-29 11:08:37 -04:00
Eric Harding e9da338402 optimize IntPrice lessthan 2019-03-29 10:58:16 -04:00
Jake Norbut bf4953f6dc adding TradedToday column to pnl 2019-03-29 09:45:32 -05:00
Jake Norbut c277fcd503 adding has_day_trades property 2019-03-29 09:44:55 -05:00
Jake Norbut dff57a43dd Adding TradedToday to Column enum 2019-03-29 09:43:53 -05:00
Jake Norbut 39c71d0c16 adding has day trades field to pnl's FilterState proto 2019-03-29 09:39:21 -05:00
Eric Harding be6c916691 Allow std::function to move into Dispatcher 2019-03-28 16:49:59 -04:00
Jake Norbut 009e72953c adding SecurityType default fitler; technical_debt++ 2019-03-28 15:31:56 -05:00
Jake Norbut 635163c895 hiding exch, start pnl, total net pos, and total net pnl by default 2019-03-28 15:03:54 -05:00
Jake Norbut 95e96a6afc remove pnl rows from which there is no net pos or day pos 2019-03-28 13:52:29 -05:00
Kevin Darby c7f0e2dd58 Merge branch 'master' of bitbucket.org:bluetradesys/spark into ice_md 2019-03-26 18:43:38 -04:00
Jake Norbut f5e79a65a3 relocating comment after PR edit 2019-03-26 11:17:37 -05:00
Jake Norbut c958f4bf1a PR suggestion - making row_vm -> communication a bit more flexibile 2019-03-26 11:15:15 -05:00
Eric Harding 95939a5534 SPARK-2087 add GE to default symbol list 2019-03-25 10:22:52 -04:00
Kevin Darby 9a7164b42b Merge branch 'ice_md' of bitbucket.org:bluetradesys/spark into ice_md 2019-03-25 10:10:04 -04:00
Kevin Darby f347fda47f PR details 2019-03-25 10:09:37 -04:00
Kevin Darby d75ab3a63f default to no ice symbols 2019-03-22 23:12:24 -04:00
Kevin Darby b30f59227e note to reference jetfix on the precision 2019-03-22 23:11:01 -04:00
Kevin Darby ef60d4200a Fix ladder highlighting and aggressor marking in sim 2019-03-22 17:56:19 -04:00
Jake Norbut ed6f0b8bdb Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-2150 2019-03-22 15:17:21 -05:00
Jake Norbut 766116eac4 adding confirmation to OB cancel buys and cancel sells 2019-03-22 09:33:49 -05:00
Jake Norbut 8d49de3f61 simplying boolean comparison 2019-03-21 14:31:59 -05:00
Jake Norbut 16cf4554c5 refactoring into a much simpler implementation 2019-03-21 14:24:32 -05:00
Jake Norbut 6b17efee60 Converting cancel all command to new SparkConfirmationCommandParamObserver 2019-03-21 13:44:17 -05:00
Jake Norbut 5d64213455 Adding new SparkConfirmationCommandParamObserver, which adds a confirmation func to SparkCommandParamObserver 2019-03-21 13:40:21 -05:00
John Colvin 0ef44e92aa SPARK-2160 pr edit: refer to 'source_window_rect' for width/height params instead of window (easy way to avoid extra dp lookups) 2019-03-20 10:46:47 -05:00
Eric Harding 4d60376cb1 SPARK-2179 Spark login screen should show error message instead of blank 2019-03-20 10:43:46 -04:00
Kevin Darby ee2f76553b tests pass 2019-03-19 17:54:37 -04:00
Kevin Darby c1887b9786 launch.json 2019-03-19 15:28:11 -04:00
Kevin Darby 1fd5e51b9e reenabled cme, now we can get both ice and cme 2019-03-19 15:23:40 -04:00
John Colvin 56d3a27e7f SPARK-2162 / SPARK-2164 INotificationService - remove fake strongly typed 'link<T>' extension method 2019-03-19 13:12:04 -05:00
John Colvin 1c65b1d87a rename generic type arg 'TPredicateBuilder' -> 'TPredicateData' 2019-03-19 13:00:25 -05:00
John Colvin 693910f093 SPARK-2162 / SPARK-2163 GridSettingsManager/VM - replace IPredicateBuilder dependency with predicate-data factory(delegate) 2019-03-19 12:58:02 -05:00
Kevin Darby 498b38ebcb Merge branch 'master' of bitbucket.org:bluetradesys/spark into ice_md 2019-03-18 16:00:59 -04:00
John Colvin fb91206e0f pr edit: get window dimensions with window.ActualWidth/Height 2019-03-18 13:56:17 -05:00
John Colvin 91f4de00f8 pr edit: should not have changed while-loop to if-condition 2019-03-18 13:54:01 -05:00
John Colvin 28980f10ae SPARK-2160 shell windows come up off screen sometimes 2019-03-18 13:41:29 -05:00
Michael Harding 04e006b812 Add new Spreader and Algo studio videos to the manual 2019-03-15 10:44:58 -04:00
Jake Norbut 02ed327ec7 clarifying tests 2019-03-12 16:20:46 -05:00
Jake Norbut e9162263d6 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2019-03-12 16:14:03 -05:00
Jake Norbut 3b3cc9a51c clarifying test 2019-03-12 16:13:51 -05:00
Jake Norbut 5b959ca5bd OB window command update when selected row's row vm changes 2019-03-12 14:39:25 -05:00
John Colvin 54f03c4a03 Merged in SPARK-2107-sound-triggers (pull request #193)
SPARK-2107 QA: settings/SoundAndNotificationsTest.txt
2019-03-12 17:58:36 +00:00
Pedro Pinto c4d297969b Improved tests. 2019-03-12 13:54:12 -04:00
John Colvin 1f86456437 SPARK-2107 QA: settings/SoundAndNotificationsTest.txt 2019-03-12 12:50:32 -05:00
Pedro Pinto e24e04ca4f Fixed OrderManager reentrancy problem reported by Taha 2019-03-12 10:01:57 -04:00
John Colvin 08a1e07ab6 Merged in SPARK-2098-spreader-orders-missing-security (pull request #192)
RTM SPARK-2098 security is not assigned to SpreaderOrderDefinition when sending orders
2019-03-11 23:57:58 +00:00
John Colvin 038e6d16af SPARK-2098 security is not assigned to SpreaderOrderDefinition when sending orders 2019-03-11 16:59:18 -05:00
Eric Harding fe2e1e9945 Merge branch 'AdditionalTracing-MinimumInterval' of bitbucket.org:bluetradesys/spark 2019-03-11 17:00:30 -04:00
Eric Harding 20d5276874 Remove trace::log_scope function and just use constructor 2019-03-11 16:53:12 -04:00
Eric Harding b2f3d093d2 Merged in AdditionalTracing-MinimumInterval (pull request #191)
RTM: cut down on tracing load by supplying a minimum interval
2019-03-11 20:28:05 +00:00
John Colvin 4860f706ad Merged in SPARK-2097-change-cover/lean-defaults (pull request #190)
SPARK-2097 change Lean/Cover defaults - Lean.MaxChaseTicks = 2, Cover.HedgeTicks = 1

Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
2019-03-11 20:24:06 +00:00
Eric Harding 29ddd6621c Move NativeOrder::on_activate trace message so it doesn't get triggered by warmup 2019-03-11 16:11:21 -04:00
Eric Harding e8e0f01fb7 Merged master into AdditionalTracing-MinimumInterval 2019-03-11 14:16:09 -04:00
Eric Harding 41a04de31b Update threshhold with help from compiler 2019-03-11 14:14:51 -04:00
John Colvin b3b1af68d0 SPARK-2097 change Lean/Cover defaults - Lean.MaxChaseTicks = 2, Cover.HedgeTicks = 1 2019-03-11 12:23:49 -05:00
Pedro Pinto 7cbf729607 Cleaned up release notes further. 2019-03-11 08:52:35 -04:00
Pedro Pinto 6e8851d92c Updated subrepo pointers 2019-03-11 08:51:31 -04:00
Pedro Pinto 460e747f55 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2019-03-11 08:49:24 -04:00
Pedro Pinto 20978b10d4 Updated release notes 2019-03-11 08:49:19 -04:00
John Colvin 42e0cfba70 Merged in SPARK-2079-trigger-cleanup (pull request #183)
RTM - SPARK-2079 trigger cleanup
2019-03-11 12:48:44 +00:00
Michael Harding 728c485013 Merged in doc_temp_build (pull request #174)
RTM: Doc temp build
2019-03-11 12:47:40 +00:00
Eric Harding b83f88e098 Merged in AlmostNeverRecordSim (pull request #185)
RTM: Almost never want to record sim market data
2019-03-11 12:46:55 +00:00
Eric Harding 32b1a38655 Merged in DenyCopy (pull request #177)
RTM: Explicitly deny copying a service.  make_*_publisher points back to the owning service
2019-03-11 12:44:49 +00:00
Taha Afzal 24934f6607 Merged in SPARK-2058-netchange (pull request #184)
RTM SPARK-2058 netchange
2019-03-11 12:43:56 +00:00
Brian Muhr 90b56277ca Merged in Documentation (pull request #179)
RTM: Documentation - added Algo Studio, new images, freshened up quite a bit

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
2019-03-11 12:42:51 +00:00
Eric Harding 20ca8a7ffe Merged in AdditionalTracing (pull request #189)
RTM: Additional Tracing

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-03-11 12:40:57 +00:00
Pedro Pinto 6ed4adbc14 Merged in fix_warmup_limit_polution (pull request #187)
RTM. Fixed order warm up code to clear errors detected during warmup
2019-03-11 12:38:05 +00:00
Eric Harding 9117e3e238 Add a minimum tracing interval for scoped logs 2019-03-11 07:58:43 -04:00
Eric Harding c0f079b7f6 Code review feedback 2019-03-11 07:31:03 -04:00
John Colvin 751f22645d SPARK-2089 [cherry-picked] Strange null ref exception in OrderBook row-selection changed callback during application shutdown 2019-03-10 22:21:53 -05:00
John Colvin 47daf439d0 OrderPropertiesEditor - observe_order_type() check if templates are enabled 2019-03-10 21:41:44 -05:00
Eric Harding 19ce3a7dd4 Additional spreader tracing 2019-03-10 16:19:15 -04:00
John Colvin 5686b57d47 ObservableAutoWrapper - fix typeo (change 'Reset' to 'Remove') 2019-03-10 14:38:06 -05:00
John Colvin cb456a439c fix typeo in LazySubscription 2019-03-10 14:33:42 -05:00
Kevin Darby 7ac54242e0 Merge branch 'master' of bitbucket.org:bluetradesys/spark into ice_md 2019-03-08 16:19:04 -05:00
Kevin Darby 1ebf8219c2 ice descriptions in mview 2019-03-08 16:17:53 -05:00
Kevin Darby f0b14cb8dc forgot to init new string fields 2019-03-08 16:10:02 -05:00
Kevin Darby 7259f81ab5 forgot to init new string fields 2019-03-08 16:03:49 -05:00
Kevin Darby 3b902b61dd ice spreads 2019-03-08 15:55:10 -05:00
Kevin Darby 6cfef51e3d subrepo 2019-03-07 18:12:39 -05:00
Kevin Darby 76edb94905 ticks.... 2019-03-07 18:10:11 -05:00
Kevin Darby e4bf9fc747 ice market data 2019-03-07 17:56:28 -05:00
Kevin Darby d488ff23ba I misunderstood how onixs wants to do the ice threading. fixed 2019-03-07 11:38:38 -05:00
Pedro Pinto c1d4b9cd9f Fixed order warm up code to clear errors detected during warmup
This could cause later orders to fail even if the error condition no
longer obained.
2019-03-07 11:23:57 -05:00
Pedro Pinto dce856e4af Fixed duplication of entries in event_log_archive in perf_log 2019-03-07 11:04:11 -05:00
Kevin Darby 6b6fd9c40a more ice 2019-03-06 14:58:09 -05:00
Kevin Darby f7265740e7 ICE securities, from the actual ICE 2019-03-05 18:08:20 -05:00
Jake Norbut f006d4ffdb Merge branch 'SPARK-2054' of bitbucket.org:bluetradesys/spark into SPARK-2058-netchange 2019-03-05 14:21:35 -06:00
John Colvin 31dd7849b9 SPARK-2086 SyntheticEditor - user can't type '.' in price multiplier cell 2019-03-05 14:17:19 -06:00
Jake Norbut bcf9b97420 Merge branch 'SPARK-2054' of bitbucket.org:bluetradesys/spark into SPARK-2058-netchange 2019-03-05 12:56:08 -06:00
John Colvin 697ffd71eb ObservableAutoWrapper.connect_item_actions - invoke action for existing items 2019-03-04 23:52:22 -06:00
Jake Norbut b56fc2445c != instead of !(); John leaves no stone unturned 2019-03-04 17:05:13 -06:00
Jake Norbut a4d162c3a2 EnumHelper equality be gone 2019-03-04 16:53:08 -06:00
Jake Norbut 527bc05b6f removing comment cruft 2019-03-04 15:49:17 -06:00
Jake Norbut a52d65859b 3 bools to 1 Enum in the protobuf 2019-03-04 15:45:30 -06:00
Jake Norbut 363b9e3e7a I missed an early return branch in _format_tick 2019-03-04 15:31:21 -06:00
Eric Harding 3a06684146 Almost never want to record sim market data 2019-03-04 16:15:53 -05:00
John Colvin 8643feabf2 SPARK-2079 [pr edit]child algo 'name' overlapped expander button 2019-03-04 13:44:50 -06:00
John Colvin e7f1c59210 SPARK-2079 [pr edit]remove #define ALLOW_FIXED_PRICE_TRIGGER 2019-03-04 13:27:44 -06:00
Jake Norbut c9f403973b Adding a ColSpan on the groupbox so it doesn't push over the merged combo box 2019-03-04 10:53:49 -06:00
Jake Norbut d6bc6fa289 simplifying logic regarding decision to format with decimal part 2019-03-04 10:02:15 -06:00
Taha Afzal 7557ca38cb Merge branch 'SPARK-2054' into SPARK-2058-netchange 2019-03-04 09:03:16 -05:00
Jake Norbut ef59511a01 woops, fixing bug 2019-03-04 07:50:10 -06:00
Jake Norbut 8dfbb106a6 hide decimals when in ticks mode 2019-03-04 07:46:59 -06:00
Taha Afzal 3e9a931c5f removing an unused function 2019-03-03 23:15:06 -05:00
Taha Afzal 71fac664e8 the net change test now tests for quoting prices on both legs 2019-03-03 16:52:29 -05:00
Taha Afzal 1b39ffc323 cleaning up the test 2019-03-03 16:30:19 -05:00
John Colvin bc69c669ae SPARK-2079 Trigger Clean up (simplify) 2019-03-02 21:50:17 -06:00
Taha Afzal 170f7426aa for net change, the reported price with filled_qty==0 was incorrect 2019-03-02 22:43:35 -05:00
John Colvin 16d30e450f refactor\cleanup OrderTypeVMBase and impls 2019-03-02 19:36:19 -06:00
Taha Afzal a3787ee65a Merge branch 'SPARK-2054' into SPARK-2058-netchange 2019-03-01 16:59:42 -05:00
Taha Afzal 14d5d36067 merging master 2019-03-01 16:57:26 -05:00
Jake Norbut 1af4b97b97 ui feedback from taha 2019-03-01 12:56:41 -06:00
Michael Harding faf60af9c3 Merge branch 'master' into doc_temp_build 2019-03-01 13:52:48 -05:00
John Colvin 109fee099f Merged in SPARK-2082 (pull request #181)
SPARK-2082 mview - canceled orders appear to be working

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-03-01 18:01:31 +00:00
John Colvin 747b44c48d fix strange line ending change in last commit to _index.md 2019-03-01 11:59:01 -06:00
John Colvin 4b091a1259 SPARK-2082 release note 2019-03-01 11:50:56 -06:00
Jake Norbut 9626bee38e resetting this width I inadvertently changed 2019-03-01 10:55:52 -06:00
Jake Norbut 776bc71c22 ladder prices in net change terms 2019-03-01 10:51:16 -06:00
John Colvin 7c0e310f6a SPARK-2082 mview - canceled orders appear to be working 2019-03-01 10:13:49 -06:00
Michael Harding 761657b56b Fix typos 2019-03-01 10:30:41 -05:00
Michael Harding 2988967f1f Merge branch 'master' into doc_temp_build 2019-03-01 09:29:36 -05:00
Kevin Darby 724da31316 adding data files 2019-02-28 17:50:46 -05:00
Pete Fisher f7309c389e _index.md edited online with Bitbucket 2019-02-28 21:53:15 +00:00
Pedro Pinto df1dbd76b5 Merge branch 'fix_cache_warm_logs' 2019-02-28 15:58:51 -05:00
Pedro Pinto deaf8b6ad5 Somehow did not push this in. 2019-02-28 15:58:11 -05:00
Kevin Darby 2ae5b50445 ice builds with .so 2019-02-28 15:01:35 -05:00
Kevin Darby dfcf38289f move externals up for ice .so 2019-02-28 14:44:13 -05:00
Pedro Pinto 5e0edd00f4 Merged in fix_cache_warm_logs (pull request #180)
Avoided logging anything when warming up order manager cache.

Approved-by: Taha Afzal <taha.afzal@bluetradesys.com>
2019-02-28 19:20:06 +00:00
Pedro Pinto f25a667ff0 Fixed transition log msg when calling warm cache. 2019-02-28 14:16:38 -05:00
Pedro Pinto 4f3eaa8b9c Updated release notes. 2019-02-28 13:43:41 -05:00
Pedro Pinto 5098767363 Avoided logging anything when warming up order manager cache. 2019-02-28 13:32:43 -05:00
Michael Harding 09436473ed Merged master into Documentation 2019-02-28 13:27:25 -05:00
Michael Harding 5524e89a10 Add images 2019-02-28 13:26:02 -05:00
Taha Afzal d615dcc7d6 tests were failing because of direct vs merged 2019-02-28 11:10:56 -05:00
Taha Afzal d582ed8436 more tests 2019-02-27 19:29:18 -05:00
Taha Afzal c5768c4022 randomized the prices and markets in the net change test 2019-02-27 19:08:29 -05:00
Kevin Darby 1c54b0140b ready to test, but we need to solve a link issue 2019-02-27 16:41:38 -05:00
John Colvin 4d52de3f09 Merged in SPARK-2076 (pull request #178)
SPARK-2076 Sounds crash (Dan 1.5)

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-02-27 20:48:54 +00:00
John Colvin 9864d18a60 SPARK-2076 [pr edit] log error instead of throwing an exception (when unable to link predicate). 2019-02-27 14:37:53 -06:00
Brian Muhr 6c652df3eb index.md edited online with Bitbucket 2019-02-27 20:34:00 +00:00
Michael Harding c4e684f934 SPARK-2047: change temp dir path 2019-02-27 14:41:43 -05:00
John Colvin 1590c6d6f6 SPARK-2076 Sounds crash (Dan 1.5) 2019-02-27 13:37:50 -06:00
Taha Afzal 51ac39c166 test comments 2019-02-27 14:21:41 -05:00
Eric Harding f04a48d4f8 Explicitly deny copying a service. make_*_publisher points back to the owning service 2019-02-27 14:00:04 -05:00
Michael Harding f7baf6c05a Merge branch 'master' into doc_temp_build 2019-02-27 13:39:11 -05:00
Taha Afzal 1eaf3d8629 taming the net_change tests 2019-02-27 13:16:31 -05:00
Brian Muhr 62ecd1a721 index.md edited online with Bitbucket 2019-02-27 16:35:51 +00:00
Kevin Darby e034fd2623 Merge branch 'master' of bitbucket.org:bluetradesys/spark into ice_md 2019-02-26 18:23:23 -05:00
Kevin Darby a3b8d579a1 hooked up all ice callbacks except books 2019-02-26 18:20:22 -05:00
Taha Afzal cabc84a577 tests for net change transformation 2019-02-26 17:04:59 -05:00
Kevin Darby 9d2cdb702d getting somewhere 2019-02-26 16:00:26 -05:00
Taha Afzal 394c7fd954 Merge branch 'SPARK-2058-netchange' of bitbucket.org:bluetradesys/spark into SPARK-2058-netchange 2019-02-26 10:52:00 -05:00
Taha Afzal b2d1969e9f ticks in net change working now, by transforming the spread 2019-02-25 17:24:31 -05:00
Eric Harding 744797b91c Add compute_synthetic_book function 2019-02-25 14:26:23 -05:00
Michael Harding 6446e38aea Merge branch 'master' into doc_temp_build 2019-02-25 11:48:36 -05:00
Michael Harding 32df88914b Merge branch 'master' into doc_temp_build 2019-02-25 11:29:21 -05:00
Michael Harding 628490ac2d Hide JS errors in IE 2019-02-25 11:10:19 -05:00
Michael Harding ef475f7003 Merge branch 'master' into doc_temp_build 2019-02-25 11:09:41 -05:00
Kevin Darby d0784feddd more ice 2019-02-25 10:34:58 -05:00
Taha Afzal 76a1367510 Price calc (standard vs net change) is a property on LegPropertyData 2019-02-24 20:21:21 -05:00
Taha Afzal fef5ae4aff Adhering spreader to bts::api::SyntheticContract::PriceCalc (from the old one) 2019-02-23 23:59:09 -05:00
Taha Afzal 2c9e83c6f0 Merge branch 'SPARK-2058-netchange' of bitbucket.org:bluetradesys/spark into SPARK-2058-netchange 2019-02-22 17:50:10 -05:00
Taha Afzal 35738c25f1 dividing by tick_size for net change 2019-02-22 17:50:02 -05:00
Eric Harding e64567f8ad SPARK-2062 Unify NetChange enums in UI 2019-02-22 17:15:52 -05:00
Michael Harding 5865ab21bc Downgrade js to es3 2019-02-22 17:05:11 -05:00
Eric Harding 78b6169750 Update SyntheticContract.PriceCalc enum values 2019-02-22 12:24:01 -05:00
Eric Harding e2c0d81f19 Update NetChange enums to be per security rather than per leg. Also non mutable 2019-02-22 11:59:51 -05:00
Eric Harding b811b960ec SPARK-2063 Ladder day change doesn't update as often as I'd like 2019-02-22 09:39:15 -05:00
Eric Harding bf0507e434 Fix synthetic_net_change unit test 2019-02-22 08:37:21 -05:00
John Colvin 1205a35b69 SyntheticEditorVM - add 'MarketDataType' param-row back (accidentally removed) 2019-02-21 19:53:18 -06:00
John Colvin 2f4d2a9c0d SPARK-2058 / SPARK-2062 Synthetic NetChange should be set per Synthetic not per Leg (UI Only) 2019-02-21 19:43:40 -06:00
John Colvin 08e5088d72 SPARK-2058 / SPARK-2059 Allow 100/100 tick size 2019-02-21 18:40:08 -06:00
Eric Harding e7b371aa6f SPARK-2060 NetChange in Ticks 2019-02-21 12:49:55 -05:00
Eric Harding 3094eee327 Add double division operator to IntPrice 2019-02-21 12:00:01 -05:00
Michael Harding 80ff61c9b2 Docs: change URL in config.toml 2019-02-20 09:40:27 -05:00
Michael Harding 2a42c1fab0 SPARK-2047: Build user docs in a temp dir 2019-02-19 16:38:17 -05:00
Kevin Darby 4a64e45d36 translated onixs::Handler settings to our config. 2019-02-15 18:08:26 -05:00
Kevin Darby 97f09768bb got the boilerplate up, now just need to get the right ice handlers and options in there 2019-02-14 17:55:34 -05:00
Kevin Darby 2b4add85a2 kinda copy / pastey stuff from cme 2019-02-12 13:00:47 -05:00
Kevin Darby 4c798f6db4 initial push 2019-02-11 16:14:41 -05:00
Kevin Darby b2e275124f update externals with onixs ice docs 2019-02-11 13:35:14 -05:00
Kevin Darby 36e4f62a53 moved externals pointer forward to capture onixs ice addition 2019-02-11 12:38:00 -05:00
293 changed files with 9987 additions and 1883 deletions

No files matched your search

+10 -4
View File
@@ -75,6 +75,7 @@
"name": "SparkVM Launch",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
"args": ["run"],
"stopAtEntry": false,
@@ -96,7 +97,12 @@
"description": "Skip STL",
"text": "skip -gfi /usr/include/c++/7/bits/*.h",
"ignoreFailures": true
}
},
{
"description": "LD LIBRARY",
"text": "set env LD_LIBRARY_PATH=.",
"ignoreFailures": false
}
],
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
@@ -208,10 +214,10 @@
"name": "MD Tests",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test", "[md]"],
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
"args": ["test", "[mbo]"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"cwd": "/home/bts/spark/build/ninja_debug/server/",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
+2
View File
@@ -55,6 +55,8 @@ else ()
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/web ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/Release/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.13.0.0 ${CMAKE_BINARY_DIR}/server
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/
COMMENT "Creating server runtime layout"
+1 -1
View File
@@ -1 +1 @@
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;/home/ppinto/Dropbox/bts/spark/doc/user/mkdocs.yml;bts-help-theme/404.html;bts-help-theme/base.html;bts-help-theme/content.html;bts-help-theme/css/base.css;bts-help-theme/css/bts.css;bts-help-theme/css/font-awesome.min.css;bts-help-theme/css/highlight.css;bts-help-theme/css/local.css;bts-help-theme/fonts/fontawesome-webfont.eot;bts-help-theme/fonts/glyphicons-halflings-regular.eot;bts-help-theme/img/favicon.ico;bts-help-theme/js/base.js;bts-help-theme/js/bootstrap-3.0.3.min.js;bts-help-theme/js/highlight.pack.js;bts-help-theme/js/jquery-1.10.2.min.js;bts-help-theme/keyboard-modal.html;bts-help-theme/main.html;bts-help-theme/nav-sub.html;bts-help-theme/nav.html;bts-help-theme/search-modal.html;bts-help-theme/toc.html;docs/Help.md;docs/Launcher.md;docs/Logging-In-to-the-System.md;docs/Market.md;docs/My-Windows.md;docs/Settings.md;docs/Spreader.md;docs/Trading.md;docs/images/image1.png;docs/images/image10.png;docs/images/image11.png;docs/images/image12.png;docs/images/image13.png;docs/images/image14.png;docs/images/image15.png;docs/images/image16.png;docs/images/image17.png;docs/images/image18.png;docs/images/image19.png;docs/images/image2.png;docs/images/image20.png;docs/images/image21.png;docs/images/image22.png;docs/images/image23.png;docs/images/image24.png;docs/images/image25.png;docs/images/image26.png;docs/images/image27.png;docs/images/image28.png;docs/images/image29.png;docs/images/image3.png;docs/images/image30.png;docs/images/image31.png;docs/images/image32.png;docs/images/image33.png;docs/images/image34.png;docs/images/image35.png;docs/images/image36.png;docs/images/image37.png;docs/images/image38.png;docs/images/image39.png;docs/images/image4.png;docs/images/image40.png;docs/images/image41.png;docs/images/image42.png;docs/images/image43.png;docs/images/image44.png;docs/images/image45.png;docs/images/image46.png;docs/images/image47.png;docs/images/image48.png;docs/images/image49.png;docs/images/image5.png;docs/images/image50.png;docs/images/image51.png;docs/images/image52.png;docs/images/image53.png;docs/images/image54.png;docs/images/image55.png;docs/images/image56.png;docs/images/image57.png;docs/images/image58.png;docs/images/image59.png;docs/images/image6.png;docs/images/image60.png;docs/images/image61.png;docs/images/image62.png;docs/images/image63.png;docs/images/image64.png;docs/images/image65.png;docs/images/image66.png;docs/images/image67.png;docs/images/image68.png;docs/images/image69.png;docs/images/image7.png;docs/images/image70.png;docs/images/image71.png;docs/images/image72.png;docs/images/image73.png;docs/images/image74.png;docs/images/image76.png;docs/images/image8.png;docs/images/image9.png;docs/index.md;docs/release_notes.md)
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Help/index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Launcher/index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Logging-In-to-the-System/index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/index.md;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/My-Windows/index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Settings/index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Spreader/index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/index.md;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/bts_spark_sdk/Client/Overview.md;content/bts_spark_sdk/Client/Threading.md;content/bts_spark_sdk/Client/_index.md;content/bts_spark_sdk/Server/_index.md;content/bts_spark_sdk/_index.md;content/release_notes/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
+1 -1
View File
@@ -22,7 +22,7 @@ update_deps_file("${All_Deps}")
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/web/docs/index.html
COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/build.sh
ARGS ${CMAKE_BINARY_DIR}/web/docs/
ARGS ${CMAKE_BINARY_DIR}/web/docs/ ${CMAKE_BINARY_DIR}
COMMENT "Building Manual UI"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${All_Deps})
+15 -3
View File
@@ -1,8 +1,20 @@
#!/bin/bash
tmpfile=$(mktemp /tmp/quiet_build_log.XXXX)
cp -r ../../src/sdk/doc/* ./content/bts_spark_sdk/Server/
./run_zola.sh build -u '/docs/' -o $1 >$tmpfile 2>&1
rm ./content/bts_spark_sdk/Server/algos.md
cur_dir=$(pwd)
temp_path="$2/web_temp"
zola_path=$(readlink -f "../../externals/linux/bin/zola/zola")
mkdir -p $temp_path
cp -r ./* $temp_path
cp -r ../../src/sdk/doc/* $temp_path/content/bts_spark_sdk/Server/
cd $temp_path
$zola_path build -u '/docs/' -o $1 >$tmpfile 2>&1
cd $cur_dir
rm -r $temp_path
if [ $? -eq 0 ]
then
rm $tmpfile
+2 -2
View File
@@ -1,5 +1,5 @@
# The URL the site will be built for
base_url = "https://www.bluetradingsystems.com/docs"
base_url = "https://www.bluetradingsystems.com/documentation"
title = "BTS Spark Documentation"
@@ -15,4 +15,4 @@ build_search_index = true
[extra]
# Put all your custom variables here
book_numbered_chapters = false
book_numbered_chapters = false
@@ -0,0 +1,46 @@
+++
title = "BTS Basic Algos"
weight = 10
sort_by = "weight"
insert_anchor_links = "right"
+++
## 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
##### Parameters
* Cross Thresh - size thresh in absolute or ratio terms
* Max Chase Ticks - max levels to pay up, in ticks
## Join
The goal of the join order is to watch the opposite-inside market and join when the market flips (that is, when the best offer becomes the best bid) and has at least threshold size.
##### Parameters
* Join Thresh - size thresh in absolute or ratio terms
## Top
The goal of the top order is to not trade by yourself, but the intent at the time of order entry is to get filled.If the order is on the market (say current bid price) AND the quantity on that bid price is below top thresh then cancel the order else remain working.
##### Parameters
* Top Thresh - size thresh in absolute or ratio terms
## Cover
A Cover order’s purpose is to fire a hedge order (of any type) in response to fills from it’s child order on the opposite side of the market N ticks away
##### Parameters
* Profit Ticks - ticks above or below fill price to place profit / cover order
## OCO
An OCO order creates a relationship between a child order and a 'stop' or 'other' order such that when one of these is cancelled, both cancel, or when one is filled, the other order's size is debited in the amount of the fill.
Composing this order with Cover simulates scalping functionality
##### Parameters
* Stop Ticks - ticks below or above main order to place stop / other order
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

+1 -1
View File
@@ -25,7 +25,7 @@ insert_anchor_links = "right"
which will list what version you are running as well as who is
currently logged in.
![](image32.png)
![](about_spark.png)
## Activity log
This is an overall log viewer which will show
Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

+20 -16
View File
@@ -51,7 +51,7 @@ insert_anchor_links = "right"
securities you have chosen, in this example, it appears like the
following:
![](image63.png)
![](market_view.png)
- Once a market view is launched and you are viewing data, you can
customize the view further by the following:
@@ -88,31 +88,34 @@ insert_anchor_links = "right"
- Change securities
- Click on the settings icon ![](image12.png)which will launch the market view security selection.  You can add or remove Securities you have already selected and are shown in the market view.
- Click on the settings icon ![](change_securities.png) which will launch the market view security selection.  You can add or remove Securities you have already selected and are shown in the market view.
- Launch an order ticket
- This is done 1 of 2 ways:
- This is done 1 of 3 ways:
- Single left clicking on either the Bid Qty, Bid Price, Ask Price,
or Ask Qty which will bring up an order ticket pre-populated with
either the Bid or Ask price and volume.
- Highlighting a single security and clicking on the order ticket
![](image23.png) icon
on the left-hand browser window of the market view
- Highlighting a single security and right clicking bringing up the
menu options to launch an order ticket.
- Launch a ladder for 1 security
- You can double-click on any security in the market view, besides
the Bid Qty, Bid Price, Ask Price, or Ask Qty columns to launch a
ladder for that Security.
- This is done 1 of 2 ways:
- You can double-click on any security in the market view, besides
the Bid Qty, Bid Price, Ask Price, or Ask Qty columns to launch a
ladder for that Security.
- Highlighting a single security and right clicking bringing up the
menu options to launch a ladder.
- Launching multiple ladders
- If you hold down the Ctrl key and select multiple securities, you
can then click on the ladder icon
![](image69.png) on
the left-hand side to bring up the selected ladders all at
once.
- This is done 1 of 2 ways:
- If you hold down the Ctrl key and select multiple securities, you
can then click on the ladder icon on the left hand side to bring
up the selected ladders all at once.
- Highlighting multiple securities and right clicking to bring up
the menu options to launch a ladder.
- Launching a Fills window for 1 or many securities
@@ -158,8 +161,9 @@ insert_anchor_links = "right"
- On launching a security browser will appear, allowing you to choose
the products you would like to view.
![](image30.png)
![](time_and_sales.png)
- From time and sales, you can launch the same items as the market
view with the exception of a launching the synthetic window to
create a synthetic product
create a synthetic product
- From the settings icon ![](settings_icon.png) you can customize the time and sales modifying grid; the colors of the aggressor, status of trades coming in, as well as font sizes and which columns to see.
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

@@ -5,6 +5,7 @@ sort_by = "weight"
insert_anchor_links = "right"
+++
{{ youtube(id="RZU9Wj0cETo") }}
<div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/XEZpuPAgMC0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

+139 -40
View File
@@ -59,7 +59,7 @@ insert_anchor_links = "right"
functionality to quickly perform various tasks with live orders as
well as launch other modules.  It appears like the below:
- ![](image4.png)
![](spark_menu_order_book.png)
- ![](image48.png)Fills -
clicking on this will launch a fills window
@@ -79,6 +79,18 @@ insert_anchor_links = "right"
- ![](image2.png)Cancel
all - clicking on this will cancel all working orders you have the
order book
- ![](recreate_icon.png) Recreate - clicking this will recreate already cancelled orders, sending them back to market with the same price and quantity as they were previously at before being cancelled.
- ![](collapse_all_icon.png) Collapse all - This will collapse all trees inside the order book to show only the top level of the order.
- ![](expand_all_icon.png) Expand all - This will expand to show everything inside the order book.
- ![](settings_icon.png) Settings - This launches the settings for the orderbook.
- Furthermore, there is a “floating” or “mini orderbook” feature as well
integrated within the ladder itself.
- To access this, you need to center click with your mouse wheel over
working orders on the ladder.
- From here, you can change the price and quantity of any working order
within the mini orderbook straight from the window
- This looks like below:
![](mini_orderbook.png)
## Fills
@@ -89,6 +101,8 @@ insert_anchor_links = "right"
like
- All fill windows are brought up as tabs and can be combined with
all other windows within BTS
- There is also a settings button in the left hand tab which can be
launched to customize the fills window
## P&amp;L
@@ -104,10 +118,20 @@ insert_anchor_links = "right"
other windows within BTS
- All columns are sortable and either searchable or can be further
filtered by entering in min and max values.
- Below is an example P&L window showing a user’s multiple accounts
and various positions in each:
- There is also a settings button in the left hand tab which can be
launched to customize the P&amp;L window
- In regards to the PnL specifically, you can choose how you want the
PnL calculated based on Last, MidPoint, or Settle price.
- This is accomplished on the top bar, which when clicked on, displays
the choices for calculation, this appears like below:
![](image67.png)
![](pnl_calculation.png)
- Below is an example P&L window showing a user’s multiple accounts and
various positions in each:
![](pnl_window.png)
## Ladder
@@ -125,7 +149,7 @@ insert_anchor_links = "right"
market with a single click
- Below is an example ladder:
![](image36.png)
![](ladder.png)
- The ladder consists of 2 major parts, the Ladder itself and the
information and control section on the left-hand side
@@ -213,37 +237,59 @@ insert_anchor_links = "right"
         
![](image50.png)
- Lean
- This is a specialized algo order which allows the user to
- Lean
- This is a specialized algo order which allows the user to
essentially watch the opposite-inside market size and cross when
that size goes below the threshold defined.
- “Crossing” means to take an order working on the bid and 'cross' by
buying into the asks.  This is often referred to as “paying up” as
well
- “Cross” means to take an order working on the bid and 'cross' by
buying into the asks.  This is often referred to as “paying up” as well
![](image22.png?v2)
![](lean_algo_control.png)
- Join
- This is a specialized algo order which allows the user to enter an
- Join
- This is a specialized algo order which allows the user to enter an
order which will watch the opposite-inside market and join when the
market flips and has at least threshold size as defined.
- “Flip” refers to the event where the best offer becomes the best
bid or the market trades away from you, i.e. the line moves up in
the GUI.  The reverse is true if selling.
![](image41.png)
![](join_algo_control.png)
- Top
- This is a specialized algo order which allows the user to
- Top
- This is a specialized algo order which allows the user to
essentially not trade by themselves
- If the order is on the market (current bid price) and the quantity
- If the order is on the market (current bid price) and the quantity
on that bid price is below the top threshold defined the order will
cancel and do nothing else
![](top_algo_control.png)
![](image26.png)
- Offset
- The goal of the offset order is to send your order X amount of ticks
from where you actually place it on the ladder. This is sometimes
known as a "fat finger" safety.
![](offset_algo_control.png)
- OCO
- An OCO order creates a relationship between a child order and a 'stop'
or 'other' order such that when one of these is cancelled, both cancel,
or when one is filled, the other order's size is debited in the amount
of the fill. Composing this order with Cover simulates scalping
functionality.
![](oco_algo_control.png)
- Cover
- A Cover order’s purpose is to fire a hedge order (of any algo-type) in response to fills from it’s child order on the opposite side of the market N ticks away.
![](cover_algo_control.png)
- Trigger
- The Trigger order is exactly as it sounds, where it will trigger an order based on conditions defined.
- This order also has exit strategy built in where a user can define when the trigger order can auto-cancel based on time as well as price conditional.
![](trigger_algo_control.png)
- The middle of the control section is where you can click on
predefined values to quickly populate the volume at which you wish
@@ -264,20 +310,36 @@ insert_anchor_links = "right"
-  The bottom section of the control area of a ladder has 4 aspects
to it
- Cancel Buys, which will cancel all buys currently working on the
ladder
- Cancel Sells, which will cancel all sells currently working on the
ladder
- Cancel All, which will cancel all orders on the ladder
- Settings, which will launch the ladder settings window which
appears like below
- Cancel Buys, which will cancel all buys currently working on the
ladder
- Cancel Sells, which will cancel all sells currently working on the
ladder
- Cancel All, which will cancel all orders on the ladder
- Settings, which will launch the ladder settings window which
appears like below
![](ladder_settings.png)
- Fonts and Colors - This is where you can customize the overall main
aspects of the ladder.
- Market Break Lines - These are where can control the viewing of what
has been traded through various shading.
- Market Size Display - This is where you can define if you wish
to see implieds or not and how exactly you would like to see them.
- Volume Display - Where you can choose to show the volume traded at price
graph and what details you would like to see.
- Size Presets - Here you can define what volume of predefined quick buttons
you would like to see or any at all. You can remove these by clicking on the “X” next to the volume number to remove one or many.
- Algo Favorites - Here you can set up which “quick button” or user defined
algos to see. You can customize the amount of columns to be
displayed as well.
- Left Panel - This is where you can remove predefined information on the
left panel to save room and get rid of data that you might not require
to see.
![](image71.png)
- The other, main part of the ladder, is the ladder itself as
displayed below
![](image65.png)
![](ladder_main_part.png)
- There are 5 main columns
@@ -291,14 +353,10 @@ insert_anchor_links = "right"
where the last trade occurred
- Functionality of the ladder
- To enter an order directly by clicking in the ladder, left click on either the bid or ask volume with a volume greater than 0 on the predefined value box on the left-hand section
- A badge will be displayed within the orders column of the value you have chosen and the quantity filled as displayed below
- To enter an order directly by clicking in the ladder, left click on
either the bid or ask volume with a volume greater than 0 on the
predefined value box on the left-hand section
- A badge will be displayed within the orders column of the value you
have chosen and the quantity filled as displayed below
![](image46.png)
![](ladder_badges.png)
- To change the price level you wish to work the order, you can right-click and hold on the badge to move it up or down the ladder for the
desired price
@@ -344,3 +402,44 @@ insert_anchor_links = "right"
- Please note you can also launch the required parameters for algo or
synthetic orders by clicking on the right arrow next to the algo or
synthetic
## Algo Studio
{{ youtube(id="DHIa5tnifKc") }}
- The BTS Algo Studio was built for users to experience algo composability
never seen before in a professional futures trading system.
- There is no limit for putting both “canned algos” already present in the
system as well as user defined algos fed into the system via the API.
- This is all in place in a very user friendly GUI as shown below:
![](algo_studio.png)
- There are 3 main areas of the Algo Studio:
- Left Pane (from top to bottom)
- Add Algo
- Clone Algo
- Save Algo
- Remove Algo
- Middle Pane
- This is where your customized algo will be displayed in a running list
- Right Pane
- This is where your complete customization happens of combining various
algos together and saving them to fit your exact needs
- On the very top are the name and abbreviation boxes
- For the name you can simply name the algo whatever you would like
- For the abbreviation section (which is optional), you can put in an
abbreviated name for the algo which will be displayed on the Algo
buttons on the ladder. (Example - Algo name = PROFIT_OCO_LEAN,
Abbrev. = POL)
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -1,6 +0,0 @@
+++
title = "Client"
weight = 1
sort_by = "weight"
redirect_to = "bts_spark_sdk/Client/overview/"
+++
@@ -1,6 +0,0 @@
+++
title = "Server"
weight = 2
sort_by = "weight"
insert_anchor_links = "right"
+++
-6
View File
@@ -1,6 +0,0 @@
+++
title = "BTS Spark SDK"
weight = 2
sort_by = "weight"
insert_anchor_links = "right"
+++
+57
View File
@@ -4,6 +4,63 @@ weight = 3
sort_by = "weight"
insert_anchor_links = "right"
+++
## 1.6.0
### User Visible Changes
#### New Features
- You can now view your order's position in queue in the ladder
#### Bug Fixes
- Fix a bug in the order popup in the ladder which could lead to a crash.
## 1.5.9
### User Visible Changes
- Fix for stunnel connections on Windows 7
## 1.5.8
### User Visible Changes
- Fixed a bug where it was possible to crash the UI by entering an order through the order ticket if Enter was used to dismiss the dialog.
- Fixed a spreader reporting bug which could cause the server to crash if an algo was used to control certain types of synthetics
- Improved user message when the UI fails to login
- 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)
- 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
- Fixed Aggressor coloring and ladder high/low coloring in the SIM execution gateway
- Added initial support for ICE market data
- In an effort to streamline the PnL report we've made the following changes:
- The PnL report will by default filter for Futures contracts only
- If you wish to see Spread contracts again you may change Security Type column filter
- The PnL report will now only show rows in which there is a position (net and/or day)
- The Exchange, Start PnL, Total Net Pos, and Total Net PnL columns are now hidden by default
- These columns can be restored by toggling their visibility within the PnL's Settings form
- A new Traded Today column has been added to the PnL
- Filtering on this column will allow a user to restrict the report to only show today's position
### Internal changes
- Improved organization of the shell notification architecture
- Added logging to help troubleshoot shell exit hang
## 1.5.6
### User Visible Changes
- Fixed a bug that could cause limits to be erroneously enforced
- Improved algo documentation
- Changed how the spreader uses net change
- Simplified trigger algo user interface
### Internal changes
- Defaulted to not recording sim market data
- Improved documentation build
- Added additional performance tracing
## 1.5.5
- Fixed Market View 'stale working orders' bug
## 1.5.4
- Avoided logging when warming up cache
## 1.5.3
- Fixed sounds startup issue
## 1.5.2
- Added better logging to AlgoDefinitionService
+5
View File
@@ -54,3 +54,8 @@ img {
background: #ccc;
border: 1px solid #aaa;
}
iframe {
width:560px;
height:315px;
}
+197 -223
View File
@@ -1,223 +1,197 @@
function initToggleMenu() {
var $menu = document.querySelector(".menu");
var $menuIcon = document.querySelector(".menu-icon");
var $page = document.querySelector(".page");
$menuIcon.addEventListener("click", function() {
$menu.classList.toggle("menu-hidden");
$page.classList.toggle("page-without-menu");
});
}
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function () {
timeout = null;
func.apply(context, args);
}, wait);
};
}
// Taken from mdbook
// The strategy is as follows:
// First, assign a value to each word in the document:
// Words that correspond to search terms (stemmer aware): 40
// Normal words: 2
// First word in a sentence: 8
// Then use a sliding window with a constant number of words and count the
// sum of the values of the words within the window. Then use the window that got the
// maximum sum. If there are multiple maximas, then get the last one.
// Enclose the terms in <b>.
function makeTeaser(body, terms) {
var TERM_WEIGHT = 40;
var NORMAL_WORD_WEIGHT = 2;
var FIRST_WORD_WEIGHT = 8;
var TEASER_MAX_WORDS = 30;
var stemmedTerms = terms.map(function (w) {
return elasticlunr.stemmer(w.toLowerCase());
});
var termFound = false;
var index = 0;
var weighted = []; // contains elements of ["word", weight, index_in_document]
// split in sentences, then words
var sentences = body.toLowerCase().split(". ");
for (var i in sentences) {
var words = sentences[i].split(" ");
var value = FIRST_WORD_WEIGHT;
for (var j in words) {
var word = words[j];
if (word.length > 0) {
for (var k in stemmedTerms) {
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
value = TERM_WEIGHT;
termFound = true;
}
}
weighted.push([word, value, index]);
value = NORMAL_WORD_WEIGHT;
}
index += word.length;
index += 1; // ' ' or '.' if last word in sentence
}
index += 1; // because we split at a two-char boundary '. '
}
if (weighted.length === 0) {
return body;
}
var windowWeights = [];
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
// We add a window with all the weights first
var curSum = 0;
for (var i = 0; i < windowSize; i++) {
curSum += weighted[i][1];
}
windowWeights.push(curSum);
for (var i = 0; i < weighted.length - windowSize; i++) {
curSum -= weighted[i][1];
curSum += weighted[i + windowSize][1];
windowWeights.push(curSum);
}
// If we didn't find the term, just pick the first window
var maxSumIndex = 0;
if (termFound) {
var maxFound = 0;
// backwards
for (var i = windowWeights.length - 1; i >= 0; i--) {
if (windowWeights[i] > maxFound) {
maxFound = windowWeights[i];
maxSumIndex = i;
}
}
}
var teaser = [];
var startIndex = weighted[maxSumIndex][2];
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
var word = weighted[i];
if (startIndex < word[2]) {
// missing text from index to start of `word`
teaser.push(body.substring(startIndex, word[2]));
startIndex = word[2];
}
// add <em/> around search terms
if (word[1] === TERM_WEIGHT) {
teaser.push("<b>");
}
startIndex = word[2] + word[0].length;
teaser.push(body.substring(word[2], startIndex));
if (word[1] === TERM_WEIGHT) {
teaser.push("</b>");
}
}
teaser.push("…");
return teaser.join("");
}
function formatSearchResultItem(item, terms) {
var li = document.createElement("li");
li.classList.add("search-results__item");
li.innerHTML = `<a href="${item.ref}">${item.doc.title}</a>`;
li.innerHTML += `<div class="search-results__teaser">${makeTeaser(item.doc.body, terms)}</div>`;
return li;
}
// Go from the book view to the search view
function toggleSearchMode() {
var $bookContent = document.querySelector(".book-content");
var $searchContainer = document.querySelector(".search-container");
if ($searchContainer.classList.contains("search-container--is-visible")) {
$searchContainer.classList.remove("search-container--is-visible");
document.body.classList.remove("search-mode");
$bookContent.style.display = "block";
} else {
$searchContainer.classList.add("search-container--is-visible");
document.body.classList.add("search-mode");
$bookContent.style.display = "none";
document.getElementById("search").focus();
}
}
function initSearch() {
var $searchInput = document.getElementById("search");
if (!$searchInput) {
return;
}
var $searchIcon = document.querySelector(".search-icon");
$searchIcon.addEventListener("click", toggleSearchMode);
var $searchResults = document.querySelector(".search-results");
var $searchResultsHeader = document.querySelector(".search-results__header");
var $searchResultsItems = document.querySelector(".search-results__items");
var MAX_ITEMS = 10;
var options = {
bool: "AND",
fields: {
title: {boost: 2},
body: {boost: 1},
}
};
var currentTerm = "";
var index = elasticlunr.Index.load(window.searchIndex);
$searchInput.addEventListener("keyup", debounce(function() {
var term = $searchInput.value.trim();
if (term === currentTerm || !index) {
return;
}
$searchResults.style.display = term === "" ? "none" : "block";
$searchResultsItems.innerHTML = "";
if (term === "") {
return;
}
var results = index.search(term, options).filter(function (r) {
return r.doc.body !== "";
});
if (results.length === 0) {
$searchResultsHeader.innerText = `No search results for '${term}'.`;
return;
}
currentTerm = term;
$searchResultsHeader.innerText = `${results.length} search results for '${term}':`;
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
if (!results[i].doc.body) {
continue;
}
// var item = document.createElement("li");
// item.innerHTML = formatSearchResultItem(results[i], term.split(" "));
console.log(results[i]);
$searchResultsItems.appendChild(formatSearchResultItem(results[i], term.split(" ")));
}
}, 150));
}
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
) {
initToggleMenu();
} else {
document.addEventListener("DOMContentLoaded", function () {
initToggleMenu();
initSearch();
});
}
function initToggleMenu() {
var $menu = document.querySelector(".menu");
var $menuIcon = document.querySelector(".menu-icon");
var $page = document.querySelector(".page");
$menuIcon.addEventListener("click", function () {
$menu.classList.toggle("menu-hidden");
$page.classList.toggle("page-without-menu");
});
}
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
var args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function () {
timeout = null;
func.apply(context, args);
}, wait);
};
}
// Taken from mdbook
// The strategy is as follows:
// First, assign a value to each word in the document:
// Words that correspond to search terms (stemmer aware): 40
// Normal words: 2
// First word in a sentence: 8
// Then use a sliding window with a constant number of words and count the
// sum of the values of the words within the window. Then use the window that got the
// maximum sum. If there are multiple maximas, then get the last one.
// Enclose the terms in <b>.
function makeTeaser(body, terms) {
var TERM_WEIGHT = 40;
var NORMAL_WORD_WEIGHT = 2;
var FIRST_WORD_WEIGHT = 8;
var TEASER_MAX_WORDS = 30;
var stemmedTerms = terms.map(function (w) {
return elasticlunr.stemmer(w.toLowerCase());
});
var termFound = false;
var index = 0;
var weighted = []; // contains elements of ["word", weight, index_in_document]
// split in sentences, then words
var sentences = body.toLowerCase().split(". ");
for (var i in sentences) {
var words = sentences[i].split(" ");
var value = FIRST_WORD_WEIGHT;
for (var j in words) {
var word = words[j];
if (word.length > 0) {
for (var k in stemmedTerms) {
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
value = TERM_WEIGHT;
termFound = true;
}
}
weighted.push([word, value, index]);
value = NORMAL_WORD_WEIGHT;
}
index += word.length;
index += 1; // ' ' or '.' if last word in sentence
}
index += 1; // because we split at a two-char boundary '. '
}
if (weighted.length === 0) {
return body;
}
var windowWeights = [];
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
// We add a window with all the weights first
var curSum = 0;
for (var i = 0; i < windowSize; i++) {
curSum += weighted[i][1];
}
windowWeights.push(curSum);
for (var i = 0; i < weighted.length - windowSize; i++) {
curSum -= weighted[i][1];
curSum += weighted[i + windowSize][1];
windowWeights.push(curSum);
}
// If we didn't find the term, just pick the first window
var maxSumIndex = 0;
if (termFound) {
var maxFound = 0;
// backwards
for (var i = windowWeights.length - 1; i >= 0; i--) {
if (windowWeights[i] > maxFound) {
maxFound = windowWeights[i];
maxSumIndex = i;
}
}
}
var teaser = [];
var startIndex = weighted[maxSumIndex][2];
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
var word = weighted[i];
if (startIndex < word[2]) {
// missing text from index to start of `word`
teaser.push(body.substring(startIndex, word[2]));
startIndex = word[2];
}
// add <em/> around search terms
if (word[1] === TERM_WEIGHT) {
teaser.push("<b>");
}
startIndex = word[2] + word[0].length;
teaser.push(body.substring(word[2], startIndex));
if (word[1] === TERM_WEIGHT) {
teaser.push("</b>");
}
}
teaser.push("…");
return teaser.join("");
}
function formatSearchResultItem(item, terms) {
var li = document.createElement("li");
li.classList.add("search-results__item");
li.innerHTML = "<a href=\"" + item.ref + "\">" + item.doc.title + "</a>";
li.innerHTML += "<div class=\"search-results__teaser\">" + makeTeaser(item.doc.body, terms) + "</div>";
return li;
}
// Go from the book view to the search view
function toggleSearchMode() {
var $bookContent = document.querySelector(".book-content");
var $searchContainer = document.querySelector(".search-container");
if ($searchContainer.classList.contains("search-container--is-visible")) {
$searchContainer.classList.remove("search-container--is-visible");
document.body.classList.remove("search-mode");
$bookContent.style.display = "block";
}
else {
$searchContainer.classList.add("search-container--is-visible");
document.body.classList.add("search-mode");
$bookContent.style.display = "none";
document.getElementById("search").focus();
}
}
function initSearch() {
var $searchInput = document.getElementById("search");
if (!$searchInput) {
return;
}
var $searchIcon = document.querySelector(".search-icon");
$searchIcon.addEventListener("click", toggleSearchMode);
var $searchResults = document.querySelector(".search-results");
var $searchResultsHeader = document.querySelector(".search-results__header");
var $searchResultsItems = document.querySelector(".search-results__items");
var MAX_ITEMS = 10;
var options = {
bool: "AND",
fields: {
title: { boost: 2 },
body: { boost: 1 }
}
};
var currentTerm = "";
var index = elasticlunr.Index.load(window.searchIndex);
$searchInput.addEventListener("keyup", debounce(function () {
var term = $searchInput.value.trim();
if (term === currentTerm || !index) {
return;
}
$searchResults.style.display = term === "" ? "none" : "block";
$searchResultsItems.innerHTML = "";
if (term === "") {
return;
}
var results = index.search(term, options).filter(function (r) {
return r.doc.body !== "";
});
if (results.length === 0) {
$searchResultsHeader.innerText = "No search results for '" + term + "'.";
return;
}
currentTerm = term;
$searchResultsHeader.innerText = results.length + " search results for '" + term + "':";
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
if (!results[i].doc.body) {
continue;
}
// var item = document.createElement("li");
// item.innerHTML = formatSearchResultItem(results[i], term.split(" "));
console.log(results[i]);
$searchResultsItems.appendChild(formatSearchResultItem(results[i], term.split(" ")));
}
}, 150));
}
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)) {
initToggleMenu();
}
else {
document.addEventListener("DOMContentLoaded", function () {
initToggleMenu();
initSearch();
});
}
+6 -1
View File
@@ -8,5 +8,10 @@ $(document).ready(function() {
$(".back-button")
.html("Go Back")
.click(_ => window.history.back());
.click(function(_) { window.history.back()});
if(navigator.userAgent.indexOf("Trident") > -1) {
console.log(navigator.userAgent);
$(".search-icon").css("display", "none");
}
});
+3
View File
@@ -1,3 +1,6 @@
1.5.7
- Fixed Aggressor coloring and ladder high/low coloring in SIM
1.4.2
- Be sure the system has a bts user before deployment. bts is the superuser, he has all permissions and can see all market data
- After deployment re-permission all existing users for trading, riskadmin, and/or md admin permissions as appropriate
+65 -4
View File
@@ -1,4 +1,6 @@
using System;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
@@ -66,18 +68,77 @@ namespace mbts.ui.shell
protected override void OnExit(ExitEventArgs e)
{
Log.Gen.info("Application.OnExit");
if (_runtime != null) _runtime.Dispose();
utils.BTSNativeMethods.finit_spark_lib();
base.OnExit(e);
Environment.Exit(0);
}
void start_stunnel(string path, uint job_error)
{
string config_error;
if (!mbts.config.Configuration.try_initialize(out config_error))
{
_stunnel_process = Process.Start(path + "bin\\stunnel.exe");
if (_stunnel_process != null)
{
for (int i = 0; i < 10; i++)
{
if (mbts.config.Configuration.try_initialize(out config_error))
break;
System.Threading.Thread.Sleep(500);
}
}
else
stunnel_job_error = "Stunnel Job Error code: " + job_error +
" could not start stunnel " + (config_error ?? string.Empty);
}
}
void kill_stunnel()
{
try
{
Process[] ps = Process.GetProcessesByName("stunnel");
ps.ForEach(p =>
{
try { p.Kill(); }
catch {}
});
}
catch { }
}
Process _stunnel_process;
static internal string stunnel_job_error { get; private set;}
void init_stunnel(string[] argv)
{
try
{
var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\BTS\\stunnel\\";
if (File.Exists(path + "config\\stunnel.conf"))
{
uint job_error = BTSUINativeMethods.setup_process_job_object();
//if we can't put this process and stunnel into a job, try to stop stunnel then restart
if(job_error != 0)
kill_stunnel();
start_stunnel(path, job_error);
}
}
catch(Exception ex)
{
stunnel_job_error = "Error starting stunnel: " + ex.Message;
}
}
public new int Run() {
var argv = Environment.GetCommandLineArgs();
try {
if (argv.Contains("-multiple") ||
_mutex == null ||
_mutex.WaitOne(TimeSpan.Zero, true)) {
try {
if (argv.Contains("-multiple") || _mutex == null || _mutex.WaitOne(TimeSpan.Zero, true))
{
if(_mutex == null || _mutex.WaitOne(TimeSpan.Zero, true))
init_stunnel(argv);
return base.Run();
}
else {
@@ -6,6 +6,7 @@ using bts.api.persistence;
using bts.api.position;
using bts.api.synthetic;
using bts.co2;
using bts.md;
using bts.metrics;
using bts.support;
using System.ComponentModel.Composition;
@@ -50,6 +51,9 @@ namespace Spark {
AlgoServiceWrapper _algo_service_wrapper;
[Export]
public AlgoServiceWrapper algo_service_wrapper => _algo_service_wrapper ?? (_algo_service_wrapper = new AlgoServiceWrapper(context));
[Export]
public MBOService mbo_service => context.locate<MBOService>();
}
class AlgoServiceWrapper {
@@ -137,6 +137,12 @@ namespace mbts.spark {
public ICommand ask_implied_size_click { get; }
public ICommand working_asks_click { get; }
public ICommand default_command { get; }
public string market_description {get;}
public string cleared_alias { get; }
public string product_description { get; }
public string hub_alias { get; }
public string strip_name{ get; }
public string mic_code { get; }
public ObservableCollection<RowAnimation> row_animations { get; } = new ObservableCollection<RowAnimation>();
@@ -57,6 +57,13 @@ namespace mbts.spark {
add_col(Columns.SessionLow, "Low", r => r.format_price(r.session_low), nameof(Row.session_low), DefaultColumnWidths.Price, session_low_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.SessionHigh, "High", r => r.format_price(r.session_high), nameof(Row.session_high), DefaultColumnWidths.Price, session_high_filter, DataGridFilterKnownTemplates.DoubleMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.SecurityKey, "Security Key", r => r.security_key.ToString(), nameof(Row.security_key), DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.MarketDescription, "Market Description", r => r.md.security.HasIce ? r.md.security.Ice.MarketDescription : string.Empty, nameof(Row.market_description), DefaultColumnWidths.SecurityKey, market_description_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.ClearedAlias, "Cleared Alias", r => r.md.security.HasIce ? r.md.security.Ice.ClearedAlias : string.Empty, nameof(Row.cleared_alias), DefaultColumnWidths.SecurityKey, cleared_alias_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.ProductDescription, "Product Description", r => r.md.security.HasIce ? r.md.security.Ice.ProductName : string.Empty, nameof(Row.product_description), DefaultColumnWidths.SecurityKey, product_description_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.HubAlias, "Hub Alias", r => r.md.security.HasIce ? r.md.security.Ice.HubAlias : string.Empty, nameof(Row.hub_alias), DefaultColumnWidths.SecurityKey, hub_alias_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.StripName, "Strip Name", r => r.md.security.HasIce ? r.md.security.Ice.StripName : string.Empty, nameof(Row.strip_name), DefaultColumnWidths.SecurityKey, strip_name_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.MicCode, "Mic Code", r => r.md.security.HasIce ? r.md.security.Ice.MicCode : string.Empty, nameof(Row.mic_code), DefaultColumnWidths.SecurityKey, mic_code_filter, DataGridFilterKnownTemplates.Regex);
//
void add_col(Columns col_key, string header, Func<Row, string> get_text, string prop, double width, IDataGridColumnFilter filter, string filter_template, bool is_animatable = false, Color? font_color = null, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
columns.Add(new RenderTextColumn<Row, Columns>(col_key, get_text, filter, filter_template) {
@@ -194,6 +201,14 @@ namespace mbts.spark {
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> working_ask_volume_filter { get; } = new IntMinMax<Row>(r => r.working_ask_volume, nameof(Row.working_ask_volume));
public IDataGridColumnPersistentFilter<Row, ULongMinMaxState> volume_filter { get; } = new ULongMinMax<Row>(r => r.volume, nameof(Row.volume));
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
public IDataGridColumnPersistentFilter<Row, string> market_description_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.MarketDescription : "", nameof(Row.market_description));
public IDataGridColumnPersistentFilter<Row, string> cleared_alias_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.ClearedAlias : "", nameof(Row.cleared_alias));
public IDataGridColumnPersistentFilter<Row, string> product_description_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.ProductName : "", nameof(Row.product_description));
public IDataGridColumnPersistentFilter<Row, string> hub_alias_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.HubAlias : "", nameof(Row.hub_alias));
public IDataGridColumnPersistentFilter<Row, string> strip_name_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.StripName : "", nameof(Row.strip_name));
public IDataGridColumnPersistentFilter<Row, string> mic_code_filter { get; } = new RegexFilterVM<Row>(r => r.security.HasIce ? r.security.Ice.MicCode : "", nameof(Row.mic_code));
public FilterStates get_filter_states() {
return new FilterStates {
@@ -216,7 +231,13 @@ namespace mbts.spark {
working_bid_volume = working_bid_volume_filter.get_state(),
working_ask_volume = working_ask_volume_filter.get_state(),
volume = volume_filter.get_state(),
security_key = security_key_filter.get_state()
security_key = security_key_filter.get_state(),
market_description = market_description_filter.get_state(),
cleared_alias = cleared_alias_filter.get_state(),
product_description = product_description_filter.get_state(),
hub_alias = hub_alias_filter.get_state(),
strip_name = strip_name_filter.get_state(),
mic_code = mic_code_filter.get_state(),
};
}
#endregion
@@ -8,7 +8,9 @@ namespace mbts.ui.spark {
/// Interaction logic for MarketSheetsView.xaml
/// </summary>
partial class MarketViewView : UserControl {
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidMergedSize, BidDirectSize, BidImpliedSize, Ask, AskMergedSize, AskDirectSize, AskImpliedSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey }
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidMergedSize, BidDirectSize, BidImpliedSize, Ask, AskMergedSize, AskDirectSize, AskImpliedSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey,
MarketDescription, ClearedAlias, ProductDescription, HubAlias, StripName, MicCode
}
public MarketViewView() {
InitializeComponent();
@@ -97,6 +97,12 @@ namespace bts.shell {
public static GridColumnSettings session_low => new GridColumnSettings(col_id(Columns.SessionLow), DefaultColumnHeaders.SessionLow, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings session_high => new GridColumnSettings(col_id(Columns.SessionHigh), DefaultColumnHeaders.SessionHigh, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static GridColumnSettings market_description => new GridColumnSettings(col_id(Columns.MarketDescription), DefaultColumnHeaders.MarketDescription, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static GridColumnSettings cleared_alias => new GridColumnSettings(col_id(Columns.ClearedAlias), DefaultColumnHeaders.ClearedAlias, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static GridColumnSettings product_description => new GridColumnSettings(col_id(Columns.ProductDescription), DefaultColumnHeaders.ProductDescription, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static GridColumnSettings hub_alias => new GridColumnSettings(col_id(Columns.HubAlias), DefaultColumnHeaders.HubAlias, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static GridColumnSettings strip_name => new GridColumnSettings(col_id(Columns.StripName), DefaultColumnHeaders.StripName, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static GridColumnSettings mic_code => new GridColumnSettings(col_id(Columns.MicCode), DefaultColumnHeaders.MicCode, is_visible: false, width: DefaultColumnWidths.SecurityKey);
@@ -128,6 +134,12 @@ namespace bts.shell {
yield return session_low;
yield return session_high;
yield return security_key;
yield return market_description;
yield return cleared_alias;
yield return product_description;
yield return hub_alias;
yield return strip_name;
yield return mic_code;
}
}
@@ -177,6 +189,18 @@ namespace bts.shell {
public UIntMinMaxState ask_direct_size;
[ProtoMember(20)]
public UIntMinMaxState ask_implied_size;
[ProtoMember(21)]
public string market_description;
[ProtoMember(22)]
public string cleared_alias;
[ProtoMember(23)]
public string product_description;
[ProtoMember(24)]
public string hub_alias;
[ProtoMember(25)]
public string strip_name;
[ProtoMember(26)]
public string mic_code;
}
}
}
@@ -20,6 +20,8 @@
<sys:Double x:Key="RowHeight">16</sys:Double>
<sys:Double x:Key="NegativeRowHeight">-16</sys:Double>
<sys:Double x:Key="ItemsHostCanBeDisabledCollapsedHeight">15</sys:Double>
<sys:Double x:Key="HeaderPanelItemSeparation">6</sys:Double>
<Thickness x:Key="Margin.LHeaderPanelItem" Left="{StaticResource HeaderPanelItemSeparation}"/>
<Thickness
x:Key="GroupLevelMargin"
Left="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
@@ -100,7 +102,7 @@
</usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
<usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
<DataTemplate>
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}" Increment="{Binding tick_increment, FallbackValue={u:Double 1}}"/>
<c:TickUpDownDouble Value="{Binding value}" IsEnabled="{Binding enabled}" Increment="{Binding tick_increment, FallbackValue={u:Double 1}}" FlushTextImmediately="False"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.DoubleTemplate>
<usp:DataGridParameterCellTemplateConverter.IntTemplate>
@@ -369,16 +371,21 @@
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="7*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" IsEnabled="{TemplateBinding IsNew}">Name</TextBlock>
<TextBox
x:Name="PART_Name_TextBox" Grid.Column="1" Margin="6,0,0,0" HorizontalAlignment="Stretch" IsEnabled="{TemplateBinding IsNew}"
x:Name="PART_Name_TextBox"
Grid.Column="1"
Margin="{StaticResource Margin.LHeaderPanelItem}"
MinWidth="80"
HorizontalAlignment="Stretch"
IsEnabled="{TemplateBinding IsNew}"
Text="{Binding SyntheticName, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"
>
<u:Behavior.BindingUpdateGroups>
@@ -390,18 +397,22 @@
</u:Behavior.BindingUpdateGroups>
</TextBox>
<TextBlock
Grid.Column="2" VerticalAlignment="Center" Margin="6,0,0,0" Text="Ticks" IsEnabled="{TemplateBinding IsNew}"
Grid.Column="2" VerticalAlignment="Center" Margin="{StaticResource Margin.LHeaderPanelItem}" Text="Ticks" IsEnabled="{TemplateBinding IsNew}"
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
/>
<c:TickFactorsPicker
x:Name="PART_TickFactorsPicker" Grid.Column="3" Margin="6,0,0,0" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
x:Name="PART_TickFactorsPicker" Grid.Column="3" Margin="{StaticResource Margin.LHeaderPanelItem}" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
SelectedTickFactors="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
/>
<TextBlock Grid.Column="4" VerticalAlignment="Center" Margin="6,0,0,0">Pricing</TextBlock>
<TextBlock Grid.Column="4" VerticalAlignment="Center" Margin="{StaticResource Margin.LHeaderPanelItem}" IsEnabled="{TemplateBinding IsNew}" >Pricing</TextBlock>
<ComboBox
Grid.Column="5" Margin="6,0,0,0" IsEnabled="False"
ItemsSource="{x:Static usp:ExecutionStrategyEditor.PriceCalcItems}" SelectedItem="{Binding PriceCalc, RelativeSource={RelativeSource TemplatedParent}}"
Grid.Column="5"
Margin="{StaticResource Margin.LHeaderPanelItem}"
Width="90"
IsEnabled="{TemplateBinding IsNew}"
ItemsSource="{x:Static usp:ExecutionStrategyEditor.PriceCalcItems}"
SelectedItem="{Binding price_calc}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
@@ -409,7 +420,7 @@
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<c:HelpButton Grid.Column="6" Margin="3,0,0,0"/>
<c:HelpButton Grid.Column="8" Margin="3,0,0,0" ManPage="bts_spark/spreader/#creating-a-synthetic" />
</Grid>
<c:DataGridEx
x:Name="PART_DataGrid"
@@ -99,7 +99,7 @@ namespace mbts.ui.spark {
#region PriceCalc
public static readonly DependencyProperty PriceCalcProperty = DependencyProperty.Register("PriceCalc", typeof(SyntheticContract.Types.PriceCalc), typeof(ExecutionStrategyEditor),
new FrameworkPropertyMetadata(SyntheticContract.Types.PriceCalc.Diff, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
new FrameworkPropertyMetadata(SyntheticContract.Types.PriceCalc.Standard, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public SyntheticContract.Types.PriceCalc PriceCalc { get => (SyntheticContract.Types.PriceCalc)GetValue(PriceCalcProperty); set => SetValue(PriceCalcProperty, value); }
public static readonly IEnumerable<SyntheticContract.Types.PriceCalc> PriceCalcItems = EnumHelper.get_values<SyntheticContract.Types.PriceCalc>();
@@ -315,7 +315,6 @@ namespace mbts.ui.spark {
else if (t == typeof(uint)) return UIntTemplate;
else if (t == typeof(QuantityMeasure)) return QuantityMeasureTemplate;
else if (t == typeof(LegSpreaderParams.Types.MarketDataType)) return MarketDataTypeTemplate;
else if (t == typeof(LegSpreaderParams.Types.LegMarketDataCalculation)) return MarketDataCalculationTemplate;
else if (t == typeof(ConditionalQuoting.Types.PriceType)) return ConditionalPriceTypeTemplate;
else return null;
}
@@ -1,18 +0,0 @@
using mbts.ui.spark;
using LegMarketDataCalculation = bts.spark.LegSpreaderParams.Types.LegMarketDataCalculation;
namespace mbts.spark {
partial class SyntheticEditorVM {
class MarketDataCalculationModel : RowModelBase<LegMarketDataCalculation> {
public override string display_name { get; } = "Market Data Calculation";
public override string help_text { get; }
public override string man_page { get; } = string.Empty;
public override LegMarketDataCalculation get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.LegMdCalc;
public override void set_value(LegSpreaderParamsTuple leg_tup, LegMarketDataCalculation value) =>
leg_tup.leg_params.LegMdCalc = value;
}
}
}
@@ -26,6 +26,7 @@ namespace mbts.spark {
readonly PriceMultiplierModel _price_mult_model;
readonly List<IDisposable> _resources = new List<IDisposable>();
readonly Property<bool> _has_working_orders, _can_add_leg;
Security _synthetic;
bool _set_name_automatically, _set_ticks_automatically, _set_quantities_automatically, _set_price_mults_automatically, _is_setting_quantities, _is_setting_price_mults;
TickFactors _tick_factors;
@@ -207,7 +208,6 @@ namespace mbts.spark {
//new MinLeanQtyModel(), //TAHA_SPREADER
//new QuoteHiddenModel(),
new MarketDataTypeModel(),
new MarketDataCalculationModel(),
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
quote_throttle_inside_ticks,
quote_throttle_outside_ticks,
@@ -291,7 +291,9 @@ namespace mbts.spark {
public ExecutionStrategyImmutable get_strategy() {
var mutable_strategy = _strategy.CloneToMutable();
mutable_strategy.LegParamsMap.Clear();
foreach (var lt in leg_tuples) mutable_strategy.LegParamsMap.Add(lt.leg.Security.Id, lt.leg_params);
foreach (var lt in leg_tuples) {
mutable_strategy.LegParamsMap.Add(lt.leg.Security.Id, lt.leg_params);
}
return new ExecutionStrategyImmutable(mutable_strategy);
}
@@ -10,7 +10,7 @@ namespace mbts.spark {
public readonly AlgoKey key;
public AlgoData(CustomOrderDefinitionImmutable value)
: this(value, value.CustomCase.display_name(), new AlgoKey(value.CustomCase)) { }
: this(value, value.display_name(), OrderDataHelper.get_shallow_factory_key(value)) { }
public AlgoData(CustomOrderDefinitionImmutable value, string name, AlgoKey key) {
this.value = value;
@@ -72,7 +72,7 @@ namespace mbts.spark {
_build_structure(definition.CloneToMutable(), ref structure, pos);
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, int pos) {
var cad = OrderDataHelper.get_child_algo_data(algo.CustomCase);
var cad = OrderDataHelper.get_child_algo_data(algo);
for (int di = 0; di < cad.Count; di++) {
if (cad[di].get_child(algo) is CustomOrderDefinition child_algo) {
ArrayHelper.ensure_capacity(ref structure, pos + 1);
@@ -87,7 +87,7 @@ namespace mbts.spark {
static void _build_def(int[] structure, ref int pos, ref CustomOrderDefinition algo) {
for (int i = pos; i < structure.Length; i++) {
var cad = OrderDataHelper.get_child_algo_data(algo.CustomCase);
var cad = OrderDataHelper.get_child_algo_data(algo);
for (int di = 0; di < cad.Count; di++) {
int mask = 1 << (31 - di);
if ((structure[i] & mask) == mask) {
@@ -95,10 +95,10 @@ namespace mbts.spark {
var child_algo = OrderDataHelper.Defaults.CustomOrderDefinition(type);
pos++;
_build_def(structure, ref pos, ref child_algo);
cad[di].set_child(algo, child_algo);
cad[di].assign_child(algo, child_algo);
}
}
}
}
}
}
}
@@ -6,11 +6,13 @@ using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using System;
using System.Reactive;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Reactive.Linq;
namespace mbts.spark {
partial class OrderBookRowVM : DisposableViewModelBase, IEquatable<OrderBookRowVM>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
@@ -44,6 +46,19 @@ namespace mbts.spark {
modify_order_price?.TryRaiseCanExecuteChanged();
}
//// This takes care of the case where the OB has all rows visible
//// and an order is cancelled outside of the OB (say via the ladder).
//// Since the OB window Commands only revaluate their CanExecuteChanged
//// when the OrderBookVM.selected_order_changes, this emulates the selected
//// order changing since the current_row setter invokes the selected_order_changed
//// handler
public IObservable<OrderSecurityAlgoMeta> subscribe_data() {
return Observable.FromEvent<Action, OrderSecurityAlgoMeta>(
cb => () => cb(order_tuple),
cb => invalidated += cb,
cb => invalidated -= cb);
}
protected override void Dispose(bool disposing) {
this.invalidated -= OrderBookRowVM_invalidated;
base.Dispose(disposing);
@@ -19,12 +19,11 @@ using System.Windows.Controls;
using System.Windows.Input;
using static bts.shell.OrderBookWindowFactory;
using static mbts.spark.OrderBookView;
using _GridSettingsManager =
using _GridSettingsManager =
mbts.ui.GridSettingsManager<
mbts.spark.OrderBookRowVM,
bts.api.orderman.OrderSecurity,
bts.shell.INotificationServiceDataCache,
mbts.OrderRecordTuplePredicate,
mbts.spark.OrderBookRowVM,
bts.api.orderman.OrderSecurity,
mbts.OrderRecordTuplePredicate,
bts.shell.OrderBookWindowFactory.OrderBookGridSettings>;
namespace mbts.spark {
@@ -54,6 +53,7 @@ namespace mbts.spark {
collapse_all_rows_command = new DelegateCommand(_row_collection.collapse_all);
expand_all_rows_command = new DelegateCommand(_row_collection.expand_all);
grid_settings_manager = _create_grid_settings_manager(owner_title, bus, notification_service, model, rows, core_columns);
_resources = new List<IDisposable> {
model.on_order_tuples.OnDispatcher().Subscribe(_row_collection.update),
grid_settings_manager,
@@ -62,7 +62,12 @@ namespace mbts.spark {
_row_collection.CollectionChanged -= _row_collection_CollectionChanged;
Disposable.dispose(_row_collection);
}),
_columns
_columns,
_current_row.SubscribeHookLatest(
r => r.newval?.subscribe_data().Subscribe(
data => selected_order_changed?.Invoke(data.order)
)
)
};
if (settings_manager != null) {
_resources.Add(settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager));
@@ -105,19 +110,23 @@ namespace mbts.spark {
}
public void row_selection_changed(SelectionChangedEventArgs e) {
if (e.RemovedItems != null) {
foreach (var row_vm in e.RemovedItems.Cast<OrderBookRowVM>()) {
row_vm.is_selected = false;
try {
if (e.RemovedItems != null) {
foreach (var row_vm in e.RemovedItems.Cast<OrderBookRowVM>()) {
row_vm.is_selected = false;
}
}
}
if (e.AddedItems != null) {
foreach (var row_vm in e.AddedItems.Cast<OrderBookRowVM>()) {
row_vm.is_selected = true;
if (e.AddedItems != null) {
foreach (var row_vm in e.AddedItems.Cast<OrderBookRowVM>()) {
row_vm.is_selected = true;
}
}
RowSelectionHelper.apply_changes(_selected_rows, e);
selected_order_changed?.Invoke(current_order);
} catch (Exception ex) {
// [John]I observed a strange null-reference exception in this callback when the launcher was shutting down (see https://bluetradesys.atlassian.net/browse/SPARK-2089)
Log.Ui.error($"[{nameof(OrderBookVM)}] Error handling selection change: {ex}");
}
RowSelectionHelper.apply_changes(_selected_rows, e);
selected_order_changed?.Invoke(current_order);
}
public OrderBookStartupData create() {
@@ -227,21 +236,25 @@ namespace mbts.spark {
col_settings_flags[c.ToString()] = GridColumnSettingsFeatureFlags.ImmutableFontColor | GridColumnSettingsFeatureFlags.ImmutableFontWeight;
}
return new _GridSettingsManager(
owner_title, bus, notification_service, model, rows, columns,
owner_title, bus, model, rows, columns,
() => new OrderRecordTuplePredicate(),
() => {
var pvm = PredicateBuilderVMWrapper.create<OrderSecurity, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
var pvm = PredicateVMWrapper.create<OrderSecurity, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
OrderRecordTuplePredicateVMFactory.get_oneofs(bus, model), rp => rp.predicate_type_case);
pvm.selected_predicate_type = pvm.predicate_types.FirstOrDefault();
return pvm;
},
col_id => {
var pvm = PredicateBuilderVMWrapper.create<OrderSecurity, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
var pvm = PredicateVMWrapper.create<OrderSecurity, OrderRecordTuplePredicate, OrderRecordTuplePredicate.PredicateTypeOneofCase>(
OrderRecordTuplePredicateVMFactory.get_oneofs(bus, model), rp => rp.predicate_type_case);
pvm.selected_predicate_type = _corresponding_predicate_type(EnumHelper.parse_or_default<Columns>(col_id));
return pvm;
},
(gen, anims) => new OrderBookGridSettings(gen, anims.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_builder, a.cell_animations, a.row_animations, a.sound_notifications))),
(notification_service == null) ? null : notification_service.data_cache,
(gen, anims) => new OrderBookGridSettings(gen, anims.Select(a => new OrderRecordTuplePredicateAnimations(a.predicate_data, a.cell_animations, a.row_animations, a.sound_notifications))),
p => p.create_predicate(notification_service.data_cache),
p => p.create_notification_predicate(notification_service.data_cache),
notification_service == null ? new Func<IPredicate<OrderSecurity>, SoundNotification, IDisposable>((p, sn) => Disposable.empty) : notification_service.link_to_order,
notification_service == null ? new Func<INotificationPredicate<OrderSecurity>, IDisposable>(t => Disposable.empty) : notification_service.link_to_order,
col_settings_flags);
}
@@ -271,4 +284,4 @@ namespace mbts.spark {
}
}
}
}
}
@@ -87,17 +87,41 @@ namespace mbts.spark {
_cancel_buy_orders_command = SparkCommands.CancelBuyOrders.complete(
_model.working_orders.Select(os => os.Values.Where(o => o.order.side() == Side.Buy).Select(o => o.order.Key)),
wbids => wbids.Any()
wbids => wbids.Any(),
() => {
var resp = BTSMessageBox.Show(
"Are you sure you want to cancel all BUY orders?",
"Cancel Buy Orders",
BTSMessageBoxButtons.Yes | BTSMessageBoxButtons.No,
MessageBoxImage.Question);
return resp == BTSMessageBoxButtons.Yes;
}
);
_cancel_sell_orders_command = SparkCommands.CancelSellOrders.complete(
_model.working_orders.Select(os => os.Values.Where(o => o.order.side() == Side.Sell).Select(o => o.order.Key)),
wasks => wasks.Any()
wasks => wasks.Any(),
() => {
var resp = BTSMessageBox.Show(
"Are you sure you want to cancel all SELL orders?",
"Cancel Sell Orders",
BTSMessageBoxButtons.Yes | BTSMessageBoxButtons.No,
MessageBoxImage.Question);
return resp == BTSMessageBoxButtons.Yes;
}
);
_cancel_all_orders_command = SparkCommands.CancelOrders.complete(
_model.working_orders.Select(os => os.Select(o => o.Key)),
worders => worders.Any()
worders => worders.Any(),
() => {
var resp = BTSMessageBox.Show(
"Are you sure you want to cancel ALL orders?",
"Cancel All Orders",
BTSMessageBoxButtons.Yes | BTSMessageBoxButtons.No,
MessageBoxImage.Question);
return resp == BTSMessageBoxButtons.Yes;
}
);
_cancel_selected_orders_command = SparkCommands.CancelSelectedOrders.complete(
@@ -84,17 +84,17 @@ namespace bts.shell {
static class DefaultColumns {
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange);
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, false, width: DefaultColumnWidths.Exchange);
public static GridColumnSettings symbol => new GridColumnSettings(col_id(Columns.Symbol), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol);
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings security_type => new GridColumnSettings(col_id(Columns.SecurityType), DefaultColumnHeaders.SecurityType, width: DefaultColumnWidths.SecurityType);
public static GridColumnSettings start_net_position => new GridColumnSettings(col_id(Columns.StartNetPos), DefaultColumnHeaders.StartNetPos, width: DefaultColumnWidths.Start_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, false, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings day_long_pnl => new GridColumnSettings(col_id(Columns.DayLong), DefaultColumnHeaders.DayLong, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings day_short_pnl => new GridColumnSettings(col_id(Columns.DayShort), DefaultColumnHeaders.DayShort, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings day_pnl => new GridColumnSettings(col_id(Columns.DayPNL), DefaultColumnHeaders.DayPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, width: DefaultColumnWidths.Total_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, false, width: DefaultColumnWidths.Total_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, false, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings pnl_price => new GridColumnSettings(col_id(Columns.PNLPrice), DefaultColumnHeaders.PnLPrice, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
@@ -158,6 +158,8 @@ namespace bts.shell {
// ProtoMember 13 cannot be used further
[ProtoMember(14)]
public SecurityType[] security_types;
[ProtoMember(15)]
public bool[] has_day_trades;
}
}
@@ -12,7 +12,6 @@ using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Linq;
using OrderTuple = System.ValueTuple<bts.api.SecurityImmutable?, bts.api.orderman.OrderRecordImmutable>;
namespace bts.shell {
interface INotificationService {
@@ -20,32 +19,14 @@ namespace bts.shell {
INotificationServiceDataCache data_cache { get; }
IDisposable link_to_activity(IPredicate<Activity> predicate, SoundNotification sound);
IDisposable link_to_activity(INotificationPredicate<Activity> toast_notification_predicate);
IDisposable link_to_order(IPredicate<OrderTuple> predicate, SoundNotification sound);
IDisposable link_to_order(INotificationPredicate<OrderTuple> toast_notification_predicate);
}
static class NotificationServiceExtensions {
public static IDisposable link<T>(this INotificationService notification_service, IPredicate<T> predicate, SoundNotification sound) {
if (predicate is IPredicate<OrderTuple> orp)
return notification_service.link_to_order(orp, sound);
if (predicate is IPredicate<Activity> ap)
return notification_service.link_to_activity(ap, sound);
throw new ArgumentException(nameof(predicate));
}
public static IDisposable link<T>(this INotificationService notification_service, INotificationPredicate<T> toast_notification_predicate) {
if (toast_notification_predicate is INotificationPredicate<OrderTuple> orp)
return notification_service.link_to_order(orp);
if (toast_notification_predicate is INotificationPredicate<Activity> ap)
return notification_service.link_to_activity(ap);
throw new ArgumentException(nameof(toast_notification_predicate));
}
IDisposable link_to_order(IPredicate<OrderSecurity> predicate, SoundNotification sound);
IDisposable link_to_order(INotificationPredicate<OrderSecurity> toast_notification_predicate);
}
[Export(typeof(INotificationService))]
partial class NotificationService : INotificationService, IDisposable {
readonly NotificationProcessor<Activity> _activity_notifications;
readonly NotificationProcessor<OrderTuple> _order_notifications;
readonly NotificationProcessor<OrderSecurity> _order_notifications;
readonly DataCache _data_cache = new DataCache();
IDisposable _dispose;
@@ -54,7 +35,7 @@ namespace bts.shell {
public NotificationService(IBus bus, INotifier notifier, OrdersModel orders_model) {
this.notifier = notifier;
_activity_notifications = new NotificationProcessor<Activity>(notifier);
_order_notifications = new NotificationProcessor<OrderTuple>(notifier);
_order_notifications = new NotificationProcessor<OrderSecurity>(notifier);
_dispose = Disposable.Create(
bus.subscribe(SparkTags.LogActivity).Subscribe(_on_activity),
orders_model.on_orders.Subscribe(_on_orders)
@@ -67,17 +48,17 @@ namespace bts.shell {
public IDisposable link_to_activity(IPredicate<Activity> predicate, SoundNotification sound) => _activity_notifications.link(predicate, sound);
public IDisposable link_to_activity(INotificationPredicate<Activity> toast_notification_predicate) => _activity_notifications.link(toast_notification_predicate);
public IDisposable link_to_order(IPredicate<OrderTuple> predicate, SoundNotification sound) => _order_notifications.link(predicate, sound);
public IDisposable link_to_order(INotificationPredicate<OrderTuple> toast_notification_predicate) => _order_notifications.link(toast_notification_predicate);
public IDisposable link_to_order(IPredicate<OrderSecurity> predicate, SoundNotification sound) => _order_notifications.link(predicate, sound);
public IDisposable link_to_order(INotificationPredicate<OrderSecurity> toast_notification_predicate) => _order_notifications.link(toast_notification_predicate);
public void Dispose() => Disposable.dispose(ref _dispose);
void _on_activity(Activity activity) =>
_activity_notifications.process_item(activity);
void _on_orders(TableUpdate<(SecurityImmutable?,OrderRecordImmutable),uint> update) {
void _on_orders(TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint> update) {
// must update notification_service_data_cache first, some of the predicate/triggers (esp. ToastNotifications) will use the notification_service_data_cache to lookup order-history
_data_cache.update(update.as_iincremental());
foreach (var o in update.added)
foreach (var o in update.added.Concat(update.changed))
_order_notifications.process_item(o);
}
@@ -27,7 +27,7 @@ namespace bts.spark {
static readonly IEqualityComparer<OrderRecordImmutable> _id_comparer =
bts.utils.EqualityComparer.Create<OrderRecordImmutable, uint>(a => a.Key);
public OrderActivitySummarizer(IBus bus, IObservable<TableUpdate<(SecurityImmutable? sec,OrderRecordImmutable order), uint>> orders) {
public OrderActivitySummarizer(IBus bus, IObservable<TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint>> orders) {
_bus = bus;
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(sec => _security = sec),
@@ -346,7 +346,9 @@ namespace mbts.spark {
_time_in_force.observe_current(start_immediately);
public IObservable<IAlgoVM> observe_order_type(bool start_immediately = false) =>
_selected_algo_template.observe_current(start_immediately).Select(t => t.algo_root);
_model.algo_model.templates_enabled() ?
_selected_algo_template.observe_current(start_immediately).Select(t => t?.algo_root) :
_selected_algo_definition.observe_selected_item(start_immediately).Select(d => d?.algo_root);
public OrderDefinition create(Side side) {
if (HasErrors)
+15 -2
View File
@@ -7,18 +7,31 @@ namespace bts.api.orderman {
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order) {
this.security = sec;
this.order = order;
piq = null;
}
public OrderSecurity((SecurityImmutable? sec, OrderRecordImmutable order) t) {
security = t.sec;
order = t.order;
piq = null;
}
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order, uint? piq) {
this.security = sec;
this.order = order;
this.piq = piq;
}
//public OrderSecurity(OrderSecurity t) {
// security = t.security;
// order = t.order;
// piq = t.piq;
//}
public static implicit operator OrderSecurity(ValueTuple<SecurityImmutable?, OrderRecordImmutable> x) => new OrderSecurity(x);
public SecurityImmutable? security { get; }
public OrderRecordImmutable order { get; }
public uint? piq { get; }
public override bool Equals(object obj) => (obj is OrderSecurity os) && Equals(os);
public bool Equals(OrderSecurity other) => order.Equals(other.order) && security == other.security;
public override int GetHashCode() => (security, order).GetHashCode();
public bool Equals(OrderSecurity other) => order.Equals(other.order) && security == other.security && piq == other.piq;
public override int GetHashCode() => (security, order, piq).GetHashCode();
public static bool operator == (OrderSecurity left, OrderSecurity right) => left.Equals(right);
public static bool operator != (OrderSecurity left, OrderSecurity right) => !left.Equals(right);
@@ -1,5 +1,8 @@
using bts.api;
using bts.api.orderman;
using bts.utils;
using Google.Protobuf.Reflection;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
@@ -10,6 +13,9 @@ namespace mbts.spark {
[Export]
public class DynamicOrderHelper
{
static readonly IReadOnlyDictionary<DynamicOrderParam.ValueOneofCase, (FieldDescriptor field_descriptor, Type field_type)> _descriptor_case_index =
ProtoHelper.get_oneof_descriptor_index<DynamicOrderParam, DynamicOrderParam.ValueOneofCase>(nameof(DynamicOrderParam.ValueCase));
readonly RegisteredDynamicOrderTypes _dynamic_types;
[ImportingConstructor]
private DynamicOrderHelper(OrderService order_service)
@@ -28,11 +34,47 @@ namespace mbts.spark {
foreach(var p in d.ParamDesc)
dorder.Dynamic.OrderParams.Add( p.ParamName, new DynamicOrderParam() );
defs.Add(new CustomOrderDefinitionImmutable(dorder));
var de = get_descriptor("");
}
}
internal DynamicOrderDescriptor get_descriptor(string dynamic_type) =>
_dynamic_types.DynamicOrderDesc.FirstOrDefault(d => d.DynamicOrderType == dynamic_type);
_dynamic_types.DynamicOrderDesc.FirstOrDefault(d => d.DynamicOrderType == dynamic_type) ?? new DynamicOrderDescriptor();
internal DynamicOrderDefinition create_def(string dynamic_type) {
var desc = get_descriptor(dynamic_type);
var def = new DynamicOrderDefinition();
foreach (var p in desc.ParamDesc) {
if (_is_child_algo_desc(p)) {
def.OrderParams[p.ParamName] = new DynamicOrderParam { AsOrderDef = new CustomOrderDefinition() };
} else if (_is_value_param_desc(p)) {
var value_case = _get_value_case(p.ParamType.Primitive);
if (_descriptor_case_index.TryGetValue(value_case, out (FieldDescriptor fd, Type type) tup)) {
var dop = new DynamicOrderParam();
tup.fd.Accessor.SetValue(dop, ConvertUtils.get_default(tup.type));
def.OrderParams[p.ParamName] = dop;
}
}
}
return def;
}
static DynamicOrderParam.ValueOneofCase _get_value_case(DynamicOrderPrimitiveDescriptor primitive_descriptor_case) {
switch (primitive_descriptor_case) {
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveBool: return DynamicOrderParam.ValueOneofCase.AsBool;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveDouble: return DynamicOrderParam.ValueOneofCase.AsBool;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt: return DynamicOrderParam.ValueOneofCase.AsInt32;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: return DynamicOrderParam.ValueOneofCase.AsUint32;
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: return DynamicOrderParam.ValueOneofCase.AsString;
default: throw ExceptionBuilder.Argument.unexpected_case(primitive_descriptor_case, nameof(primitive_descriptor_case));
}
}
static bool _is_child_algo_desc(DynamicOrderParamDescriptor desc) =>
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive &&
desc.ParamType.Primitive == DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef;
static bool _is_value_param_desc(DynamicOrderParamDescriptor desc) =>
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated ||
(desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive && desc.ParamType.Primitive != DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef);
}
}
}
@@ -1,28 +1,36 @@
using bts.api.orderman;
using mbts.collections;
using mbts.ui;
using mbts.ui.shell;
using mbts.spark.utilities;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Linq;
using System.Reactive.Linq;
namespace mbts.spark {
static partial class OrderTypeVMFactory {
class DynamicOrderVM : OrderTypeVMBase<DynamicOrderDefinition, DynamicOrderDefinitionImmutable> {
readonly ObservableKeyedCollection<DynamicOrderParamVM, string> _parameters = new ObservableKeyedCollection<DynamicOrderParamVM, string>(vm => vm.descriptor.ParamName);
readonly Property<string> _dyn_type;
OrdersModel _model;
bool _active;
public DynamicOrderVM(AlgoKey key, string dyn_type, string display_name, AlgoEditingPermissions editing_permissions)
: base(key, display_name, editing_permissions) {
_dyn_type = add_property(nameof(this.dyn_type), DynamicOrderDefinition.DynamicOrderTypeFieldNumber, dyn_type);
_parameters = new ObservableKeyedCollection<DynamicOrderParamVM, string>(vm => vm.descriptor.ParamName);
_resources.AddRange(
_dyn_type.Subscribe(_ => _reset_dynamic_types()),
_dyn_type.Subscribe(_ => {
_sync_dynamic_params();
sync_selectors();
}),
ObservableAutoWrapper.connect_item_hooks(_parameters.AsCollection(), pvm => {
add_property(pvm.descriptor.ParamName, mdef => mdef.OrderParams[pvm.descriptor.ParamName] = pvm.param, mdef => {
if (mdef.OrderParams.TryGetValue(pvm.descriptor.ParamName, out DynamicOrderParam p))
pvm.param = p;
});
return Disposable.Create(() => remove_property(pvm.descriptor.ParamName));
}),
ObservableAutoWrapper.connect_item_hooks(_parameters, p => {
p.PropertyChanged += on_param_prop_changed;
return Disposable.Create(() => p.PropertyChanged -= on_param_prop_changed);
@@ -31,103 +39,26 @@ namespace mbts.spark {
}
public string dyn_type => _dyn_type.Value;
public ObservableCollection<DynamicOrderParamVM> parameters {
get {
_ensure_init();
return _parameters;
public ObservableCollection<DynamicOrderParamVM> parameters => _parameters;
protected override void on_model_internal() =>
_sync_dynamic_params();
protected override IEnumerable<ChildAlgoData> get_selector_datum() =>
OrderDataHelper.get_child_algo_data(new CustomOrderDefinition {
Dynamic = model?.dynamic_helper?.create_def(dyn_type) ?? new DynamicOrderDefinition()
});
void _sync_dynamic_params() {
if (model != null) {
var desc = model.dynamic_helper.get_descriptor(dyn_type);
_parameters.SyncWithDictionary(desc.ParamDesc.Where(_is_value_param_desc).ToDictionary(p => p.ParamName), DynamicOrderParamVM.create);
}
}
protected override void assign_to_internal(DynamicOrderDefinition dynamic_def) {
foreach (var vm in _parameters.Values)
dynamic_def.OrderParams[vm.descriptor.ParamName] = vm.param;
}
protected override void load_from_internal(DynamicOrderDefinitionImmutable dyn_defi) {
foreach (var p in _parameters) {
if (dyn_defi.OrderParams.TryGetValue(p.descriptor.ParamName, out DynamicOrderParam dop)) {
p.param = dop;
} else {
p.param.ClearValue();
}
}
}
protected override async Task on_load_from_async(CustomOrderDefinitionImmutable defi) {
await Wait.until_true(() => _model != null, TimeSpan.FromSeconds(60));
_ensure_init();
}
void _ensure_init() {
if (!_active) {
_active = true;
// lazy initialization is required to avoid a stack overflow (otherwise, the algo-hierarchy would grow recursively without stopping).
_reset_dynamic_types();
}
}
protected override void on_model_internal(OrdersModel model) {
_model = model;
_reset_dynamic_types();
}
void _reset_dynamic_types() {
if (!_active)
return;
var value_descs = new Dictionary<string, DynamicOrderParamDescriptor>();
var child_descs = new Dictionary<string, DynamicOrderParamDescriptor>();
if (_model != null) {
var desc = _model.dynamic_helper.get_descriptor(dyn_type);
if (desc != null) {
foreach (var p in desc.ParamDesc) {
if (_is_value_param_desc(p)) {
value_descs[p.ParamName] = p;
} else if (_is_child_algo_desc(p)) {
child_descs[p.ParamName] = p;
}
}
}
}
_parameters.SyncWithDictionary(value_descs, DynamicOrderParamVM.create);
using (reset_selectors()) {
foreach (var kv in child_descs) {
add_selector(kv.Key, get_child, set_child);
// local methods
CustomOrderDefinitionImmutable? get_child(DynamicOrderDefinitionImmutable dyn_defi) {
if (dyn_defi.OrderParams.TryGetValue(kv.Key, out DynamicOrderParam p) && p.ValueCase == DynamicOrderParam.ValueOneofCase.AsOrderDef)
return new CustomOrderDefinitionImmutable(p.AsOrderDef);
return null;
}
void set_child(DynamicOrderDefinition dyn_def, CustomOrderDefinition child) =>
dyn_def.OrderParams[kv.Key] = new DynamicOrderParam { AsOrderDef = child };
}
}
}
static bool _is_child_algo_desc(DynamicOrderParamDescriptor desc) =>
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive &&
desc.ParamType.Primitive == DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef;
static bool _is_value_param_desc(DynamicOrderParamDescriptor desc) =>
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated ||
(desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive && desc.ParamType.Primitive != DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef);
public class SelectorVM : ViewModelBase {
readonly IAlgoSelector _selector;
public SelectorVM(IAlgoSelector selector) {
_selector = selector;
_selector.selected_algo_changed += _on_selected_algo_changed;
}
public IAlgoVM selected_algo { get => _selector.selected_algo; set => _selector.selected_algo = value; }
public ObservableCollection<IAlgoVM> algos => _selector.algos;
public void Dispose() =>
_selector.selected_algo_changed -= _on_selected_algo_changed;
void _on_selected_algo_changed(IAlgoVM oldval, IAlgoVM newval) =>
RaisePropertyChanged(nameof(selected_algo));
}
}
}
}
@@ -113,11 +113,12 @@ namespace mbts.spark {
readonly IDictionary<string, object> _children_vm = new ExpandoObject();
protected CompositeDisposable _resources = new CompositeDisposable();
readonly List<(Action<TMutable> assign, Action<TMutable> load)> _auto_props = new List<(Action<TMutable> assign, Action<TMutable> load)>();
readonly ObservableKeyedCollection<(string name, Action<TMutable> assign, Action<TMutable> load), string> _auto_props = new ObservableKeyedCollection<(string name, Action<TMutable> assign, Action<TMutable> load), string>(t => t.name);
readonly ObservableKeyedCollection<AlgoSelectorData, string> _child_algo_selector_data = new ObservableKeyedCollection<AlgoSelectorData, string>(sd => sd.prop_name);
readonly ObservableAutoWrapper<IAlgoSelector, AlgoSelectorData> _child_algo_selectors;
readonly Property<bool> _is_expanded, _can_save_values, _can_save_structure;
readonly DeferrableActionWrapper _def_change_notifier;
IDisposable _pending_load;
protected OrderTypeVMBase(AlgoKey id, string display_name, AlgoEditingPermissions editing_permissions) {
this.id = id;
@@ -127,9 +128,7 @@ namespace mbts.spark {
_can_save_values = backing(nameof(can_save_values), true);
_can_save_structure = backing(nameof(can_save_structure), true);
_is_expanded = backing<bool>(nameof(is_expanded));
foreach (var c in OrderDataHelper.get_child_algo_data(order_type)) {
_children_vm[c.name] = add_selector(c.name, c.descriptor);
}
meta = OrderDataHelper.get_metadata(order_type);
_def_change_notifier = new DeferrableActionWrapper(() => definition_changed?.Invoke());
PropertyChanged += (s, e) => {
switch (e.PropertyName) {
@@ -170,39 +169,41 @@ namespace mbts.spark {
}, true));
}
public AlgoEditingPermissions editing_permissions { get; private set; }
public event Action definition_changed;
// core
public AlgoDefi definition => new AlgoDefi(this.created_assigned());
public AlgoType order_type => OrderDataHelper.get_order_type(new TMutable());
public bool is_bts_type => display_name.StartsWith("BTS");
public AlgoKey id { get; }
// display
public bool is_bts_type => display_name.StartsWith("BTS");
public string display_name { get; private set; }
public OrderMetadata meta => OrderDataHelper.get_metadata(order_type);
public virtual bool can_expand => !meta.NoProps;
public virtual bool is_expanded {
get => _is_expanded.Value;
set => _is_expanded.Value = value;
}
public OrderMetadata meta { get; }
// child algos
public dynamic children_vm => _children_vm;
public ObservableCollection<IAlgoSelector> child_algo_selectors => _child_algo_selectors;
// editing permissions
public AlgoEditingPermissions editing_permissions { get; private set; }
public bool can_edit_values => EnumHelper.includes_flag(editing_permissions, AlgoEditingPermissions.AllowEditValues);
public bool can_edit_structure => EnumHelper.includes_flag(editing_permissions, AlgoEditingPermissions.AllowEditStructure);
public bool can_save_values => _can_save_values.Value;
public bool can_save_structure => _can_save_structure.Value;
// misc
public virtual bool can_expand => !meta.NoProps;
public virtual bool is_expanded { get => _is_expanded.Value; set => _is_expanded.Value = value; }
// protected (observed props)
protected OrdersModel model { get; private set; }
protected SecurityMD? security_md { get; private set; }
protected SecurityImmutable? security => security_md?.security;
public void assign_to(AlgoDef value) {
var algo = new TMutable();
foreach (var (assign, load) in _auto_props)
foreach (var (name, assign, load) in _auto_props)
assign(algo);
_descriptor.Accessor.SetValue(value, algo);
foreach (var sd in _child_algo_selector_data) {
if (sd.selector.selected_algo != null)
sd.set_order(value, sd.selector.selected_algo.created_assigned());
}
assign_to_internal(algo);
}
public void set_permissions(AlgoEditingPermissions editing_permissions) {
@@ -217,26 +218,23 @@ namespace mbts.spark {
public void load_from(AlgoDefi value) {
if (value.CustomCase != order_type)
return;
using (_def_change_notifier.defer_execution()) {
foreach (var sd in _child_algo_selector_data) {
var inner_def = sd.get_order(value);
if (inner_def.HasValue) {
if (security.HasValue) {
inner_def = OrderDataHelper.fit_to_type(inner_def.Value, security.Value.ContractCase);
if (!inner_def.HasValue)
continue;
var algom = _descriptor.Accessor.GetValue(value.CloneToMutable()) as TMutable ?? new TMutable();
Disposable.replace(ref _pending_load,
new CompositeDisposable(
ObservableAutoWrapper.connect_item_actions(_child_algo_selector_data.AsCollection(), sd => {
var inner_def = sd.get_order(value);
if (inner_def.HasValue) {
if (security.HasValue) {
inner_def = OrderDataHelper.fit_to_type(inner_def.Value, security.Value.ContractCase);
if (!inner_def.HasValue)
return;
}
var vm = sd.selector.algos.GetOrAdd(OrderDataHelper.get_shallow_factory_key(inner_def.Value), k => create(k, inner_def.Value.display_name(), inner_def.Value.CustomCase));
vm.load_from(inner_def.Value);
sd.selector.selected_algo = vm;
}
var vm = sd.selector.algos.GetOrAdd(OrderDataHelper.get_shallow_factory_key(inner_def.Value), k => create(k, inner_def.Value.display_name(), inner_def.Value.CustomCase));
vm.load_from(inner_def.Value);
sd.selector.selected_algo = vm;
}
}
var algom = _descriptor.Accessor.GetValue(value.CloneToMutable()) as TMutable ?? new TMutable();
var algoi = _create_immutable(algom);
foreach (var (assign, load) in _auto_props)
load(algom);
load_from_internal(algoi);
}
}),
ObservableAutoWrapper.connect_item_actions(_auto_props.AsCollection(), p => p.load(algom))));
}
public virtual async Task<AlgoDefi?> load_from_async(AlgoDefi defi) {
@@ -254,92 +252,72 @@ namespace mbts.spark {
}
}
var algom = _descriptor.Accessor.GetValue(defi.CloneToMutable()) as TMutable ?? new TMutable();
var algoi = _create_immutable(algom);
foreach (var (assign, load) in _auto_props)
foreach (var (name, assign, load) in _auto_props)
load(algom);
load_from_internal(algoi);
return defi;
}
}
protected virtual Task on_load_from_async(AlgoDefi defi) => Task.CompletedTask;
protected virtual void assign_to_internal(TMutable value) { }
protected virtual void load_from_internal(TImmutable value) { }
protected void raise_definition_changed() =>
_def_change_notifier.execute();
protected virtual void validate() { }
protected virtual void on_permissions_changed() {
foreach (var vm in child_algo_selectors.SelectMany(s => s.algos.AsCollection()))
vm.set_permissions(editing_permissions);
}
protected void raise_definition_changed() =>
_def_change_notifier.execute();
protected virtual void validate() { }
protected override void Dispose(bool disposing) =>
Disposable.dispose(ref _resources);
#region Chidl Algo Selectors (add/remove/reset)
protected IAlgoSelector add_selector(string vm_prop_name, FieldDescriptor inner_desc) {
return add_selector(vm_prop_name, def => {
if (_descriptor.Accessor.GetValue(def.CloneToMutable()) is TMutable mval) {
if (inner_desc.Accessor.GetValue(mval) is AlgoDef inner_def)
return new AlgoDefi(inner_def);
}
return null;
}, (mdef, inner_def) => {
if (_descriptor.Accessor.GetValue(mdef) is TMutable mval)
inner_desc.Accessor.SetValue(mval, inner_def);
});
}
protected IAlgoSelector add_selector(string prop_name, Func<AlgoDefi, AlgoDefi?> get_order, Action<AlgoDef, AlgoDef> set_order) =>
add_selector(prop_name, get_order, set_order, (s, e) => validate());
protected IAlgoSelector add_selector(string prop_name, Func<AlgoDefi, AlgoDefi?> get_order, Action<AlgoDef, AlgoDef> set_order, EventHandler<DataErrorsChangedEventArgs> on_errors_changed) {
var rv = new AlgoSelector();
_child_algo_selector_data[prop_name] = new AlgoSelectorData(prop_name, rv, get_order, set_order);
return rv;
}
protected IAlgoSelector add_selector(string prop_name, Func<TImmutable, AlgoDefi?> get_algo, Action<TMutable, AlgoDef> set_algo) {
var rv = new AlgoSelector();
_child_algo_selector_data[prop_name] = new AlgoSelectorData(prop_name, rv, def => _descriptor.Accessor.GetValue(def.CloneToMutable()) is TMutable mval ? get_algo(_create_immutable(mval)) : null,
(def, child_def) => {
if (_descriptor.Accessor.GetValue(def) is TMutable mval)
set_algo(mval, child_def);
});
return rv;
}
protected bool remove_selector(string prop_name) =>
_child_algo_selector_data.Remove(prop_name);
/// TODO: maybe <see cref="_child_algo_selector_data"/> should just be a protected field?
/// Conversely, it is nice to hide these sort of implementation-specific details from inheriting classes.
/// Fwiw, as of 2/12/2019, the only class that needs the ability to add/remove selectors dynamically is <see cref="DynamicOrderVM"/>.
/// The other implementations simply add a whatever selectors they need in their constructor.
protected IDisposable reset_selectors() {
var to_remove = _child_algo_selector_data.Keys.ToList();
_child_algo_selector_data.CollectionChanged += on_change;
void on_change(object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action) {
case NotifyCollectionChangedAction.Reset:
throw new InvalidOperationException($"{nameof(_child_algo_selector_data)} was reset; unable to continue processing incremental changes.");
default:
// catches Replace/Remove/Add ops (Add ops account for cases where the item was effectively replaced by a remove/re-add)
if (e.NewItems != null) {
foreach (AlgoSelectorData sd in e.NewItems)
to_remove.Remove(sd.prop_name);
}
break;
#region Child Algo Selectors
protected virtual IEnumerable<ChildAlgoData> get_selector_datum() =>
OrderDataHelper.get_child_algo_data(order_type);
protected void sync_selectors() {
using (reset()) {
foreach (var c in get_selector_datum()) {
_children_vm[c.name] = mk_selector(c.name, new Func<AlgoDefi, AlgoDefi?>(defi => {
var mchild = c.get_child(defi.CloneToMutable());
if (mchild != null)
return new AlgoDefi(mchild);
return null;
}),
c.assign_child);
}
_populate_selectors();
}
// local methods
IDisposable reset() {
var to_remove = _child_algo_selector_data.Keys.ToList();
_child_algo_selector_data.CollectionChanged += on_change;
void on_change(object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action) {
case NotifyCollectionChangedAction.Reset:
throw new InvalidOperationException($"{nameof(_child_algo_selector_data)} was reset; unable to continue processing incremental changes.");
default:
// catches Replace/Remove/Add ops (Add ops account for cases where the item was effectively replaced by a remove/re-add)
if (e.NewItems != null) {
foreach (AlgoSelectorData sd in e.NewItems)
to_remove.Remove(sd.prop_name);
}
break;
}
}
return Disposable.Create(() => {
_child_algo_selector_data.CollectionChanged -= on_change;
foreach (var id in to_remove)
_child_algo_selector_data.Remove(id);
});
}
IAlgoSelector mk_selector(string prop_name, Func<AlgoDefi, AlgoDefi?> get_order, Action<AlgoDef, AlgoDef> set_order) {
var rv = new AlgoSelector();
_child_algo_selector_data[prop_name] = new AlgoSelectorData(prop_name, rv, get_order, set_order);
return rv;
}
return Disposable.Create(() => {
_child_algo_selector_data.CollectionChanged -= on_change;
foreach (var id in to_remove)
_child_algo_selector_data.Remove(id);
});
}
#endregion
@@ -347,7 +325,7 @@ namespace mbts.spark {
protected Property<T> add_property<T>(string prop_name, int target_field_number, T init_value = default) {
var prop = backing(prop_name, init_value);
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
_auto_props.Add((mval => {
_auto_props.Add((prop_name, mval => {
if (mval != null)
field_desc.Accessor.SetValue(mval, prop.Value);
}, mval => {
@@ -356,22 +334,12 @@ namespace mbts.spark {
return prop;
}
protected UserProperty<T> add_user_property<T>(string prop_name, int target_field_number, T init_value = default) {
var prop = user_backing(prop_name, init_value);
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
add_property(
mdef => ConvertUtils.try_convert<T>(field_desc.Accessor.GetValue(mdef)),
(mdef, val) => field_desc.Accessor.SetValue(mdef, val),
val => prop.bypass_setter(val),
() => prop.Value);
return prop;
}
protected Property<TImmutableValue> add_property<TMutableValue, TImmutableValue>(string prop_name, int target_field_number, TImmutableValue init_value = default) where TMutableValue : class, IMessage, new() where TImmutableValue : IImmutableMessage {
var prop = backing(prop_name, init_value);
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
var immutable_factory = OrderTypeVMUtils.immutable_factory<TMutableValue, TImmutableValue>();
add_property<TMutableValue>(
prop_name,
mdef => field_desc.Accessor.GetValue(mdef) as TMutableValue,
(mdef, mval) => field_desc.Accessor.SetValue(mdef, mval),
mval => prop.set(immutable_factory(mval ?? new TMutableValue())),
@@ -383,15 +351,36 @@ namespace mbts.spark {
return prop;
}
protected void add_property<TValue>(Func<TMutable, TValue> get_from_def, Action<TMutable, TValue> assign_to_def, Action<TValue> set_local, Func<TValue> get_local) =>
_auto_props.Add((def => {
protected void add_property<TValue>(string prop_name, Func<TMutable, TValue> get_from_def, Action<TMutable, TValue> assign_to_def, Action<TValue> set_local, Func<TValue> get_local) =>
_auto_props.Add((
prop_name,
def => {
assign_to_def(def, get_local());
}, def => {
set_local(get_from_def(def));
}));
protected virtual void on_model_internal(OrdersModel model) { }
protected virtual void on_security_md_internal(SecurityMD? security_md) { }
protected void add_property(string prop_name, Action<TMutable> assign, Action<TMutable> load) =>
_auto_props[prop_name] = (prop_name, assign, load);
protected UserProperty<T> add_user_property<T>(string prop_name, int target_field_number, T init_value = default) {
var prop = user_backing(prop_name, init_value);
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
add_property(
prop_name,
mdef => ConvertUtils.try_convert<T>(field_desc.Accessor.GetValue(mdef)),
(mdef, val) => field_desc.Accessor.SetValue(mdef, val),
val => prop.bypass_setter(val),
() => prop.Value);
return prop;
}
protected void remove_property(string prop_name) =>
_auto_props.Remove(prop_name);
#endregion
protected virtual void on_model_internal() { }
protected virtual void on_security_md_internal() { }
// model
void IObserver<OrdersModel>.OnNext(OrdersModel model) {
@@ -402,36 +391,39 @@ namespace mbts.spark {
return;
}
this.model = model;
on_model_internal(model);
_reset();
using (_def_change_notifier.defer_execution()) {
on_model_internal();
sync_selectors();
}
}
void IObserver<OrdersModel>.OnError(Exception error) { }
void IObserver<OrdersModel>.OnCompleted() { }
// security md
void IObserver<SecurityMD?>.OnNext(SecurityMD? security_md) {
this.security_md = security_md;
on_security_md_internal(security_md);
_reset();
using (_def_change_notifier.defer_execution()) {
on_security_md_internal();
_populate_selectors();
}
}
void IObserver<SecurityMD?>.OnError(Exception error) { }
void IObserver<SecurityMD?>.OnCompleted() { }
IDisposable _hook;
void _reset() {
IDisposable _selector_data_hooks;
void _populate_selectors() {
if (model != null && security.HasValue) {
var algo_data = model.factory.default_algos(security.Value);
var default_algo = new AlgoKey(OrderDataHelper.get_default_algo(OrderDataHelper.get_algo_format(security.Value.ContractCase)));
foreach (var sd in _child_algo_selectors)
sd.load_algo_data_recursive(algo_data, default_algo);
Disposable.replace(ref _hook, ObservableAutoWrapper.connect_item_hooks(child_algo_selectors, s => {
s.load_algo_data_recursive(algo_data, default_algo);
return Disposable.empty;
}));
Disposable.replace(
ref _selector_data_hooks,
ObservableAutoWrapper.connect_item_hooks(child_algo_selectors, s => {
s.load_algo_data_recursive(algo_data, default_algo);
return Disposable.empty;
}));
}
}
public IDisposable Subscribe(IObserver<OrdersModel> observer) => throw new NotImplementedException();
#endregion
}
struct AlgoSelectorData {
@@ -521,4 +513,4 @@ namespace mbts.spark {
}
#endregion
}
}
}
@@ -14,8 +14,6 @@ namespace mbts.spark {
class SpreaderOrderVM : OrderTypeVMBase<SpreaderDef, SpreaderDefi>, IAlgoVM, ILazyAlgo, IObserver<SecurityMD?>, IObserver<OrdersModel> {
readonly Property<SpreaderDefi?> _template;
readonly StrategyEditorWrapper _strategy_editor_wrapper;
OrdersModel _model;
SecurityMD? _security_md;
IDisposable _strategy_sub;
public SpreaderOrderVM(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions)
@@ -23,7 +21,38 @@ namespace mbts.spark {
_template = backing<SpreaderDefi?>(nameof(template));
/// <see cref="_strategy_editor_wrapper"/> depends on <see cref="_template"/> (i.e. initialize the latter first).
_strategy_editor_wrapper = new StrategyEditorWrapper(this);
concat_disposable(_strategy_editor_wrapper, _template.Subscribe(vals => validate()));
_resources.AddRange(_strategy_editor_wrapper, _template.Subscribe(vals => validate()));
// strategy
add_property(
nameof(SpreaderDef.ExecutionStrategy),
mdef => mdef.ExecutionStrategy,
(mdef, val) => mdef.ExecutionStrategy = val,
val => {
if (template.HasValue) {
template = template.Value.with(execution_strategy: val);
} else {
var mdef = new SpreaderDef {
Security = security?.CloneToMutable(),
ExecutionStrategy = val
};
template = new SpreaderDefi(mdef);
}
},
() => strategy_editor?.get_strategy().CloneToMutable());
// security
add_property(
nameof(SpreaderDef.Security),
mdef => mdef.Security,
(mdef, val) => mdef.Security = val,
val => {
if (template.HasValue) {
template = template.Value.with(security: val);
} else {
var mdef = new SpreaderDef { Security = val };
template = new SpreaderDefi(mdef);
}
},
() => security?.CloneToMutable());
validate();
}
@@ -43,12 +72,6 @@ namespace mbts.spark {
}
public bool is_loaded { get; private set; }
protected override void assign_to_internal(SpreaderDef value) =>
value.MergeFrom(template.HasValue ? template.Value.CloneToMutable() : new SpreaderDef());
protected override void load_from_internal(SpreaderDefi value) =>
_template.set(value);
protected override void validate() {
base.validate();
if (!template.HasValue)
@@ -56,26 +79,15 @@ namespace mbts.spark {
else clear_error(nameof(template));
}
void IObserver<SecurityMD?>.OnCompleted() { }
void IObserver<SecurityMD?>.OnError(Exception error) { }
void IObserver<SecurityMD?>.OnNext(SecurityMD? security_md) {
_security_md = security_md;
_reset_strategy_sub();
}
void IObserver<OrdersModel>.OnCompleted() { }
void IObserver<OrdersModel>.OnError(Exception error) { }
void IObserver<OrdersModel>.OnNext(OrdersModel model) {
_model = model;
_reset_strategy_sub();
}
protected override void on_security_md_internal() => _reset_strategy_sub();
protected override void on_model_internal() => _reset_strategy_sub();
void _reset_strategy_sub() {
Disposable.dispose(ref _strategy_sub);
if (_model != null) {
if (_security_md.HasValue) {
_strategy_sub = _model.spreader.security_strategy(_security_md.Value.security.Id)
.Select(strategy => new SpreaderDefi?(_model.factory.spreader_orderi(_security_md.Value.security, strategy)))
if (model != null) {
if (security_md.HasValue) {
_strategy_sub = model.spreader.security_strategy(security_md.Value.security.Id)
.Select(strategy => new SpreaderDefi?(model.factory.spreader_orderi(security_md.Value.security, strategy)))
.OnDispatcher()
.Subscribe(s => {
is_loaded = true;
@@ -115,7 +127,7 @@ namespace mbts.spark {
}
}
OrdersModel _model => _owner._model;
OrdersModel _model => _owner.model;
SpreaderDefi? _template { get => _owner.template; set => _owner.template = value; }
public void Dispose() {
@@ -31,11 +31,6 @@ namespace mbts.spark {
public ContractSpecificationImmutable? spec => _security_md.spec_or_default();
protected override void load_from_internal(StopLimitDefi value) {
// TODO: the StopLimitOrderDefinition, StopLimitOrderDefinitionImmutable needs relative vs absolute prices to function as a useful template (talk to Pedro)
_stop_price.bypass_setter(value.StopPrice);
}
// no need to constantly poll the md, just check the inline props when the popup opens, and right before sending an order
// (if the user never looks at the popup, assume that the bid_price will be used)
// TODO: add settings so the trigger-price can default to something relative to the md, e.g "bid-price" or "ask-price - nticks" or "best-bid-price with a minimum size of 10"
@@ -16,14 +16,26 @@ namespace mbts.spark {
public TimeBoxOrderVM(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions)
: base(key, display_name, editing_permissions) {
add_property(def => def.Start, (def, val) => def.Start = val, val => absolute_start = TimeTextTuple.FromDateTime(val.ToDateTime()), () => {
_validate(out DateTime? start_time, out DateTime? _);
return start_time.GetValueOrDefault().ToUniversalTime().ToGoogleTime();
});
add_property(def => def.Stop, (def, val) => def.Stop = val, val => absolute_stop = TimeTextTuple.FromDateTime(val.ToDateTime()), () => {
_validate(out DateTime? _, out DateTime? stop_time);
return stop_time.GetValueOrDefault().ToUniversalTime().ToGoogleTime();
});
// start
add_property(
nameof(TimeBoxOrderDefinition.Start),
def => def.Start,
(def, val) => def.Start = val,
val => absolute_start = TimeTextTuple.FromDateTime(val.ToDateTime()),
() => {
_validate(out DateTime? start_time, out DateTime? _);
return start_time.GetValueOrDefault().ToUniversalTime().ToGoogleTime();
});
// stop
add_property(
nameof(TimeBoxOrderDefinition.Stop),
def => def.Stop,
(def, val) => def.Stop = val,
val => absolute_stop = TimeTextTuple.FromDateTime(val.ToDateTime()),
() => {
_validate(out DateTime? _, out DateTime? stop_time);
return stop_time.GetValueOrDefault().ToUniversalTime().ToGoogleTime();
});
validate();
}
@@ -1,5 +1,4 @@
using bts.api;
using bts.api.orderman;
using bts.api.orderman;
using mbts.ui.utility;
using mbts.utils;
using System;
@@ -31,13 +30,59 @@ namespace mbts.spark {
_is_priceout_checked = create_user_prop(nameof(is_priceout_checked), false);
_is_timeout_checked = create_user_prop(nameof(is_timeout_checked), false);
_timeout_secs = create_user_prop(nameof(timeout_secs), 10);
add_property(nameof(TriggerDef), assign, load);
validate();
//
// local methods
UserProperty<T> create_user_prop<T>(string name, T default_value = default) {
var rv = user_backing(name, default_value);
rv.Changed += validation_callback;
return rv;
}
void assign(TriggerDef trigger_def) {
trigger_def.TriggerAction = new TriggerAction();
if (security.HasValue)
trigger_def.TriggerAction.SecurityId = security.Value.Id;
trigger_def.TriggerCondition = new TriggerCondition() {
PriceType = trigger_price_type,
AbsolutePriceTrigger = new AbsolutePriceTrigger() { TriggerQuantity = new TriggerQuantity() },
};
trigger_def.ExitCondition = new TriggerExitCondition {
ConditionalCancel = is_priceout_checked,
TimeoutEnabled = is_timeout_checked,
TimeoutSeconds = (uint)(timeout_secs)
};
switch (order_price_case) {
case OrderpriceOneofCase.Price:
// 'fixed price' is no longer supported (ca. 03/04/2019)
goto case OrderpriceOneofCase.Payup;
case OrderpriceOneofCase.Payup:
trigger_def.TriggerAction.Price = double.NaN;
trigger_def.TriggerAction.Payup = new TriggerPayupPrice {
PayupTicks = payup_ticks,
PayupPriceType = payup_price_type
};
break;
}
}
void load(TriggerDef trigger_defi) {
if (trigger_defi.TriggerCondition is TriggerCondition c) {
trigger_price_type = c.PriceType;
}
if (trigger_defi.ExitCondition is TriggerExitCondition ec) {
is_priceout_checked = ec.ConditionalCancel;
is_timeout_checked = ec.TimeoutEnabled;
if (ec.TimeoutSeconds < int.MaxValue)
timeout_secs = (int)ec.TimeoutSeconds;
}
if (trigger_defi.TriggerAction is TriggerAction a) {
if (a.Payup is TriggerPayupPrice pp) {
order_price_case = OrderpriceOneofCase.Payup;
fixed_price = double.NaN;
payup_ticks = pp.PayupTicks;
payup_price_type = pp.PayupPriceType;
}
}
}
void validation_callback<T>(T oldval, T newval) => validate();
}
@@ -56,68 +101,20 @@ namespace mbts.spark {
public bool is_timeout_checked { get => _is_timeout_checked.Value; set => _is_timeout_checked.Value = value; }
public int timeout_secs { get => _timeout_secs.Value; set => _timeout_secs.Value = value; }
protected override void assign_to_internal(TriggerDef trigger_def) {
trigger_def.TriggerAction = new TriggerAction();
if (security.HasValue)
trigger_def.TriggerAction.SecurityId = security.Value.Id;
trigger_def.TriggerCondition = new TriggerCondition() {
PriceType = trigger_price_type,
AbsolutePriceTrigger = new AbsolutePriceTrigger() { TriggerQuantity = new TriggerQuantity() },
};
trigger_def.ExitCondition = new TriggerExitCondition {
ConditionalCancel = is_priceout_checked,
TimeoutEnabled = is_timeout_checked,
TimeoutSeconds = (uint)(timeout_secs)
};
switch (order_price_case) {
case OrderpriceOneofCase.Price:
// should already be null, setting it here for clarity
trigger_def.TriggerAction.Payup = null;
trigger_def.TriggerAction.Price = fixed_price;
break;
case OrderpriceOneofCase.Payup:
trigger_def.TriggerAction.Price = double.NaN;
trigger_def.TriggerAction.Payup = new TriggerPayupPrice {
PayupTicks = payup_ticks,
PayupPriceType = payup_price_type
};
break;
}
}
protected override void load_from_internal(TriggerDefi trigger_defi) {
if (trigger_defi.HasTriggerCondition) {
trigger_price_type = trigger_defi.TriggerCondition.PriceType;
}
if (trigger_defi.HasExitCondition) {
var exit_condition = trigger_defi.ExitCondition;
is_priceout_checked = exit_condition.ConditionalCancel;
is_timeout_checked = exit_condition.TimeoutEnabled;
if (exit_condition.TimeoutSeconds < int.MaxValue)
timeout_secs = (int)exit_condition.TimeoutSeconds;
}
if (trigger_defi.HasTriggerAction) {
var trigger_action = trigger_defi.TriggerAction;
if (trigger_action.HasPayup) {
order_price_case = OrderpriceOneofCase.Payup;
fixed_price = double.NaN;
var payup = trigger_action.Payup;
payup_ticks = payup.PayupTicks;
payup_price_type = payup.PayupPriceType;
} else {
order_price_case = OrderpriceOneofCase.Price;
fixed_price = trigger_action.Price;
}
}
}
void _refresh_md() {
protected override void on_security_md_internal() {
if (security.HasValue) {
security_md.Value.md.get_bbo(ref _bbo_ref, out co2.NativePriceSize bid, out co2.NativePriceSize ask);
_best_bid = bid.price;
_best_ask = ask.price;
security_md.Value.md.get_last_trade(ref _last_trade_ref, out co2.NativePriceSize ps, out DateTime ts, out ulong v);
}
if (double.IsNaN(fixed_price) || !_fixed_price.setter_called) {
switch (trigger_price_type) {
case TriggerPriceType.Bid: _fixed_price.bypass_setter(_best_bid); break;
case TriggerPriceType.Ask: _fixed_price.bypass_setter(_best_ask); break;
case TriggerPriceType.LastTradePrice: _fixed_price.bypass_setter(_last_trade_price); break;
}
}
}
protected override void validate() {
@@ -131,17 +128,6 @@ namespace mbts.spark {
}
assert(!is_timeout_checked || timeout_secs > 0, nameof(timeout_secs), "Please specify valid Timeout(secs).");
}
protected override void on_security_md_internal(SecurityMD? security_md) {
_refresh_md();
if (double.IsNaN(fixed_price) || !_fixed_price.setter_called) {
switch (trigger_price_type) {
case TriggerPriceType.Bid: _fixed_price.bypass_setter(_best_bid); break;
case TriggerPriceType.Ask: _fixed_price.bypass_setter(_best_ask); break;
case TriggerPriceType.LastTradePrice: _fixed_price.bypass_setter(_last_trade_price); break;
}
}
}
}
}
}
+71 -6
View File
@@ -3,9 +3,11 @@ using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.position;
using bts.md;
using bts.spark;
using bts.utils;
using mbts.collections;
using mbts.config;
using mbts.spark.utilities;
using mbts.utils;
using System;
@@ -16,8 +18,10 @@ using System.ComponentModel.Composition;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using OrderUpdate = bts.api.core.TableUpdate<(bts.api.SecurityImmutable? sec, bts.api.orderman.OrderRecordImmutable order), uint>;
using OrderUpdatePiq = bts.api.core.TableUpdate<bts.api.orderman.OrderSecurity, uint>;
namespace mbts.spark {
[Export]
@@ -26,21 +30,26 @@ namespace mbts.spark {
readonly OrderFactory _factory;
readonly OrderService _service;
readonly PositionService _position_service;
readonly MBOService _mbo_service;
readonly SpreaderModel _spreader;
readonly ErrorReporter _error;
readonly DynamicOrderHelper _dynamic_helper;
readonly OrderActivitySummarizer _activity;
readonly TableUpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_demuxer;
// we need a new one of these for the time being since the ladder is subscribing to orders with piq
readonly TableUpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_piq_demuxer;
readonly TableUpdateDemuxer<PositionUnitImmutable, int, uint> _security_positions_demuxer;
IDisposable _dispose;
static readonly bool _piq_enabled = Configuration.get<bts.config.toggles.PlacementInQueue>().enabled;
[ImportingConstructor]
OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service,
OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, MBOService mbo_service,
SpreaderModel spreader, DynamicOrderHelper dynamic_helper, AdminModel admin_model, IAlgoStudioModel algo_model, SecurityStore sstore, ErrorReporter error) {
_bus = bus;
_factory = factory;
_service = service;
_position_service = position_service;
_mbo_service = mbo_service;
_spreader = spreader;
_error = error;
_dynamic_helper = dynamic_helper;
@@ -48,13 +57,34 @@ namespace mbts.spark {
this.algo_model = algo_model;
var scheduler = ThreadPoolScheduler.Instance;
var interval = TimeSpan.FromMilliseconds(100);
on_orders =
on_orders =
_service
.subscribei_orders()
.Naggle(interval, scheduler)
.PermissionFilter(admin_model.on_admin_data)
.join(sstore, order => order.security_key())
.Table().Cache();
.Table()
.Cache();
if (_piq_enabled) {
on_orders_with_piq = on_orders
.LeftJoin<
(SecurityImmutable?, OrderRecordImmutable),
OrderPIQImmutable,
OrderSecurity,
uint
>(mbo_service.subscribei_piq().Table(),
si_ort => si_ort.Item2.Key,
p => p.OrderId,
(si_ort, r, p) => new OrderSecurity(si_ort.Item1, si_ort.Item2, p.Position),
(si_ort, r) => new OrderSecurity(si_ort.Item1, si_ort.Item2, null),
osp => osp.order.Key);
} else {
on_orders_with_piq = on_orders
.Select((ft) => new OrderSecurity(ft.Item1, ft.Item2, null), os => os.order.Key)
.Replay(1);
}
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
working_orders =
on_orders.Scan(
@@ -62,6 +92,13 @@ namespace mbts.spark {
(workers, table) => _update_working_orders(workers, table))
.Replay(1).RefCount();
working_orders_with_piq =
on_orders_with_piq.Scan(
ImmutableDictionary<uint, OrderSecurity>.Empty,
(workers, table) => _update_working_orders(workers, table))
.Replay(1).RefCount();
on_order_securities =
on_orders
.as_iincremental()
@@ -70,6 +107,7 @@ namespace mbts.spark {
_activity = new OrderActivitySummarizer(bus, on_orders);
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
_security_orders_piq_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
_security_positions_demuxer = new TableUpdateDemuxer<PositionUnitImmutable, int, uint>(o => o.RecordId, p => p.SecurityId);
var algo_meta_join = algo_model.definitions.LeftJoin<AlgoData, AlgoDefinitionMetadata, (AlgoData algo, AlgoDefinitionMetadata? meta), AlgoKey>(
algo_model.metadata_service.subscribe_metadata().Table(m => m.id),
@@ -104,6 +142,7 @@ namespace mbts.spark {
on_fill_tuples = fill_order_tuples_join;
_dispose = Disposable.Create(
on_orders.Select(t => t.order, o => o.Key).Subscribe(_security_orders_demuxer),
on_orders_with_piq.RefCount().Select(t => t.order, o => o.Key).Subscribe(_security_orders_piq_demuxer),
on_positions.Subscribe(_security_positions_demuxer),
order_secs_algo_meta_join.Connect(),
algo_meta_join.Connect(),
@@ -121,11 +160,14 @@ namespace mbts.spark {
public SpreaderModel spreader => _spreader;
public DynamicOrderHelper dynamic_helper => _dynamic_helper;
public IObservable<OrderUpdate> on_orders { get; }
public IConnectableObservable<OrderUpdatePiq> on_orders_with_piq { get; }
public IObservable<TableUpdate<OrderSecurityAlgoMeta, uint>> on_order_tuples { get; }
public IObservable<IIncrementalUpdate<SecurityImmutable, uint>> on_order_securities { get; }
public IObservable<TableUpdate<FillTuple, string>> on_fill_tuples { get; }
public IObservable<IReadOnlyDictionary<uint,OrderSecurity>> working_orders { get; }
public IObservable<IReadOnlyDictionary<uint, OrderSecurity>> working_orders_with_piq { get; }
public IObservable<TableUpdate<PositionUnitImmutable, int>> on_positions { get; }
public AdminModel admin_model { get; }
public IAlgoStudioModel algo_model { get; }
@@ -197,8 +239,30 @@ namespace mbts.spark {
public void Dispose() =>
Disposable.dispose(ref _dispose);
static ImmutableDictionary<uint, OrderSecurity>
_update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdate table) {
static ImmutableDictionary<uint, OrderSecurity> _update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdate table) {
var clone = target.ToBuilder();
foreach (var o in table.removed) {
if (o.order.is_active()) {
clone.Remove(o.order.Key);
}
}
foreach (var o in table.changes) {
if (o.Old.order.is_active()) {
clone.Remove(o.Old.order.Key);
}
if (o.New.order.is_active()) {
clone.Add(o.New.order.Key, o.New);
}
}
foreach (var o in table.added) {
if (o.order.is_active()) {
clone.Add(o.order.Key, o);
}
}
return clone.ToImmutableDictionary();
}
static ImmutableDictionary<uint, OrderSecurity> _update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdatePiq table) {
var clone = target.ToBuilder();
foreach (var o in table.removed) {
if (o.order.is_active()) {
@@ -222,6 +286,7 @@ namespace mbts.spark {
}
}
// usinig new OrdersModel extensions to reduce the amount of code change needed to switch uis over to the new OrdersModel,
// without changing any code in the actual new OrdersModel class.
static class OrdersModelExtensions {
@@ -285,7 +350,7 @@ namespace mbts.spark {
// DO NOT REMOVE (END)
public static IObservable<ITableUpdateTransform<OrderRecordImmutable, uint>> on_security_root_orders(this OrdersModel orders_model, uint security_key) =>
orders_model.on_security_orders(security_key).Scan(new RootOrderData(), (rod, table) => {
orders_model.on_security_orders(security_key).AddRemoveOnly().Scan(new RootOrderData(), (rod, table) => {
// ** using HashSet/List pairs, rather than a Dictionary, because TableUpdateTransform calls for IReadOnlyLists, while Dictionary.Values only implements IReadOnlyCollection.
// (this way, we avoid copying both added/removed Dictionary.Values to new Lists at the end, see comment at bottom of method if this is unclear) **
var added_root_keys = new HashSet<uint>();
@@ -4,7 +4,7 @@ using System;
namespace mbts {
[ProtoContract]
class DiscreteStringPredicate : IDiscreteValuesPredicateBuilder<string>, IEquatable<DiscreteStringPredicate> {
class DiscreteStringPredicate : IDiscreteValuesPredicateData<string>, IEquatable<DiscreteStringPredicate> {
[ProtoMember(1)]
string[] _values;
[ProtoMember(2)]
@@ -4,7 +4,7 @@ using System;
namespace mbts {
[ProtoContract]
class DiscreteUIntPredicate : IDiscreteValuesPredicateBuilder<uint, uint?, uint[]>, IEquatable<DiscreteUIntPredicate> {
class DiscreteUIntPredicate : IDiscreteValuesPredicateData<uint, uint?, uint[]>, IEquatable<DiscreteUIntPredicate> {
[ProtoMember(1)]
uint[] _values;
[ProtoMember(2)]
@@ -16,7 +16,7 @@ namespace mbts {
public uint[] not_in_set { get => _get_set(true); set => _set(true, value); }
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
public IPredicate<uint> create(object obj) => DiscreteValuesPredicateUtils.create(this);
public IPredicate<uint> create() => DiscreteValuesPredicateUtils.create(this);
public bool Equals(DiscreteUIntPredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
public override bool Equals(object obj) => Equals(obj as DiscreteUIntPredicate);
@@ -8,14 +8,14 @@ using System.Collections.Generic;
using System.Linq;
namespace mbts {
interface IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> : IPredicateBuilder<T> {
interface IDiscreteValuesPredicateData<T, TSingle, TMulti> {
TSingle is_value { get; set; }
TSingle not_value { get; set; }
TMulti in_set { get; set; }
TMulti not_in_set { get; set; }
DiscreteValuesOneofCase predicate_type_case { get; }
}
interface IDiscreteValuesPredicateBuilder<T> : IDiscreteValuesPredicateBuilder<T, T, T[]> { }
interface IDiscreteValuesPredicateData<T> : IDiscreteValuesPredicateData<T, T, T[]> { }
public enum DiscreteValuesOneofCase {
None,
@@ -32,8 +32,8 @@ namespace mbts {
namespace mbts.ui.settings {
static class DiscreteValuesPredicateUtils {
public static bool equals<T>(IDiscreteValuesPredicateBuilder<T> a, IDiscreteValuesPredicateBuilder<T> b) => a.safe_equals(b, _equals);
static bool _equals<T>(IDiscreteValuesPredicateBuilder<T> a, IDiscreteValuesPredicateBuilder<T> b) {
public static bool equals<T>(IDiscreteValuesPredicateData<T> a, IDiscreteValuesPredicateData<T> b) => a.safe_equals(b, _equals);
static bool _equals<T>(IDiscreteValuesPredicateData<T> a, IDiscreteValuesPredicateData<T> b) {
var dvoc = a.predicate_type_case;
if (dvoc != b.predicate_type_case)
return false;
@@ -47,8 +47,8 @@ namespace mbts.ui.settings {
}
}
public static bool equals<T, TSingle, TMulti>(IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> a, IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> b) => a.safe_equals(b, _equals);
public static bool _equals<T, TSingle, TMulti>(IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> a, IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> b) {
public static bool equals<T, TSingle, TMulti>(IDiscreteValuesPredicateData<T, TSingle, TMulti> a, IDiscreteValuesPredicateData<T, TSingle, TMulti> b) => a.safe_equals(b, _equals);
public static bool _equals<T, TSingle, TMulti>(IDiscreteValuesPredicateData<T, TSingle, TMulti> a, IDiscreteValuesPredicateData<T, TSingle, TMulti> b) {
var dvoc = a.predicate_type_case;
if (dvoc != b.predicate_type_case)
return false;
@@ -62,7 +62,7 @@ namespace mbts.ui.settings {
}
}
public static int hash<T>(IDiscreteValuesPredicateBuilder<T> a) {
public static int hash<T>(IDiscreteValuesPredicateData<T> a) {
switch (a.predicate_type_case) {
case DiscreteValuesOneofCase.IsValue: return a.is_value.GetHashCode() % MathUtils.Primes.P_17191;
case DiscreteValuesOneofCase.NotValue: return a.not_value.GetHashCode() % MathUtils.Primes.P_83987;
@@ -72,7 +72,7 @@ namespace mbts.ui.settings {
}
}
public static int hash<T, TSingle, TMulti>(IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> a) {
public static int hash<T, TSingle, TMulti>(IDiscreteValuesPredicateData<T, TSingle, TMulti> a) {
switch (a.predicate_type_case) {
case DiscreteValuesOneofCase.IsValue: return a.is_value.GetHashCode() % MathUtils.Primes.P_17191;
case DiscreteValuesOneofCase.NotValue: return a.not_value.GetHashCode() % MathUtils.Primes.P_83987;
@@ -88,7 +88,7 @@ namespace mbts.ui.settings {
(negated ? DiscreteValuesOneofCase.NotValue : DiscreteValuesOneofCase.IsValue) :
(negated ? DiscreteValuesOneofCase.NotInSet : DiscreteValuesOneofCase.IsInSet);
public static DiscreteValuesOneofCase get_predicate_type_case<T, TSingle, TMulti>(IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> predicate_builder) {
public static DiscreteValuesOneofCase get_predicate_type_case<T, TSingle, TMulti>(IDiscreteValuesPredicateData<T, TSingle, TMulti> predicate_builder) {
if (predicate_builder.is_value != null) return DiscreteValuesOneofCase.IsValue;
if (predicate_builder.not_value != null) return DiscreteValuesOneofCase.NotValue;
if (predicate_builder.in_set != null) return DiscreteValuesOneofCase.IsInSet;
@@ -96,7 +96,7 @@ namespace mbts.ui.settings {
return DiscreteValuesOneofCase.None;
}
public static IPredicate<T> create<T>(IDiscreteValuesPredicateBuilder<T> predicate_builder) {
public static IPredicate<T> create<T>(IDiscreteValuesPredicateData<T> predicate_builder) {
switch (predicate_builder.predicate_type_case) {
case DiscreteValuesOneofCase.IsValue: return PredicateBuilder.equals(predicate_builder.is_value);
case DiscreteValuesOneofCase.NotValue: return PredicateBuilder.not_equals(predicate_builder.not_value);
@@ -106,7 +106,7 @@ namespace mbts.ui.settings {
}
}
public static IPredicate<T> create<T>(IDiscreteValuesPredicateBuilder<T, T?, T[]> predicate_builder) where T : struct {
public static IPredicate<T> create<T>(IDiscreteValuesPredicateData<T, T?, T[]> predicate_builder) where T : struct {
switch (predicate_builder.predicate_type_case) {
case DiscreteValuesOneofCase.IsValue: return PredicateBuilder.equals(predicate_builder.is_value.Value);
case DiscreteValuesOneofCase.NotValue: return PredicateBuilder.not_equals(predicate_builder.not_value.Value);
@@ -118,13 +118,13 @@ namespace mbts.ui.settings {
}
static class DiscreteValuesPredicateVMFactory {
public static Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> get_oneofs_value_type<T, TPredicate>(IEnumerable<T> items)
where T : struct where TPredicate : IDiscreteValuesPredicateBuilder<T, T?, T[]> =>
where T : struct where TPredicate : IDiscreteValuesPredicateData<T, T?, T[]> =>
//
get_oneofs<T, T?, T[], TPredicate>(identity_view_value_collection(items), val => val.Value, val => val, vals => vals ?? Enumerable.Empty<T>(), vals => vals?.ToArray() ?? new T[0], get_identity_view);
public static Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> get_oneofs<T, TSingle, TMulti, TPredicate>(
IObservableCollection<IViewValue<T>> items, Func<TSingle, T> from_single, Func<T, TSingle> to_single, Func<TMulti,IEnumerable<T>> from_multi, Func<IEnumerable<T>, TMulti> to_multi, Func<T, object> get_view)
where TPredicate : IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> {
where TPredicate : IDiscreteValuesPredicateData<T, TSingle, TMulti> {
//
return new Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> {
{ DiscreteValuesOneofCase.IsValue, PropertyWrapperVMUtils.create_selector_vm(
@@ -153,14 +153,14 @@ namespace mbts.ui.settings {
public static Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> get_oneofs<T, TKey, TSingle, TMulti, TPredicate>(
IEnumerable<T> items, Func<T, TKey> key, Func<TSingle, TKey> from_single, Func<TKey, TSingle> to_single, Func<TMulti, IEnumerable<TKey>> from_multi, Func<IEnumerable<TKey>, TMulti> to_multi)
where TKey : struct, IEquatable<TKey>
where TPredicate : IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> =>
where TPredicate : IDiscreteValuesPredicateData<T, TSingle, TMulti> =>
//
get_oneofs<T, TKey, TSingle, TMulti, TPredicate>(RXCollection.from_enumerable(items.Select<T, IViewValue<T>>(v => new ViewValueTuple<T>(v, v))), key, from_single, to_single, from_multi, to_multi);
public static Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> get_oneofs<T, TKey, TSingle, TMulti, TPredicate>(
IObservableCollection<IViewValue<T>> items, Func<T, TKey> key, Func<TSingle, TKey> from_single, Func<TKey, TSingle> to_single, Func<TMulti, IEnumerable<TKey>> from_multi, Func<IEnumerable<TKey>, TMulti> to_multi)
where TKey : struct, IEquatable<TKey>
where TPredicate : IDiscreteValuesPredicateBuilder<T, TSingle, TMulti> {
where TPredicate : IDiscreteValuesPredicateData<T, TSingle, TMulti> {
//
return new Dictionary<DiscreteValuesOneofCase, ITemplatedPropertyWrapperVM<TPredicate>> {
{ DiscreteValuesOneofCase.IsValue, PropertyWrapperVMUtils.create_keyed_selector_vm<TPredicate, IViewValue<T>, TKey>(
@@ -5,7 +5,7 @@ using System;
namespace mbts {
[ProtoContract]
class ExchangePredicate : IDiscreteValuesPredicateBuilder<Venue, Venue?, Venue[]>, IEquatable<ExchangePredicate> {
class ExchangePredicate : IDiscreteValuesPredicateData<Venue, Venue?, Venue[]>, IEquatable<ExchangePredicate> {
[ProtoMember(1)]
Venue[] _values;
[ProtoMember(2)]
@@ -17,7 +17,7 @@ namespace mbts {
public Venue[] not_in_set { get => _get_set(true); set => _set(true, value); }
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
public IPredicate<Venue> create(object obj) => DiscreteValuesPredicateUtils.create(this);
public IPredicate<Venue> create() => DiscreteValuesPredicateUtils.create(this);
public bool Equals(ExchangePredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
public override bool Equals(object obj) => Equals(obj as ExchangePredicate);
@@ -5,7 +5,7 @@ using System;
namespace mbts {
[ProtoContract]
class OrderStatusPredicate : IDiscreteValuesPredicateBuilder<OrderStatus, OrderStatus?, OrderStatus[]>, IEquatable<OrderStatusPredicate> {
class OrderStatusPredicate : IDiscreteValuesPredicateData<OrderStatus, OrderStatus?, OrderStatus[]>, IEquatable<OrderStatusPredicate> {
[ProtoMember(1)]
OrderStatus[] _values;
[ProtoMember(2)]
@@ -17,7 +17,7 @@ namespace mbts {
public OrderStatus[] not_in_set { get => _get_set(true); set => _set(true, value); }
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
public IPredicate<OrderStatus> create(object obj) => DiscreteValuesPredicateUtils.create(this);
public IPredicate<OrderStatus> create() => DiscreteValuesPredicateUtils.create(this);
public bool Equals(OrderStatusPredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
public override bool Equals(object obj) => Equals(obj as OrderStatusPredicate);
@@ -5,7 +5,7 @@ using System;
namespace mbts {
[ProtoContract]
class OrderSubTypePredicate : IDiscreteValuesPredicateBuilder<OrderSubType, OrderSubType?, OrderSubType[]>, IEquatable<OrderSubTypePredicate> {
class OrderSubTypePredicate : IDiscreteValuesPredicateData<OrderSubType, OrderSubType?, OrderSubType[]>, IEquatable<OrderSubTypePredicate> {
[ProtoMember(1)]
OrderSubType[] _values;
[ProtoMember(2)]
@@ -17,7 +17,7 @@ namespace mbts {
public OrderSubType[] not_in_set { get => _get_set(true); set => _set(true, value); }
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
public IPredicate<OrderSubType> create(object obj) => DiscreteValuesPredicateUtils.create(this);
public IPredicate<OrderSubType> create() => DiscreteValuesPredicateUtils.create(this);
public bool Equals(OrderSubTypePredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
public override bool Equals(object obj) => Equals(obj as OrderSubTypePredicate);
@@ -5,7 +5,7 @@ using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
namespace mbts {
[ProtoContract]
class OrderTypePredicate : IDiscreteValuesPredicateBuilder<OrderType, OrderType?, OrderType[]>, IEquatable<OrderTypePredicate> {
class OrderTypePredicate : IDiscreteValuesPredicateData<OrderType, OrderType?, OrderType[]>, IEquatable<OrderTypePredicate> {
[ProtoMember(1)]
OrderType[] _values;
[ProtoMember(2)]
@@ -17,7 +17,7 @@ namespace mbts {
public OrderType[] not_in_set { get => _get_set(true); set => _set(true, value); }
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negated);
public IPredicate<OrderType> create(object obj) => DiscreteValuesPredicateUtils.create(this);
public IPredicate<OrderType> create() => DiscreteValuesPredicateUtils.create(this);
public bool Equals(OrderTypePredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
public override bool Equals(object obj) => Equals(obj as OrderTypePredicate);
@@ -5,7 +5,7 @@ using System;
namespace mbts {
[ProtoContract]
class TimeInForcePredicate : IDiscreteValuesPredicateBuilder<TimeInForce, TimeInForce?, TimeInForce[]>, IEquatable<TimeInForcePredicate> {
class TimeInForcePredicate : IDiscreteValuesPredicateData<TimeInForce, TimeInForce?, TimeInForce[]>, IEquatable<TimeInForcePredicate> {
[ProtoMember(1)]
TimeInForce[] _values;
[ProtoMember(2)]
@@ -17,7 +17,7 @@ namespace mbts {
public TimeInForce[] not_in_set { get => _get_set(true); set => _set(true, value); }
public DiscreteValuesOneofCase predicate_type_case => DiscreteValuesPredicateUtils.get_predicate_type_case(_values, _negate);
public IPredicate<TimeInForce> create(object obj) => DiscreteValuesPredicateUtils.create(this);
public IPredicate<TimeInForce> create() => DiscreteValuesPredicateUtils.create(this);
public bool Equals(TimeInForcePredicate other) => DiscreteValuesPredicateUtils.equals(this, other);
public override bool Equals(object obj) => Equals(obj as TimeInForcePredicate);
@@ -4,7 +4,6 @@ using bts.shell;
using bts.utils;
using mbts.spark;
using mbts.ui.settings;
using mbts.utils;
using ProtoBuf;
using System;
using System.Collections.Generic;
@@ -14,7 +13,7 @@ namespace mbts {
static class OrderCanceledEventPredicateVMFactory {
public static Dictionary<OrderCanceledEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderCanceledEventPredicate>> get_oneofs(IBus bus) =>
new Dictionary<OrderCanceledEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderCanceledEventPredicate>> {
{ OrderCanceledEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateBuilderVMWrapper.create<OrderCanceledEventPredicate>(p => p.security = null) },
{ OrderCanceledEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateVMWrapper.create<OrderCanceledEventPredicate>(p => p.security = null) },
{ OrderCanceledEventPredicate.PredicateTypeOneofCase.SecurityOrder, SecurityCriteriaVMFactory.create<OrderCanceledEventPredicate>(bus, o => o.security?.is_value ?? new SecurityCriteria(),
(p, s) => {
if (p.security == null)
@@ -26,7 +25,7 @@ namespace mbts {
}
[ProtoContract]
class OrderCanceledEventPredicate : IPredicateBuilder<OrderSecurity, INotificationServiceDataCache>, IEquatable<OrderCanceledEventPredicate> {
class OrderCanceledEventPredicate : IEquatable<OrderCanceledEventPredicate> {
public enum PredicateTypeOneofCase {
[Description("(any order)")]
AnyOrder,
@@ -52,7 +51,7 @@ namespace mbts {
OrderEventPredicateHelper.create_status_transition_predicate(OrderStatus.Canceled, security, data_cache);
public INotificationPredicate<OrderSecurity> create_toast(INotificationServiceDataCache data_cache) =>
new MyToaster(security?.create(data_cache), data_cache);
new MyToaster(security?.create(), data_cache);
public bool Equals(OrderCanceledEventPredicate other) => other != null && other.security.safe_equals(other.security);
public override bool Equals(object obj) => Equals(obj as OrderCanceledEventPredicate);
@@ -4,7 +4,6 @@ using bts.shell;
using bts.utils;
using mbts.spark;
using mbts.ui.settings;
using mbts.utils;
using ProtoBuf;
using System;
using System.Collections.Generic;
@@ -14,7 +13,7 @@ namespace mbts {
static class OrderCompletedEventPredicateVMFactory {
public static Dictionary<OrderCompletedEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderCompletedEventPredicate>> get_oneofs(IBus bus) =>
new Dictionary<OrderCompletedEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderCompletedEventPredicate>> {
{ OrderCompletedEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateBuilderVMWrapper.create<OrderCompletedEventPredicate>(p => p.security = null) },
{ OrderCompletedEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateVMWrapper.create<OrderCompletedEventPredicate>(p => p.security = null) },
{ OrderCompletedEventPredicate.PredicateTypeOneofCase.SecurityOrder, SecurityCriteriaVMFactory.create<OrderCompletedEventPredicate>(bus, o => o.security?.is_value ?? new SecurityCriteria(),
(p, s) => {
if (p.security == null)
@@ -26,7 +25,7 @@ namespace mbts {
}
[ProtoContract]
class OrderCompletedEventPredicate : IPredicateBuilder<OrderSecurity, INotificationServiceDataCache>, IEquatable<OrderCompletedEventPredicate> {
class OrderCompletedEventPredicate : IEquatable<OrderCompletedEventPredicate> {
public enum PredicateTypeOneofCase {
[Description("(any order)")]
AnyOrder,
@@ -52,7 +51,7 @@ namespace mbts {
OrderEventPredicateHelper.create_status_transition_predicate(OrderStatus.Completed, security, data_cache);
public INotificationPredicate<OrderSecurity> create_toast(INotificationServiceDataCache data_cache) =>
new MyToaster(security?.create(data_cache), data_cache);
new MyToaster(security?.create(), data_cache);
public bool Equals(OrderCompletedEventPredicate other) => other != null && other.security.safe_equals(other.security);
public override bool Equals(object obj) => Equals(obj as OrderCompletedEventPredicate);
@@ -2,8 +2,6 @@
using bts.api.orderman;
using bts.shell;
using bts.utils;
using mbts.spark;
using mbts.utils;
using System;
namespace mbts {
@@ -39,7 +37,7 @@ namespace mbts {
}
public static IPredicate<OrderSecurity> create_status_transition_predicate(OrderStatus status, SecurityPredicate security_predicate, INotificationServiceDataCache data_cache) =>
new OrderStatusTransitionPredicate(status, security_predicate?.create(data_cache), data_cache);
new OrderStatusTransitionPredicate(status, security_predicate?.create(), data_cache);
class OrderStatusTransitionPredicate : IPredicate<OrderSecurity>, IEquatable<OrderStatusTransitionPredicate> {
readonly IPredicate<SecurityImmutable> _security_predicate;
@@ -11,7 +11,7 @@ namespace mbts {
static class OrderFilledEventPredicateVMFactory {
public static Dictionary<OrderFilledEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderFilledEventPredicate>> get_oneofs(IBus bus) =>
new Dictionary<OrderFilledEventPredicate.PredicateTypeOneofCase, ITemplatedPropertyWrapperVM<OrderFilledEventPredicate>> {
{ OrderFilledEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateBuilderVMWrapper.create<OrderFilledEventPredicate>(p => p.security = null) },
{ OrderFilledEventPredicate.PredicateTypeOneofCase.AnyOrder, PredicateVMWrapper.create<OrderFilledEventPredicate>(p => p.security = null) },
{ OrderFilledEventPredicate.PredicateTypeOneofCase.SecurityOrder, SecurityCriteriaVMFactory.create<OrderFilledEventPredicate>(bus, o => o.security?.is_value ?? new SecurityCriteria(),
(p, s) => {
if (p.security == null) {
@@ -24,7 +24,7 @@ namespace mbts {
}
[ProtoContract]
class OrderFilledEventPredicate : IPredicateBuilder<OrderSecurity, INotificationServiceDataCache>, IEquatable<OrderFilledEventPredicate> {
class OrderFilledEventPredicate : IEquatable<OrderFilledEventPredicate> {
public enum PredicateTypeOneofCase {
[PredicateCase("(any order)")]
AnyOrder,
@@ -59,7 +59,7 @@ namespace mbts {
public override int GetHashCode() => security == null ? 0 : security.GetHashCode();
FillTriggerModel _create_model(INotificationServiceDataCache nsdc) =>
new FillTriggerModel(nsdc, security?.create(nsdc));
new FillTriggerModel(nsdc, security?.create());
class FillTriggerModel {
readonly IPredicate<SecurityImmutable> _security_predicate;
@@ -82,7 +82,7 @@ namespace mbts {
// old order will be a copy of new order, except with filledQuantity set to 0
var old_state = ort.order.State.CloneToMutable();
old_state.FilledQuantity = 0;
return (new OrderSecurity(ort.security, ort.order.with(state: old_state)), ort);
return (new OrderSecurity(ort.security, ort.order.with(state: old_state), ort.piq), ort);
}
}
return null;
@@ -56,12 +56,12 @@ namespace mbts {
Func<TChildPredicate, TChildPredicateOneof> key,
Dictionary<TChildPredicateOneof, ITemplatedPropertyWrapperVM<TChildPredicate>> oneofs) where TChildPredicateOneof : struct where TChildPredicate : new() =>
//
PredicateBuilderVMWrapper.wrap_child_oneof<OrderRecordTuplePredicate, TChildPredicate, TChildPredicateOneof>(get, set, key, oneofs);
PredicateVMWrapper.wrap_child_oneof<OrderRecordTuplePredicate, TChildPredicate, TChildPredicateOneof>(get, set, key, oneofs);
}
}
[ProtoContract]
class OrderRecordTuplePredicate : INotificationPredicateBuilder<OrderSecurity, INotificationServiceDataCache>, IEquatable<OrderRecordTuplePredicate> {
class OrderRecordTuplePredicate : IEquatable<OrderRecordTuplePredicate> {
SecurityPredicate _security;
SidePredicate _side;
DoublePredicate _price, _last_price, _avg_price;
@@ -208,39 +208,36 @@ namespace mbts {
public IPredicate<OrderSecurity> create_predicate(INotificationServiceDataCache data_cache) {
switch (predicate_type_case) {
case PTOC.Security: return wrap(_security, r => r.security ?? SecurityDataHelper.Defaults.SecurityImmutable);
case PTOC.Time: return wrap(_time_created, r => r.order.created());
case PTOC.LastChange: return wrap(_last_change, r => r.order.last_change());
case PTOC.User: return wrap(_user, r => r.order.user_id());
case PTOC.Account: return wrap(_account, r => r.order.account_id());
case PTOC.Exchange: return wrap(_exchange, r => r.security.exchange_or_default());
case PTOC.TimeInForce: return wrap(_time_in_force, r => r.order.time_in_force());
case PTOC.OrderType: return wrap(_order_type, r => r.order.order_type());
case PTOC.SubType: return wrap(_order_sub_type, r => r.order.sub_type());
case PTOC.Side: return wrap(_side, r => r.order.side());
case PTOC.Size: return wrap(_quantity, r => r.order.quantity());
case PTOC.Price: return wrap(_price, r => r.order.price());
case PTOC.Filled: return wrap(_filled_quantity, r => r.order.filled());
case PTOC.AvgFilled: return wrap(_avg_price, r => r.order.avg_price());
case PTOC.LastPrice: return wrap(_last_price, r => r.order.last_price());
case PTOC.Status: return wrap(_order_status, r => r.order.status());
case PTOC.LastMessage: return wrap(_last_message, r => r.order.last_msg());
case PTOC.SecurityKey: return wrap(_security_key, r => r.order.security_key());
case PTOC.OrderKey: return wrap(_order_key, r => r.order.Key);
case PTOC.ParentKey: return wrap(_parent_key, r => r.order.ParentKey);
case PTOC.Security: return wrap(_security.create(), r => r.security ?? SecurityDataHelper.Defaults.SecurityImmutable);
case PTOC.Time: return wrap(_time_created.create(), r => r.order.created());
case PTOC.LastChange: return wrap(_last_change.create(), r => r.order.last_change());
case PTOC.User: return wrap(_user.create(), r => r.order.user_id());
case PTOC.Account: return wrap(_account.create(), r => r.order.account_id());
case PTOC.Exchange: return wrap(_exchange.create(), r => r.security.exchange_or_default());
case PTOC.TimeInForce: return wrap(_time_in_force.create(), r => r.order.time_in_force());
case PTOC.OrderType: return wrap(_order_type.create(), r => r.order.order_type());
case PTOC.SubType: return wrap(_order_sub_type.create(), r => r.order.sub_type());
case PTOC.Side: return wrap(_side.create(), r => r.order.side());
case PTOC.Size: return wrap(_quantity.create(), r => r.order.quantity());
case PTOC.Price: return wrap(_price.create(), r => r.order.price());
case PTOC.Filled: return wrap(_filled_quantity.create(), r => r.order.filled());
case PTOC.AvgFilled: return wrap(_avg_price.create(), r => r.order.avg_price());
case PTOC.LastPrice: return wrap(_last_price.create(), r => r.order.last_price());
case PTOC.Status: return wrap(_order_status.create(), r => r.order.status());
case PTOC.LastMessage: return wrap(_last_message.create(), r => r.order.last_msg());
case PTOC.SecurityKey: return wrap(_security_key.create(), r => r.order.security_key());
case PTOC.OrderKey: return wrap(_order_key.create(), r => r.order.Key);
case PTOC.ParentKey: return wrap(_parent_key.create(), r => r.order.ParentKey);
case PTOC.FilledEvent: return _filled_event.create_predicate(data_cache);
case PTOC.CompletedEvent: return _completed_event.create_predicate(data_cache);
case PTOC.CanceledEvent: return _canceled_event.create(data_cache);
default: return null;
}
//
IPredicate<OrderSecurity> wrap<T>(IPredicateBuilder<T> trigger, Func<OrderSecurity, T> select) =>
trigger.create(data_cache).wrap<OrderSecurity,T>(select);
IPredicate<OrderSecurity> wrap<T>(IPredicate<T> trigger, Func<OrderSecurity, T> select) =>
trigger.wrap<OrderSecurity, T>(select);
}
IPredicate<OrderSecurity> IPredicateBuilder<OrderSecurity>.create(object obj) =>
create_predicate((INotificationServiceDataCache)obj);
public INotificationPredicate<OrderSecurity> create_notification_predicate(INotificationServiceDataCache notification_service_data_cache) {
if (!is_toast_enabled)
return null;
@@ -252,9 +249,6 @@ namespace mbts {
}
}
INotificationPredicate<OrderSecurity> INotificationPredicateBuilder<OrderSecurity>.create_notification_predicate(object obj) =>
create_notification_predicate((INotificationServiceDataCache)obj);
public bool Equals(OrderRecordTuplePredicate other) {
if (other == null)
return false;
@@ -378,9 +372,9 @@ namespace mbts {
_sound_animations = new List<SoundNotification> ( sound_animations );
}
public OrderRecordTuplePredicate predicate_builder => _predicate;
public OrderRecordTuplePredicate predicate_data => _predicate;
public IReadOnlyList<CellAnimation> cell_animations => _cell_animations ?? (_cell_animations = new List<CellAnimation>());
public IReadOnlyList<RowAnimation> row_animations => _row_animations ?? (_row_animations = new List<RowAnimation>());
public IReadOnlyList<SoundNotification> sound_notifications => _sound_animations ?? (_sound_animations = new List<SoundNotification>());
}
}
}
@@ -1,7 +1,6 @@
using bts.proto;
using bts.utils;
using mbts.spark;
using mbts.spark.utilities;
using mbts.ui.spark;
using ProtoBuf;
using System;
@@ -24,7 +23,7 @@ namespace mbts {
}
[ProtoContract]
class DoublePredicate : IPredicateBuilder<double>, IEquatable<DoublePredicate> {
class DoublePredicate : IEquatable<DoublePredicate> {
// not using a 'negate' flag here bc its meaning is not consistent for all value-types
// greater than vs less than for specific values, or inside vs outside for value-ranges
// trading a little more code for more clarity
@@ -62,7 +61,7 @@ namespace mbts {
}
}
public IPredicate<double> create(object obj) {
public IPredicate<double> create() {
switch (predicate_type_case) {
case PredicateTypeOneofCase.GreaterThan: return PredicateBuilder.greater_than(_greater_than.Value);
case PredicateTypeOneofCase.LessThan: return PredicateBuilder.less_than(_less_than.Value);
Loaded 100 of 293 files, more files were not shown because too many files have changed in this diff. Show more