Compare commits

..
Author SHA1 Message Date
Michael Harding 189157b1c7 initialize_for_test changes (thanks eric) 2019-06-13 14:50:24 -04:00
Michael Harding 64fd8addb2 Update Lean qty factor shell script 2019-06-13 09:38:11 -04:00
Michael Harding 4490ac303e SPARK-2438: Lean Qty test 2019-06-12 15:41:16 -04:00
Michael Harding 666bd07411 Merge branch 'master' into spreader_lean_qty 2019-06-10 12:56:44 -04:00
Pedro Pinto 0ff4624778 Updated release notes. 2019-06-10 11:05:59 -04:00
Taha Afzal ab04b4afab Merged in SPARK-2505_qho_ib_1 (pull request #254)
RTM
2019-06-10 15:02:57 +00:00
Taha Afzal ac226ddd3b Merged in SPARK-2488-LeanAlgoBug (pull request #255)
RTM
2019-06-10 14:36:18 +00:00
Pedro Pinto 0223e43aa2 Updated release notes. 2019-06-10 10:20:51 -04:00
John Colvin 73e5cfdada Merged in SPARK-2481-fills-filter-fix (pull request #249)
SPARK-2481 fills filter fix

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-06-10 14:19:31 +00:00
Eric Harding 33b4b88af1 For some strange reason the gregorian calender does not start counting days at 0. Maybe 0 hadn't been invented yet. 2019-06-10 10:12:44 -04:00
Pedro Pinto 12f4b4b25b Merged with master 2019-06-10 08:49:56 -04:00
Taha Afzal 96cacb8bb9 qho's were reporting their cancelled state twice, which was causing the spreader to cancel it 2019-06-09 21:16:41 -04:00
John Colvin 300a1ed17f SPARK-2481 filtering in fills window is badly broken (2nd take) 2019-06-07 23:06:14 -05:00
John Colvin 1a0836ca6d RXCollection.connect - always assign local table in callback 2019-06-07 20:41:04 -05:00
Jake Norbut 88908543e1 Merged in SPARK-2165 (pull request #244)
RTM - Ladder Settings - hide net change group setting for synthetic ladders

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-06-07 21:26:37 +00:00
Pedro Pinto 80c9b6a534 Merged with master 2019-06-07 17:26:05 -04:00
Pedro Pinto f6282c0676 Updated release notes 2019-06-07 17:20:17 -04:00
Pedro Pinto 02fe83f20b Merge branch 'master' into SPARK-2165 2019-06-07 17:15:18 -04:00
John Colvin 84d666c29c Merged in SPARK-2476-OB-KeyNotFoundException (pull request #248)
RTM: SPARK-2476 OB _get_legs KeyNotFoundException

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-06-07 21:14:14 +00:00
Pedro Pinto 147e43b464 Merged with master 2019-06-07 16:58:35 -04:00
Pedro Pinto b909a6062e Updated release notes 2019-06-07 15:58:32 -04:00
Pedro Pinto 421b3af6a7 Merge branch 'master' into SPARK-2481-fills-filter-fix 2019-06-07 15:54:05 -04:00
Pedro Pinto 46a340d493 Merged in fix-fill-accounting (pull request #251)
RTM. Fix fill accounting
2019-06-07 19:52:02 +00:00
Pedro Pinto 3cc7d76a1b Merged with master 2019-06-07 15:49:10 -04:00
Jake Norbut 5123b928f2 Merged in SPARK-2480 (pull request #247)
RTM - SPARK-2480 - fixing Composite order type showing in root algo

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-06-07 19:40:43 +00:00
Pedro Pinto c2631e3257 Merged with master 2019-06-07 15:16:34 -04:00
Pedro Pinto 685181ae28 Merge branch 'fix-fill-accounting' of bitbucket.org:bluetradesys/spark into fix-fill-accounting 2019-06-07 13:50:58 -04:00
Pedro Pinto 929492c2f0 Made Limiter more robust for orders with undiscovered securities. 2019-06-07 13:50:44 -04:00
Pedro Pinto be023ff6cb Made Limiter more robust for orders with undiscovered securities. 2019-06-07 13:21:06 -04:00
Taha Afzal a4d63a1421 this should fix the confusion 2019-06-06 19:13:46 -04:00
Michael Harding 2fd8c39888 Merge branch 'spreader_lean_qty' of bitbucket.org:bluetradesys/spark into spreader_lean_qty 2019-06-06 16:01:56 -04:00
Michael Harding eb97fd045a SPARK-2438: spreader_lean_qty_factor test setup 2019-06-06 16:01:16 -04:00
Michael Harding a07bc152d1 SPARK-2438: init spreader_lean_qty_factor test 2019-06-06 16:01:16 -04:00
Michael Harding aef8e2dbb1 spell check manual tests 2019-06-06 16:01:16 -04:00
Pedro Pinto baac6a893d Re-directed SecurityDB's expiration to securities::expiration 2019-06-06 11:14:56 -04:00
Pedro Pinto df681ca209 Removed duplicate declaration. 2019-06-06 11:03:36 -04:00
Jake Norbut 21b34434ef Merged in SPARK-2464 (pull request #233)
RTM - SPARK-2464 - ladder cursor provides visual feedback when middle clicking over bid ask cols

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-06-05 21:57:43 +00:00
Pedro Pinto 0c17dbb55b Updated release notes. 2019-06-05 17:57:11 -04:00
Pedro Pinto fd6c6a3055 Merge branch 'master' into SPARK-2464 2019-06-05 17:52:29 -04:00
Pedro Pinto 259355d629 Updated release notes. 2019-06-05 17:51:22 -04:00
Michael Harding c6c2d3448d Merged in net_contracts_fix (pull request #250)
RTM. Fix lazyview updating net cotracts
2019-06-05 21:49:54 +00:00
Eric Harding f860649996 Merged in SPARK-2472-rerun (pull request #245)
RTM. SPARK-2472 rerun

* SPARK-2482 - End Jake's left handed discrimination

* SPARK-2472 add * to our order size

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-06-05 21:43:03 +00:00
Pedro Pinto 05c3c97713 Hid fills for expired securities. 2019-06-05 17:11:14 -04:00
Pedro Pinto f3be277484 Fixed limits double counting of spread leg fills 2019-06-05 17:10:16 -04:00
John Colvin 0ba311788c DIYGridRowsManager - expose fills/sort manager methods, so callers don't need to hang on to fills/sort manager references 2019-06-05 11:35:32 -05:00
John Colvin 341eda5962 FillsVM - rows does not need to be an OperableCollection (just a regular ObservableCollection) 2019-06-05 11:35:32 -05:00
John Colvin 3398bb5b86 FillsVM - remove unusued field '_active_comparers' 2019-06-05 11:35:28 -05:00
John Colvin dc7cacc126 get rid of hashset-vs-array optimization (doesn't actually help) 2019-06-05 10:59:43 -05:00
John Colvin 6d3d4e1f56 SPARK-2481 Filtering in fills window is badly broken 2019-06-04 18:57:36 -05:00
John Colvin 433b1abecd abstract out OrderBookFilterStates.build_predicate method for generic use 2019-06-04 18:55:41 -05:00
John Colvin 4544180c5f move 'FillsStartupData' into its own file, change typename to 'FillsSettings' 2019-06-04 18:54:43 -05:00
John Colvin 5722fcffc3 SPARK-2476 OB _get_legs KeyNotFoundException 2019-06-04 16:08:47 -05:00
Jake Norbut f1880615b1 SPARK-2480 - fixing a particular overload of the OrderSecurity constructor (root_* specific) 2019-06-04 14:08:46 -05:00
Jake Norbut e60f5678ec PR nits from chat 2019-06-04 10:22:10 -05:00
Pedro Pinto e9a70e51e4 Merged with master 2019-06-04 10:40:56 -04:00
Jake Norbut e98d18a5aa Merged in SPARK-2482 (pull request #243)
RTM - SPARK-2482 - Ladder middle click change - middle click recentering is back; alt + middle click now brings up mbo popup

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-06-04 14:35:16 +00:00
Pedro Pinto 513cc248b8 Updated release notes and added shift+middle click to trigger MBO
(Alt+Middle Click doesn't work very well on the linux rdesktop client)
2019-06-04 10:33:54 -04:00
Pedro Pinto c024095f75 Merged master 2019-06-04 10:02:19 -04:00
Pedro Pinto 3e5eb80a7c Updated release notes 2019-06-04 09:57:59 -04:00
Eric Harding 942dc1640e Merged in SPARK-2472 (pull request #242)
RTM: 🏭 SPARK-2472 - our orders are hard to identify

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-06-04 13:17:29 +00:00
Pedro Pinto b5c3abb782 Merged from prod 2019-06-04 09:15:36 -04:00
Pedro Pinto 3478d1ad52 Updated release notes. 2019-06-04 09:14:10 -04:00
Pedro Pinto d54e9233d1 Improved comment. 2019-06-04 09:12:11 -04:00
Jake Norbut e94759cd89 Ladder Settings - hide net change group setting for synthetic ladders 2019-06-03 15:47:54 -05:00
Jake Norbut b58da4a20e SPARK-2482 - Ladder middle click change - middle click recentering is back; alt + middle click now brings up mbo popup 2019-06-03 12:43:44 -05:00
Jake Norbut 8426399a23 SPARK-2479 - bug introduced in 8dd0709; Contract Pickers launched via the Security Picker Daemon now close on cancel button being pressed 2019-06-03 12:11:17 -05:00
Eric Harding 9b2f41c805 SPARK-2472 our orders are hard to identify in mbo 2019-05-30 17:00:09 -04:00
Eric Harding 15f96dda3e Fix mbo command line diagnostic utility 2019-05-30 16:02:07 -04:00
Michael Harding a9d5c17d63 Merged master into spreader_lean_qty 2019-05-30 14:43:53 -04:00
Michael Harding 715b629778 Set use_onixs to false; for system tests 2019-05-30 12:04:21 -04:00
Eric Harding dd82d127ea Merged in shell_crash_empty_size (pull request #241)
RTM: Fix for shell able to send 0 size orders

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-05-29 21:37:27 +00:00
Pedro Pinto f97e22d6eb Updated release notes. 2019-05-29 17:31:34 -04:00
Eric Harding f9e04ee28c Fix for shell able to send 0 size orders 2019-05-29 17:31:24 -04:00
Pedro Pinto 1ce00ca13d Fixed simulator's handling of 0 sized quantities. 2019-05-29 17:27:08 -04:00
Jake Norbut 3b9c2f5d70 adding a try/catch around the leg lookup 2019-05-28 16:58:27 -05:00
Pedro Pinto 5cd2dfec18 Fixed PIQ in GLBX Icerberg, fixed handling of stop limit. 2019-05-28 16:26:16 -04:00
Jake Norbut 31b2ba9165 PR suggestion - if statement readability 2019-05-28 14:38:27 -05:00
Jake Norbut 2b1b1f1681 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-2464 2019-05-28 13:23:17 -05:00
Pedro Pinto 4427b02d8d _index.md edited online with Bitbucket 2019-05-28 16:11:11 +00:00
Jake Norbut dcd3921379 Merged in SPARK-2460 (pull request #238)
RTM - SPARK-2460

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2019-05-28 15:59:45 +00:00
Jake Norbut bd156ecbed fix merge 2019-05-28 10:58:02 -05:00
Eric Harding 02f5731e1d Merged in SPARK-2462 (pull request #235)
RTM: SPARK-2462 QueryServer crash
2019-05-28 15:54:15 +00:00
Jake Norbut 7e75a71898 check root algo's can_expand to see if we should draw Cursor.No 2019-05-28 10:47:11 -05:00
Eric Harding 798c4dcd49 Merged in SPARK-2194 (pull request #236)
RTM: SPARK-2194 synthetic book display needs more digits
2019-05-28 15:29:29 +00:00
Jake Norbut 7e47d7f8a5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-2464 2019-05-28 10:08:14 -05:00
Pedro Pinto 260ca06cde Merged in pnt_nits (pull request #240)
RTM. PnT work.
2019-05-28 14:57:57 +00:00
Jake Norbut 8615adb564 piq column to be instantiated with Right HorizontalAlignment 2019-05-28 08:18:03 -05:00
Pedro Pinto ab15d9d348 Merge branch 'master' into pnt_nits 2019-05-28 08:52:02 -04:00
Pedro Pinto 9540a15585 Updated release notes and fixed bug that brought Haris down. 2019-05-28 07:06:39 -04:00
Taha Afzal e8a467d5f1 1.Position limit check for QHO and 2. QHO crash for rejected orders 2019-05-27 16:03:44 -04:00
John Colvin 567c18f325 ManualFills - hardcode min width to 466 (hides horizontal scrollbar when window first opens) 2019-05-24 17:35:05 -05:00
John Colvin 270ab9c3f1 change 'row' to 'fill' in ImportConflicts view 2019-05-24 16:59:26 -05:00
John Colvin 9a9024c2c0 OrderBookView - order params/ladder grid splitter should be horizontally stretched (was only 5 pixels wide) 2019-05-24 16:58:52 -05:00
John Colvin 45a5049a2d fix typeo ('or' conditions should be in parenthesis) 2019-05-24 16:58:52 -05:00
Jake Norbut 8b17911da0 refining pnl test 2019-05-24 15:45:28 -05:00
Taha Afzal 61556eb534 'Algo->spreader is not sending the price over' is fixed 2019-05-24 16:43:56 -04:00
Pedro Pinto b691dbae80 Merged from master 2019-05-24 14:05:46 -04:00
Pedro Pinto f6be20ce8d Updated release notes. 2019-05-24 14:02:06 -04:00
Pedro Pinto 68428ab89d Ran clang format. 2019-05-24 13:59:31 -04:00
Pedro Pinto 12d54fdb31 Added manual test to validate spreader performance. 2019-05-24 13:58:59 -04:00
Pedro Pinto 366e13ef05 Fixes requoted performance regression introduced in commit e998edc 2019-05-24 13:56:37 -04:00
Jake Norbut e94ef6a602 Adding PIQ column to OB (and mini); more OrderSecurityAlgoMeta to OrderSecurity param changes 2019-05-24 11:53:02 -05:00
Jake Norbut 3e7f145290 switching on_order_tuples over to OrderSecurity as well so that we can toggle back and forth if necessary via config between orders w/ no piq and with those with 2019-05-24 09:43:50 -05:00
John Colvin 748a99d4ef SPARK-2425 Add day position to ladder 2019-05-23 20:22:17 -05:00
John Colvin 35b8c1e53f Ladder - settings driven properties should share 1 subscription to the settings 2019-05-23 18:38:43 -05:00
Jake Norbut 123b690c6c switch OB over to the new Observable, which means change use of OrderSecurtyAlgoMeta to OrderSecurity (which includes a piq) 2019-05-23 15:15:14 -05:00
Jake Norbut 38a65bd0e3 OrdersModel now includes an on_order_tuples_with_root_and_piq 2019-05-23 15:14:03 -05:00
John Colvin 1cb8c54225 SPARK-2454 Root algos show the incorrect color in the OB (and perhaps miniOB) 2019-05-23 14:04:18 -05:00
John Colvin 414833c0c5 normalize line endings in OrderBookSettings.cs 2019-05-23 14:03:02 -05:00
John Colvin 6cc4064cd3 normalize spacing in OrderBookRowVM.cs 2019-05-23 13:32:35 -05:00
Eric Harding 0fcdc9c493 Merged in SPARK-2474 (pull request #237)
RTM: SPARK-2474 Improve mdp3 error message for missing feed_handler

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-05-23 15:32:56 +00:00
Eric Harding 0f967ae4cd Bump up feed_engine default buffer to 256mb (support is using this as a default already) 2019-05-23 09:41:48 -04:00
Eric Harding a232d738a0 Add instructions to error message 2019-05-23 09:40:40 -04:00
Eric Harding 3d3ac4e49f SPARK-2474 Improve mdp3 error message for missing feed_handler 2019-05-23 09:24:29 -04:00
John Colvin 15a5abf1ad SPARK-2465 Use ladder middle click on bid/ask to launch parameterized order 2019-05-22 20:56:32 -05:00
Eric Harding 76218fd35a SPARK-2194 synthetic book display needs more digits 2019-05-22 17:35:10 -04:00
Pedro Pinto d0370c5dc7 Merge branch 'pnt_nits' of bitbucket.org:bluetradesys/spark into pnt_nits 2019-05-22 16:11:34 -04:00
Pedro Pinto b07700cdc7 Fixed some issues with position service and manual fill integration. 2019-05-22 16:11:13 -04:00
John Colvin 15de3bbfe9 SPARK-2456 GLBX Iceberg is a native order 2019-05-22 13:53:59 -05:00
John Colvin 8d2fa40dcc ManualFills - change placeholder row id from Guid to long, to ensure sort order (so new rows always appear at the bottom) 2019-05-22 13:18:57 -05:00
Eric Harding bced75ea50 Ubuntu 18.04 broke dev dns for mdreplay. Back to IP address. 2019-05-22 14:03:15 -04:00
Jake Norbut b48accf1f9 only iterate over workspaces that contains windows 2019-05-22 12:59:54 -05:00
Pedro Pinto 3758ac6101 Merge branch 'pnt_nits' of bitbucket.org:bluetradesys/spark into pnt_nits 2019-05-22 13:52:21 -04:00
Pedro Pinto ac45e9bdb2 WIP: manual fills in pos server. 2019-05-22 13:52:04 -04:00
John Colvin 769bad89a7 ManualFillsVM - placeholder rows weren't getting removed after they were persisted 2019-05-22 12:39:51 -05:00
John Colvin 7dc272506a ManualFills - focus 'add row' popup when it opens (hooks up the 'OK' button to the enter key) 2019-05-22 12:19:06 -05:00
John Colvin 86c787efaf ManualFills - make account field immutable for persisted rows 2019-05-22 11:42:19 -05:00
John Colvin f917f581c3 SPARK-2469 add 'account_name' field to ManualFill protobuf 2019-05-22 11:03:25 -05:00
Pedro Pinto ca1217cd64 _index.md edited online with Bitbucket 2019-05-22 14:59:05 +00:00
John Colvin 4ed4e2e5e4 Merged in backwards-compatibility (pull request #234)
Backwards compatibility

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-05-22 14:57:09 +00:00
Jake Norbut bb0171a2e4 preemptivly updating pnl test 2019-05-22 09:33:51 -05:00
John Colvin 88bf448e68 ICollectionView.subscribe_sorting_criterion extension was not notifying observers on subscribe 2019-05-21 22:25:36 -05:00
John Colvin 05a615038d ManualFills - added 'Save Selected' spark command 2019-05-21 22:24:43 -05:00
John Colvin 51cfd0e5bb ManualFillsVM initializes side to Buy' for new rows 2019-05-21 22:23:02 -05:00
John Colvin d2ee575f19 set 'size' dropdown width in ManualFillsRowEditor 2019-05-21 22:09:58 -05:00
John Colvin 0b76ffb4f3 DragDropHelper.subscribe_drag_coordinates uses 'regular' mouse events (not 'preview') 2019-05-21 21:22:09 -05:00
John Colvin 0f9b93115a ManualFills - don't access row_view.SortDescriptions in get_save_state, as it is invoked on main the thread (not the window's thread) 2019-05-21 21:17:56 -05:00
John Colvin 8cfc6a7591 normalize spacing in WPFExtensions.cs 2019-05-21 21:14:43 -05:00
John Colvin 9de2dd30bc change ObservableAutoWrapper.connect 'target' arg to IList 2019-05-21 20:37:50 -05:00
John Colvin 38005e3266 change ManualFills launcher icon to match the one in the window title/tab 2019-05-21 20:25:24 -05:00
John Colvin 26ae70f432 fix ContractPickerPopup 'CloseOnCancel' behavior 2019-05-21 20:23:17 -05:00
John Colvin f6ca8286ea SPARK-2467 fix backwards compatibility bug in orderbook settings 2019-05-21 19:05:13 -05:00
John Colvin ae471b07fa SPARK-2466 fix backwards compatibility bug in fills settings 2019-05-21 18:56:59 -05:00
Eric Harding e7cea797ba Simplify append logi 2019-05-21 16:40:07 -04:00
Eric Harding f16545dfaa SPARK-2462 Expunge tbb::concurrent_vector 2019-05-21 16:29:23 -04:00
Jake Norbut dcd62409f1 fixing merge 2019-05-21 15:22:54 -05:00
Eric Harding 11252b4300 Add concurrent test for swcv 2019-05-21 15:45:08 -04:00
Jake Norbut 90526a8bfd SPARK-2464 - ladder cursor provides visual feedback when middle clicking over bid ask cols 2019-05-21 14:18:58 -05:00
Eric Harding e815dade3a Improve SingleWriterConcurrentVector perf (push_back 30-21ns, iterate(2us to 1.3us). Now iterates faster than tbb_concurrent_vector 2019-05-21 14:21:51 -04:00
John Colvin 576be7e961 ManualFillsView - enable mouse drag behavior in new row popup 2019-05-21 12:23:03 -05:00
John Colvin 407de320f6 PopupAdorner - implement MouseDrag behavior to move popup element around 2019-05-21 12:23:03 -05:00
Jake Norbut bc0234a5e9 pnl refactoring - PnLCalculator class does nothing now, so remove it; note file still exists and has a differently named class therein 2019-05-21 10:58:23 -05:00
Jake Norbut e1229186de pnl refactoring - PnLModel didn't really do anything, so I remomed it 2019-05-21 10:45:59 -05:00
Jake Norbut a3e00217a1 pnl refactoring - moving cpp lookup, fill immutable pnl and standard pnl caclulations into helper class 2019-05-21 10:45:59 -05:00
Eric Harding 07fa55d317 Fix settingsmanager crash 2019-05-21 10:32:34 -04:00
John Colvin 16f0beb3d8 ManualFills - added conf to switch between inline/modal transactions (default to modal) 2019-05-20 19:54:10 -05:00
John Colvin d244c62c26 ManualFillsView - add 'size' column 2019-05-20 19:32:41 -05:00
John Colvin 44730204a5 fix PropertyChangedDemuxer.PropertyChannel bug on dispose 2019-05-20 19:03:20 -05:00
John Colvin bd47816d16 ManualFillsParser - don't pass null string key to dictionary 2019-05-20 15:57:58 -05:00
John Colvin 2f12f2609a added RowParser.parse_rows_checked, which supports partial rows (with missing cells) 2019-05-20 15:54:47 -05:00
John Colvin 9c489c03c6 accidentally gave the generic RowParser.AssignColumnValue<T> delegate a name specific to manual fills 2019-05-20 15:33:20 -05:00
Eric Harding 5ae8a66558 Add SingleWriterConcurrentVector 2019-05-20 15:32:48 -04:00
John Colvin 03882718a0 ManualFills - spark commands menu item groups (i.e., separators in ContextMenu) 2019-05-20 13:52:09 -05:00
John Colvin 0f21d9783b file extension filter for ManualFills import file dialog 2019-05-20 13:41:28 -05:00
John Colvin 2c547f37fb ManualFillsVM - use ObservableAggregateCollection to merge persisted/placeholder rows together 2019-05-20 13:41:28 -05:00
John Colvin a7f4944bd6 FillsVM - more functional implementation of row filters/sorting 2019-05-20 13:41:28 -05:00
Jake Norbut a3a33ffb6a vm now consumes data via position service 2019-05-20 13:13:48 -05:00
Jake Norbut b3db0fc4ca also extended to supported calculating pnl via PositionsUnit data instead of Fill data 2019-05-20 13:00:01 -05:00
Jake Norbut 0f066fdeb9 extended to supported calculating pnl via PositionsUnits instead of Fills 2019-05-20 12:58:59 -05:00
Jake Norbut 22f89490a3 adding SecurityMD lookup facility to model; this is ultimately for compatability with the PnLSummaryRow, of which a SecurityMD instance is a component of the row's key tuple 2019-05-20 12:55:09 -05:00
Jake Norbut 2137d37cbd adding SecurityImmutable? to on_positions and updating security_positions_demuxer for additional argument type 2019-05-20 12:51:24 -05:00
Jake Norbut 082a241bb9 passing the OrdersModel, from where the PositionUnit will be published from, down the line 2019-05-20 12:48:14 -05:00
Jake Norbut 0057f8f080 when determining average price, position service should use the fill's last_price() and not avg_price; using avg_price from partials leads to wildly inaccurate pnl 2019-05-20 12:29:34 -05:00
Michael Harding 83650f1e1b System tests: fix timeout, minor improvements 2019-05-20 11:13:36 -04:00
John Colvin 4768c1cd87 SPARK-2447 / SPARK-2458 import dialog 2019-05-19 17:52:03 -05:00
John Colvin 57b70684ea EnumHelper.get_values returns a cached strongly typed IReadOnlyList<T> (simple optimization) 2019-05-19 17:44:34 -05:00
John Colvin af64592f52 ReactiveThresholdMonitor use RefCountMap to support duplicate adds 2019-05-19 17:44:34 -05:00
John Colvin a1b89ccb34 change RefCountMap.keys from IEnumerable to IReadOnlyCollection 2019-05-19 17:44:34 -05:00
John Colvin 44f2a9daf7 RefCountMap implements IReadOnlyCollection 2019-05-19 17:44:34 -05:00
John Colvin 0e5833b275 SetStack - 'SoftCount' logic was incorrect 2019-05-19 17:44:34 -05:00
John Colvin bded6d334c ContractPickerPopup 'CloseOnOK' should just close the popup (not the owning window) 2019-05-19 17:44:34 -05:00
John Colvin 893c6702a2 make DictionaryExtensions.Join more efficient 2019-05-19 17:44:34 -05:00
John Colvin 627b8742c4 SPARK-2447 / SPARK-2457 main window view (for singular transactions) 2019-05-19 17:44:34 -05:00
John Colvin 1f7e0a672e fix DataGridBehavior.SingleClickEdit 2019-05-19 17:44:34 -05:00
John Colvin 29e8fd1050 PopupControl - set default background to ComboBox.Static.Background 2019-05-19 17:44:33 -05:00
John Colvin 6ce741bcaf ContractPickerPopup - set default padding=2,0 2019-05-19 17:44:33 -05:00
John Colvin 8dd070961a ContractPickerPopup - SelectedSecurity(MD) dependency props 2019-05-19 17:44:33 -05:00
John Colvin f6a3a276d9 ContractPickerPopup - load securities lazily 2019-05-19 17:44:33 -05:00
John Colvin 68c8828d4f Account/UserTuple implement IComparable 2019-05-19 17:44:33 -05:00
John Colvin 0d68ebbbea ReactiveThresholdMonitor should notify subscribers (if necessary), when subs are disposed 2019-05-19 17:44:33 -05:00
John Colvin cc04e79f8b made anonymous SparkWindow factory 2019-05-19 17:44:33 -05:00
John Colvin ac76462f7e DiscreteValuesFilterVM.CreateTableUpdate - removed unused 'row_key' param 2019-05-19 17:44:33 -05:00
John Colvin d20b30557f added ObservantAsyncCommand factory methods 2019-05-19 17:44:33 -05:00
John Colvin a6fa5f9852 default Button Template BorderThickness should use TemplateBinding (was hardcoded to 1) 2019-05-19 17:44:33 -05:00
John Colvin 0db165bf76 set wpf DataGridColumnHeader font size to 12 (matches diygrid) 2019-05-19 17:44:33 -05:00
John Colvin 705fd293ba 'find_ancestor_of_type()' does not check the same ancestor obj twice 2019-05-19 17:44:33 -05:00
Michael Harding 523002ac56 Fix System tests 2019-05-17 16:57:24 -04:00
Pedro Pinto 9c25a8751e Updated release notes. 2019-05-17 15:38:42 -04:00
Pedro Pinto 89fc99b59a Updated docker subrepo ptr 2019-05-17 15:37:52 -04:00
Eric Harding cd413d10ab Merged in SPARK-2363 (pull request #231)
RTM: SPARK-2363 - PIQ popup

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-05-17 19:35:39 +00:00
Eric Harding 1c5cc9c1bb Merged in SPARK-2455-crash-on-md-startup-server-si (pull request #232)
SPARK-2455 crash on sim startup

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-05-17 19:33:53 +00:00
Eric Harding 7965743a80 SPARK-2455 crash on sim startup 2019-05-17 15:20:53 -04:00
Pedro Pinto 2172f007a8 Fixed a few bugs in GLBX Iceberg order. 2019-05-17 15:19:11 -04:00
Michael Harding c9db5d13a9 System tests wait for system ready 2019-05-17 13:36:21 -04:00
Eric Harding 13e911760d Middle click off popup shoudl close popup 2019-05-17 11:46:06 -04:00
Eric Harding a12c3128a3 Don't allow mbo popup index number to flicker width 2019-05-17 11:45:22 -04:00
Eric Harding 6bb84b65a8 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-2363 2019-05-17 10:34:11 -04:00
Eric Harding 6fb1140a97 test review feedback 2019-05-17 10:29:12 -04:00
Eric Harding 0e449844ca Clarify this point 2019-05-17 10:25:28 -04:00
Eric Harding 476206edb0 add piq/mbo manual test 2019-05-17 10:21:12 -04:00
Eric Harding 3483ac0712 Code review feedback 2019-05-17 09:03:49 -04:00
Eric Harding d6e586c4ce SPARK2452 - Update release notes for MBO 2019-05-16 16:44:18 -04:00
Eric Harding 0f1735fba2 SPARK-2453 Do not end order on bid/ask middle click 2019-05-16 16:39:50 -04:00
Eric Harding e5254dcf7f btree_set benchmarks are veerry interesting 2019-05-16 15:40:17 -04:00
Eric Harding 7bd183c8a4 btree_set benchmarks are very interesting 2019-05-16 15:29:59 -04:00
Michael Harding aaf09b197b Fix sdk docs 2019-05-16 15:16:49 -04:00
Eric Harding 6081128b92 move sparkvm launch to the top of the debug menu 2019-05-16 15:11:26 -04:00
Eric Harding 6d6ecb33ba SPARK-2450 Simulator MBO realism ™ 2019-05-16 15:08:51 -04:00
Michael Harding 6e85612adc deleting the right files this time 2019-05-16 14:01:35 -04:00
Eric Harding f33a13bfc8 Wire mbo popup to ladder in order book 2019-05-16 13:36:19 -04:00
Jake Norbut c4eeac8d0a fixing ServiceTail time formatting option 2019-05-16 12:07:49 -05:00
Michael Harding 432f39a1fa Don't delete everything 2019-05-16 12:54:39 -04:00
Eric Harding 6084da64a2 merge master 2019-05-16 12:18:45 -04:00
Jake Norbut 5ae484e6c1 adding comment to method 2019-05-16 10:07:59 -05:00
Eric Harding a2d4eb7d10 SPARK-2444 MBO Polish 2 - the Scrollening 2019-05-16 10:43:23 -04:00
Michael Harding 8082ab12dd Delete everything 2019-05-16 10:37:00 -04:00
Jake Norbut b1def915d6 Merged in SPARK-2449 (pull request #229)
SPARK-2449 - if the badge only represents 1 order, that order's piq has to be the min piq
2019-05-15 23:04:56 +00:00
Jake Norbut 70bb22c966 SPARK-2449 - if the badge only represents 1 order, that order's piq has to be the min piq 2019-05-15 17:39:47 -05:00
Eric Harding 06520791fa SPARK-2444 MBO UI polish 2019-05-15 17:19:46 -04:00
Pedro Pinto 9d762e0777 Merge branch 'pnt_nits' of bitbucket.org:bluetradesys/spark into pnt_nits 2019-05-15 17:02:41 -04:00
Pedro Pinto ef53c70c7e Fixed some bugs in GLBX Iceberg order. 2019-05-15 17:02:25 -04:00
Jake Norbut 8cf4e37310 adding preliminary create, update, delete of manual fills into position service 2019-05-15 14:58:46 -05:00
Pedro Pinto 04e7180b69 Merge branch 'master' into pnt_nits 2019-05-15 15:40:45 -04:00
Jake Norbut d40d3615b2 mutable props that RaisePropertyChanged 2019-05-15 13:41:05 -05:00
Jake Norbut 4efec939aa Security to Nullable, props now have public setters; inherit from VMB 2019-05-15 13:28:55 -05:00
Jake Norbut a0f24d8227 making use of syntactic sugar for less typing 2019-05-15 13:19:37 -05:00
Jake Norbut a2278423f6 users to non nullable for ItemSource 2019-05-15 13:14:15 -05:00
Jake Norbut 7321d5d8f7 filling out account and user, creating ObservableCollections for them to be used as ItemSources 2019-05-15 13:13:29 -05:00
Jake Norbut 464426537e making use of new ManualFillTuple 2019-05-15 12:35:59 -05:00
Michael Harding ebb2d5618c Merge branch 'master' into spreader_lean_qty 2019-05-15 13:35:33 -04:00
John Colvin 7b9c10f47c ManualFillTuple - benign code style tweak 2019-05-15 12:26:50 -05:00
John Colvin 478640e808 ManualFillTuple join with account and user 2019-05-15 12:24:34 -05:00
Jake Norbut 1fa5e3c9af fixing test_join_qty_threshold() of JoinAlgoTests 2019-05-15 11:29:23 -05:00
Michael Harding 7fede58044 Make system tests fail 2019-05-15 11:35:17 -04:00
Pedro Pinto 090a7985c3 Fixed spelling mistakes in release notes. 2019-05-15 10:38:35 -04:00
Michael Harding ab993423a6 Merge branch 'master' into spreader_lean_qty 2019-05-15 10:30:16 -04:00
Pedro Pinto ab8802c3f1 More master merging 2019-05-15 09:51:18 -04:00
Michael Harding f5fa2ff23a Adding Pete's fixes to system test scripts 2019-05-15 09:42:23 -04:00
Taha Afzal 6a1b154061 Fixing the bug causing the broken unit test 2019-05-14 22:43:42 -04:00
Michael Harding 73a4e474f1 System Test scripts: jenkins improvements 2019-05-14 17:26:59 -04:00
Eric Harding 6f630a0e62 MBOService should indicate bid/ask 2019-05-14 17:03:17 -04:00
Pedro Pinto 42906f5a37 Merged 2019-05-14 16:58:54 -04:00
Jake Norbut 931ed76673 implementing IEditableObject via reflection 2019-05-14 14:58:54 -05:00
Jake Norbut ebc4e202fe mf_vm: rows should be populated now 2019-05-14 14:23:43 -05:00
Jake Norbut a7dda587ba orders model - combinging manual fills with securities 2019-05-14 14:19:45 -05:00
Pedro Pinto 1ace2b7a3f Merge remote-tracking branch 'origin' into pnt_nits 2019-05-14 14:45:04 -04:00
Michael Harding 4e30591837 Hopefully fixing line endings this time 2019-05-14 14:38:36 -04:00
Eric Harding 3144032ed6 Fix dictionary key with ShellLatency (add user) 2019-05-14 13:46:03 -04:00
Jake Norbut af2875e0ef vm skeleton 2019-05-14 12:00:25 -05:00
Michael Harding d9221e1053 fix system test scripts 2019-05-14 12:54:01 -04:00
Eric Harding bee1df45ad SPARK-2430 prioritize draining incremental queue over publishing shapshots 2019-05-14 11:52:51 -04:00
Pedro Pinto 316be12b71 Return order key in API create. This broke in 1944. 2019-05-14 11:46:27 -04:00
Pedro Pinto 6c51ff83d5 Release notes again. 2019-05-14 10:01:39 -04:00
Pedro Pinto 1cad5b6b39 Updated release notes. 2019-05-14 09:56:47 -04:00
Michael Harding 4138ab0730 Merged master into spreader_lean_qty 2019-05-14 09:55:20 -04:00
Michael Harding 171396ca57 Merged in system_tests (pull request #228)
System Tests
2019-05-14 13:51:33 +00:00
Pedro Pinto 193eb4e445 Merged in SPARK-1944-attach/detach/update (pull request #141)
SPARK-1944 attach/detach/update
2019-05-14 13:48:17 +00:00
Pedro Pinto 69b3831271 Merged with master 2019-05-14 08:58:30 -04:00
Pedro Pinto 39efacace4 Updated release notes 2019-05-14 08:57:32 -04:00
Eric Harding e70dfc8832 Merged in SPARK-2151 (pull request #225)
RTM: SPARK-2151 Dump shared memory on bad handle for forensic analysis
2019-05-14 12:55:17 +00:00
Jake Norbut 60bf851567 Merged in SPARK-2364 (pull request #222)
RTM - settingsmanager - print descriptive workspace info after selecting a user's workspace to copy

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-05-14 12:54:03 +00:00
Jake Norbut 053038df8d Merged in piq_ladder_dirty (pull request #226)
RTM - don't draw piq on every tick; might save some cycles

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2019-05-14 12:53:22 +00:00
Eric Harding f0afc74e5f Merged in slightly_fast_webui (pull request #220)
RTM: Apply lazyView to the limits tables

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-05-14 12:51:38 +00:00
Eric Harding 9d3c8aaf5c Merged in SPARK-2303-assert-failure-trade.key-inva (pull request #224)
RTM: Safe workaround for SPARK-2303.
2019-05-14 12:49:34 +00:00
John Colvin 86467b2be1 SPARK-2411 / SPARK-2415 Add param change popup to favorites buttons 2019-05-13 21:26:20 -05:00
Taha Afzal 8575d92373 once the lean's child has crossed, changin lean's price has no effect on the child 2019-05-13 17:53:19 -04:00
Pedro Pinto 21ee7c6af5 2.0.0 Release notes. 2019-05-13 17:30:49 -04:00
Pedro Pinto 7b61b4bd5b Fixed error message 2019-05-13 17:18:32 -04:00
John Colvin 1bcb7ac87f SPARK-2411 / SPARK-2414 Add UI for glbx icerberg order 2019-05-13 14:01:25 -05:00
John Colvin 52305d69f2 normalize spacing in OrderDataHelper.cs 2019-05-13 14:01:25 -05:00
John Colvin bd70e7c09e OrderDataHelper - fix typeo (Offset order metadata was assigning the tick order color) 2019-05-13 14:00:08 -05:00
Michael Harding f6cf0f28ea Move test scripts in spark_test.csproj 2019-05-13 13:31:02 -04:00
John Colvin d04f9f4e96 fix server build 2019-05-13 13:27:35 -04:00
Michael Harding c0d097cb40 SPARK-2438: spreader_lean_qty_factor test setup 2019-05-13 13:26:53 -04:00
Michael Harding 533e688d1a SPARK-2438: init spreader_lean_qty_factor test 2019-05-13 13:09:15 -04:00
Michael Harding 36d40dec70 spell check manual tests 2019-05-13 12:59:49 -04:00
Jake Norbut a3f5de099f swapping out bar brushes and glyph values for using last 2019-05-13 11:18:56 -05:00
Jake Norbut 9b497e323f adding last on volume column to ladder grid host, tooltips 2019-05-13 11:17:15 -05:00
Jake Norbut 9a5b5595c3 adding show last setting to Volumn tab 2019-05-13 11:09:22 -05:00
Jake Norbut 24f23adf24 extending show last trade to VolumnColumnVM 2019-05-13 11:08:36 -05:00
Jake Norbut cc26d0bd8b Adding show_ltq to VolumeColumnSettings 2019-05-13 11:03:57 -05:00
Michael Harding 2b51bda3e7 Update system test scripts 2019-05-13 11:27:12 -04:00
Pedro Pinto 1be2e81920 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-05-12 14:33:40 -04:00
John Colvin ea45c5c694 OrderBook - added config for status color opacity when row is selected 2019-05-12 12:24:32 -05:00
John Colvin 768567dfc7 HighPerformanceRow - added BackgroundProperty 2019-05-12 12:14:38 -05:00
John Colvin 870e7a9ef9 DIYGrid - added mutable InactiveSelectionOpacity prop (was just hardcoded at 0.2) 2019-05-12 12:11:39 -05:00
Pedro Pinto a9e572a1bf Merge branch 'master' into SPARK-1944-attach/detach/update 2019-05-11 19:41:18 -04:00
John Colvin c7899a1dae SPARK-2434 OrderBook - security column filter popup is empty if shell is started before server has published all securities 2019-05-11 12:47:07 -05:00
John Colvin 7bb077a74d remove ToLArray extension (it is not more efficient than Enumerable.ToArray()) 2019-05-11 12:27:42 -05:00
John Colvin a29eaa2618 EnumCaseStringConverter - cleanup code style/syntax 2019-05-11 12:25:38 -05:00
John Colvin 762369ad2b SPARK-2433 child order font weight in OB should be dropped or adjusted 2019-05-10 21:22:42 -05:00
John Colvin a8610beafd SPARK-2432 Crash when detaching order window 2019-05-10 20:38:33 -05:00
John Colvin 5e1a91f484 add period to the end of 'Make current parameters the local defaults' tooltip 2019-05-10 16:01:07 -05:00
John Colvin 04fdb9626d SPARK-2409 Changing a composite algo name does not reflect in the attach window dropdown 2019-05-10 15:57:31 -05:00
Michael Harding 6c5429b353 Minor updates to position limits test 2019-05-10 16:36:13 -04:00
Michael Harding 2bedf1f355 Merge branch 'master' into system_tests 2019-05-10 16:22:18 -04:00
Michael Harding 02752d9151 SPARK-2064: simple sanity simplified 2019-05-10 16:14:06 -04:00
John Colvin 689bfa1c76 OrderBook 'attach popup' price/qty vis depends on the selected attach-algo (not the selected orders) 2019-05-10 13:51:39 -05:00
Pedro Pinto 70af046131 Updated docker subrepo ptr 2019-05-10 13:52:44 -04:00
Pedro Pinto 7a45013782 Changed cover to stick around until all hedges are completed. 2019-05-10 13:52:44 -04:00
Michael Harding 6f7851b7ba SPARK-2064: Simple sanity test 2019-05-10 13:41:05 -04:00
Taha Afzal 85f42d1e23 Join won't modify its child's price once it has joined 2019-05-10 11:12:52 -04:00
John Colvin f8ddaab884 OrderBook - (Root)OrderType col filters show green beaker when filter active on startup/load workspace 2019-05-09 18:00:12 -05:00
John Colvin 78373c61ce miniob - hide price/qty param rows in attach popup 2019-05-09 16:10:18 -05:00
John Colvin 13e5c017e2 miniob - set orderbookvm.is_attaching_orders to false when attach popup closes 2019-05-09 15:53:12 -05:00
Eric Harding 3789d73593 Do not allow minTick to be 0 (infinite loop) 2019-05-09 16:25:25 -04:00
John Colvin 38f42fe747 SPARK-2417 Attach window price, qty 2019-05-09 14:28:52 -05:00
John Colvin 3a6f9a608d OrderBookVM.send_attached_orders_async uses CustomDef.actual() to unwrap composite algos 2019-05-09 14:28:52 -05:00
Michael Harding e14a5b2575 update SDK readme 2019-05-09 18:47:09 +00:00
John Colvin 04a68a4e96 OrderBookRowVM - fix typeo in badge_color prop (was using 'root') 2019-05-09 13:43:47 -05:00
Eric Harding dd049374d7 Fix equality comparison between mbo price levels 2019-05-09 14:26:14 -04:00
John Colvin 526ec306ae (Root)OrderType col filters ItemTemplate uses 'OrderTypeItem' (displays badge and name) 2019-05-09 12:41:35 -05:00
John Colvin f16ed40c82 OrderUtils.badge_fill does not need the OrderRecordImmutable arg 2019-05-09 12:35:17 -05:00
Eric Harding 96572539a7 SPARK-2381 View MBO in ladder 2019-05-09 13:05:47 -04:00
John Colvin 793eb14ed7 AlgoDefinitionMetadata - null check _favorite_templates in GetHashCode 2019-05-09 11:39:55 -05:00
John Colvin 1f94454d63 SPARK-2421 Default ladder to new fancy param grid 2019-05-09 11:20:10 -05:00
John Colvin c7e3bdcd6c OrderBookRowVM - normalize spacing 2019-05-09 11:18:27 -05:00
John Colvin 004a7b1395 OrderBookRowVM - don't hook up to invalidated event until '_modify_order_quantity_andor_price' cmd is created 2019-05-09 11:17:03 -05:00
Pedro Pinto 96bb63ba6d Merged 2019-05-09 12:13:37 -04:00
Pedro Pinto 0ea72b017e Storage component of manual fills code complete. 2019-05-09 12:12:02 -04:00
John Colvin b1498f3df1 OrderBookRowVM - forgot to raise property changed when root badge color changes 2019-05-09 11:03:08 -05:00
John Colvin b0759eba62 OrderBookRowVM - fix root badge color 2019-05-09 10:58:40 -05:00
Jake Norbut ff69cafe7f ob alignments - left aligning nearly everything, price and qty cols stay right aligned 2019-05-09 09:59:40 -05:00
Pedro Pinto 698612bb3d Storage component of manual fills code complete. 2019-05-09 10:29:54 -04:00
Jake Norbut f1e90394a0 restoring Root Algo column header after merge 2019-05-09 08:24:44 -05:00
Jake Norbut b24f855321 misc refactoring after root algo branch merge; OrderBookVM parameter passing no longer necessary 2019-05-09 08:20:36 -05:00
Pedro Pinto 2f23292836 Pos server 2019-05-09 09:12:37 -04:00
Pedro Pinto 085e6ae95a Merged 1944 in 2019-05-09 09:01:32 -04:00
Pedro Pinto 3be2aedfe3 Work in progress for manual fills 2019-05-09 08:59:59 -04:00
John Colvin 6e56b6cbaf sort algo type items in (Root)OrderType col filters 2019-05-08 23:12:21 -05:00
John Colvin 16ff473fd6 ViewModelBase - added 'DelegateProperty' backing method that doesn't invoke get_value during creation 2019-05-08 23:07:55 -05:00
John Colvin 4565fada08 change KeyedMultiSelector.selected_items to ObservableCollectionEx (supported by ListBoxEx, which is used by DiscreteValuesFilters) 2019-05-08 23:05:21 -05:00
John Colvin 648f808583 SPARK-2409 / SPARK-2422 fix duplicate algo items in (Root)OrderType col filters 2019-05-08 20:53:54 -05:00
John Colvin 7a6212932e Merge branch 'root_algo_col' into SPARK-1944-attach/detach/update 2019-05-08 19:20:38 -05:00
John Colvin aa933696ec OrderBook - include root_algo member in order_tuples join (for new 'Root Algo' col) 2019-05-08 19:16:10 -05:00
John Colvin 39d59dbbb0 IObservable<IReadOnlyDictionary>.Table() extension applies value-changes to state 2019-05-08 18:43:03 -05:00
Taha Afzal a511ea3a4b Lean cancels itself when detach on OnToward 2019-05-08 17:23:10 -04:00
John Colvin e489e809ab Merge remote-tracking branch 'origin/SPARK-1944-attach/detach/update' into root_algo_col 2019-05-08 16:22:30 -05:00
Jake Norbut e56d41d26b SPARK-2419 - algo studio childs display correct name and badge color now when they are not roots; verified w/ john his great comment no longer applied 2019-05-08 16:05:21 -05:00
John Colvin b1d45ad593 fix benign typeos in UpdateExtensions.ParentDependent 2019-05-08 15:45:17 -05:00
Pedro Pinto 1f01d974e3 Merged from 1944 2019-05-08 16:40:40 -04:00
Pedro Pinto 32038f8948 Fixed algo API bug that could lead to lost order updates. 2019-05-08 16:36:11 -04:00
Pedro Pinto 910c04544b Code complete on update of stop limit and iceberg qty. 2019-05-08 16:27:23 -04:00
Pedro Pinto 10674b8930 Merged 2019-05-08 15:25:58 -04:00
Pedro Pinto 8c9522e22b Work in progress to add support for update to stop limit and glbx iceberg. 2019-05-08 15:22:29 -04:00
Jake Norbut 2a90b521da SPARK-2418 - miniob to make sure of new algo_tree_display_mode setting for RootAndLeafNodes 2019-05-08 14:19:35 -05:00
Jake Norbut 2098a60af1 eliminating root_algo_badge_fill property so that the fill color is reevaluated on all updates, for now ... 2019-05-08 12:01:18 -05:00
Jake Norbut 67c0178b9a adding GridColumnSettings for root algo column 2019-05-08 12:00:28 -05:00
Jake Norbut 5da07c42bb Adding RootAlgoType to ColumnBase 2019-05-08 11:59:31 -05:00
Jake Norbut f82035be98 refactoring OrderBookOrderTypeColumn to eliminate the newly created RootAlgo column type 2019-05-08 11:58:33 -05:00
Jake Norbut a652268eb8 get filtering working correctly 2019-05-08 11:02:33 -05:00
Jake Norbut 13d054a54c just keep checking the OB cache rather than against our local one 2019-05-08 10:04:01 -05:00
Jake Norbut 84cf2a3ad1 refactoring; kind of get filtering working, but not really 2019-05-08 09:42:50 -05:00
Jake Norbut 32e0d60dcd get filering working by passing in an 'owner' reference to the OrderBookColumnCollection 2019-05-08 08:53:04 -05:00
Jake Norbut 0a6755992a removing new non-working rootalgocvorderbookcolumn interface; using OB cache instead of tree; filtering still a mystery 2019-05-08 08:26:47 -05:00
Pedro Pinto f60afe1196 Woark in progress to add support for update to stop limit and glbx iceberg. 2019-05-08 06:46:40 -04:00
Pedro Pinto 0cc30a7934 Added server side support for GLBX Iceberg. 2019-05-08 06:46:40 -04:00
Pedro Pinto 3cc0f32617 Protected iceberg against negative show quantities. 2019-05-08 06:46:40 -04:00
John Colvin 29ae9c6927 SPARK-2404 Make ladder param grid behave more like a popup 2019-05-07 21:44:47 -05:00
John Colvin 892abe098c SPARK-2404 / SPARK-2410 replace dialog skirt buttons with toolbar menu items 2019-05-07 21:27:56 -05:00
John Colvin 9cf683f64f AlgoTemplateToolBar - RevertDefinition's visibility is not contingent upon a studio definition being selected 2019-05-07 21:27:56 -05:00
John Colvin f2bb8de4e1 AlgoTemplateToolBar - execute RevertDefinition visual state transitions through InvokeAsync 2019-05-07 21:27:51 -05:00
John Colvin b6a28af99e SPARK-2406 OrderBook - update param rows misbehave with non-uniform values 2019-05-07 18:35:06 -05:00
John Colvin 13a9dd3696 SPARK-2406 / SPARK-2408 QuantityMeasureControl should assume 'Absolute' mode when QuantityMeasure is null, or QuantityCase is 'None' 2019-05-07 18:33:38 -05:00
John Colvin 60bbd12386 code style change in ReflectionHelper.GetGenericTypeArgs method 2019-05-07 17:57:56 -05:00
John Colvin 3e09274cb5 SPARK-2403 Top is still showing this unknown parameter 2019-05-07 17:17:59 -05:00
John Colvin e9d1da4b51 SPARK-2401 mview - mass selection degrades performance 2019-05-07 17:13:06 -05:00
John Colvin 68e3fcb1cd made extension methods for Ice-dependent security props 2019-05-07 17:13:06 -05:00
John Colvin 745be0909a remove unused implementations of SecurityGrid 2019-05-07 17:13:06 -05:00
Eric Harding 74dd36bde1 SPARK-2380 add MBO popup 2019-05-07 18:02:59 -04:00
Taha Afzal 0bee4168b4 Join test has attach/detach/updates now 2019-05-07 16:19:24 -04:00
Jake Norbut d87c6027c9 display of column works, filtering not so much 2019-05-07 14:27:42 -05:00
John Colvin 859faadecd SPARK-2376 When collapsed the OB scrollbar overlaps with the collapsing widget 2019-05-07 12:22:40 -05:00
Taha Afzal e8741f9151 Top order manual test 2019-05-07 13:19:38 -04:00
Jake Norbut ef8b0fc258 enforce 2 digit algo abbreviation on badge 2019-05-07 11:43:59 -05:00
John Colvin 3b27fedcd3 SPARK-2402 piq merge broke the orderbook mini-ladder order filter 2019-05-07 11:18:27 -05:00
Michael Harding 0991fbfffd Update SDK README 2019-05-07 11:31:56 -04:00
Jake Norbut 499072c94f PR edit - we don't need an array here 2019-05-07 09:33:25 -05:00
Jake Norbut fa8598adbd PR suggestion - explicitly set default value 2019-05-07 09:22:44 -05:00
Jake Norbut 089c4ac35e part of SPARK-2376 - OB footer now spans the entire window width, regardless of the new right panel state 2019-05-07 08:44:48 -05:00
Jake Norbut af8b559fa6 Merge branch 'master' of bitbucket.org:bluetradesys/spark into piq_ladder_dirty 2019-05-07 08:26:15 -05:00
Taha Afzal 62ba85d383 Lean deals with gapped markets better 2019-05-06 19:22:06 -04:00
Taha Afzal f4b2f84e68 Lean's corner cases 2019-05-06 18:14:33 -04:00
Taha Afzal 474e7880d6 LeanAlgo's spurious price/qty controlled 2019-05-06 18:14:33 -04:00
John Colvin 89f74b7c81 SPARK-2399 add commit/revert buttons to ladder algo params grid popup 2019-05-06 17:05:52 -05:00
Jake Norbut 0abcb8bf23 SPARK-2391 - attach/detach icons 2019-05-06 16:33:25 -05:00
Pedro Pinto 47887b67d2 Fixed SIM handling of updates to size of partially filled orders. 2019-05-06 15:57:28 -04:00
John Colvin 41a96b990d SPARK-2382 Windows that take a long time to render do not give any user feedback until they are visible 2019-05-06 14:18:21 -05:00
Jake Norbut ce8bc5c631 SPARK-2394 - rename Fonts and Colors to Column Settings 2019-05-06 12:23:21 -05:00
John Colvin eebb214574 make FixNestedScrolling diygrid popup conditional check more specific 2019-05-06 11:28:53 -05:00
Jake Norbut 47c827caf6 badge - refactor (forgot a ToUpper for 1 of the return paths 2019-05-06 11:22:42 -05:00
Michael Harding a82a22b133 Update test text 2019-05-06 12:17:07 -04:00
Brian Muhr 1fd444c91c adding new tests 2019-05-06 11:12:58 -05:00
Jake Norbut f28ae29990 changing A and D geometry to Segoe UI font 2019-05-06 11:07:56 -05:00
Jake Norbut aedabcdf00 SPARK-2392 - ob attach/detach icons to match mini ob 2019-05-06 11:07:56 -05:00
Jake Norbut 3b37f8e48b adding A and D icons; created from Inkscape 2019-05-06 11:07:56 -05:00
John Colvin beb328ec09 SPARK-2397 grid column popups don't scroll on mouse wheel 2019-05-06 10:58:41 -05:00
Jake Norbut f9b9bdab47 SPARK-2391 - generate badge abbreviation for algo studio orders lacking one 2019-05-06 09:22:44 -05:00
Eric Harding 3beb82c959 Add comment about why dump_to_shm does'nt need to hold mutex 2019-05-06 09:01:45 -04:00
Jake Norbut 9e6776c6fb updating test text 2019-05-06 07:49:31 -05:00
Pedro Pinto e807f6a702 Fixed composite handling of detach. 2019-05-05 11:56:35 -04:00
John Colvin 07212aa5d6 IAlgoVM.load impls unwrap CompositeOrderDefinitions to get the underlying algo 2019-05-04 15:57:50 -05:00
Pedro Pinto a0f31d6443 Fixed composite order some more. 2019-05-04 15:14:20 -04:00
Pedro Pinto df6a5096c2 Composite order was not reflecting child price/size 2019-05-04 14:33:16 -04:00
John Colvin 43336d04d4 SPARK-2375 Composite algo order types do not show as options in the OB filter 2019-05-04 13:26:23 -05:00
John Colvin ca73b20fe5 made 'MergeTable' extension method for TableUpdate 2019-05-04 13:16:45 -05:00
John Colvin 09f6ad4031 OrderBook - fix crash when selecting composite-order row 2019-05-04 13:04:56 -05:00
John Colvin 97b52f2e64 OrdersModel.update_order should ignore size change if size == quantity (not working_quantity) 2019-05-04 13:04:34 -05:00
Pedro Pinto 0adc77a5cf Fixed composite order. 2019-05-04 11:53:12 -04:00
Pedro Pinto e469e8fc70 Tweaked cover test. 2019-05-04 11:53:12 -04:00
Pedro Pinto 10da3c517c Changed hedge ticks to be a uint, from int 2019-05-04 11:53:12 -04:00
Pedro Pinto 5f7c4b5c37 Improved test. 2019-05-04 11:53:12 -04:00
Pedro Pinto c34d05218a Fixed clang build. 2019-05-04 11:53:12 -04:00
Pedro Pinto 3abf99da91 Cleaned up tests. 2019-05-04 11:53:12 -04:00
Pedro Pinto 8d94238e91 Updated cover test 2019-05-04 11:53:12 -04:00
John Colvin dc169fc4fd SPARK-2388 studio-template orders should use new CompositeOrderDefinition (not CustomOrderDefinition) 2019-05-04 11:53:12 -04:00
John Colvin abe8361b18 when updating orders without a 'new size' arg, use the order's actual quantity, not working_quantity 2019-05-03 20:16:18 -05:00
Eric Harding 9f406e83aa SPARK-2151 Dump shared memory on bad handle for forensic analysis 2019-05-03 17:39:47 -04:00
John Colvin 2ee163bf8a SPARK-2387 crash when opening orderbook from other windows 2019-05-03 15:44:22 -05:00
Jake Norbut 775a720e0a SPARK-2027 - it's depressing how many hours of your life 1 line of code can steal 2019-05-03 15:01:27 -05:00
Jake Norbut 62c56762bf lighten some of the OB row colors 2019-05-03 13:31:21 -05:00
Pete Fisher 16fbab8677 added author/complain to tests 2019-05-03 13:57:39 -04:00
Pete Fisher 505ac39a3f removed parts about visual notifications and checkboxes per John 2019-05-03 13:33:05 -04:00
Eric Harding 84aefabfea Safe workaround for SPARK-2303. 2019-05-03 13:19:35 -04:00
Eric Harding 102fefbd92 1.6.2 release notes 2019-05-03 13:13:59 -04:00
Jake Norbut 87b8a802c9 SPARK-2050 - syth editor - remove borked help button 2019-05-03 10:31:47 -05:00
Eric Harding fc7a4b0800 Merged in PIQachu (pull request #223)
RTm: spark-2316 mbo support for simulator

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2019-05-03 15:30:13 +00:00
Taha Afzal 679330b9a8 fixing cover test 2019-05-03 10:26:08 -04:00
Jake Norbut ab7b495358 ladder - restrict LOVM processing to only those working orders whose security matches the current ladder security 2019-05-03 09:23:13 -05:00
John Colvin 9b6064e87e SPARK-2377 OB Last Message column displays strange alignment when resizing and/or scrolling 2019-05-02 18:28:02 -05:00
John Colvin ab3fa25709 SPARK-2357 On new order book grids default to hiding intermediate nodes 2019-05-02 17:29:33 -05:00
John Colvin 07af7a4298 SPARK-2531 Clear button numpad 2019-05-02 17:15:48 -05:00
Jake Norbut 926274e2d9 eliminate unecessary join in OrdersModel and use on_orders_tuples for algometadata when joining 2019-05-02 17:09:48 -05:00
Jake Norbut 5e4c22c99d badge - use customized algo border if specified from metadata 2019-05-02 17:09:48 -05:00
Taha Afzal 06b4f8055f Fixing the build after merge 2019-05-02 17:40:49 -04:00
Taha Afzal 81b307fa73 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-05-02 17:28:12 -04:00
Taha Afzal 6d131f9b4f merging from queueholder order 2019-05-02 17:15:47 -04:00
John Colvin 014c1e968a SPARK-2360 Lean's cross threshold in the UI can be made to overflow 2019-05-02 16:08:44 -05:00
Eric Harding 8f9321701e merge PIQ in simulator 2019-05-02 15:05:57 -04:00
Eric Harding 6b5016c9fe SPARK-2316 Remove "Give me everything please" 2019-05-02 15:04:43 -04:00
Taha Afzal 50e8808af0 Merge branch 'master' into queue_holder_order 2019-05-02 14:33:03 -04:00
Eric Harding 916fda1eb5 Merge branch 'master' of bitbucket.org:bluetradesys/spark into PIQachu 2019-05-02 13:31:35 -04:00
Eric Harding 9422e3ac98 SPARK-2316 Simulator should stop cutting in front 2019-05-02 12:36:25 -04:00
Eric Harding 09e8fabcf2 ExchangeSim - use open_quantity for mbo instead of quantity 2019-05-02 12:14:24 -04:00
Jake Norbut 2064de0752 Adding Abbreviation to badge 2019-05-02 10:51:05 -05:00
Jake Norbut f94aa936b0 on_order_with_piq now joins with algo_meta, so that working orders with piq can have algo metadata 2019-05-02 10:50:18 -05:00
Jake Norbut 4a22ad7616 OrderSecurity now holds AlgoData and AlgoDefinitionMetadata 2019-05-02 10:48:19 -05:00
Eric Harding 133bd89a2c add mbo diagnostic tool 2019-05-02 11:29:01 -04:00
Michael Harding 6d9c083c0f Verify quoting order 2019-05-02 10:58:45 -04:00
Jake Norbut 63d97c8982 ob/mini - adding background color triggers based on order status 2019-05-02 09:24:27 -05:00
Eric Harding 7540ae0ef7 update onixs license 2019-05-02 09:25:42 -04:00
Pedro Pinto 3f06470c5e Added new filtering/collapsing sections to the OB test. 2019-05-02 09:10:33 -04:00
John Colvin 04537684cb remove unused dictionary vars in TableUpdateDemuxer.OnNext 2019-05-01 23:59:51 -05:00
John Colvin 6fba76b399 SPARK-2365 Crash when entering limit orders at various levels quickly (log attached) 2019-05-01 23:51:29 -05:00
Eric Harding 9f9da1847b OrderStress - do asks first so the console looks like little text ladders 2019-05-01 17:53:47 -04:00
Eric Harding 975f7f64b7 SPARK-2316 add MBO support to the simulator 2019-05-01 17:52:29 -04:00
Pedro Pinto cacf3ac862 Addresses SPARK-2369, where Lean was not reportin fills properly. 2019-05-01 15:49:21 -04:00
Pedro Pinto 104247c786 Fixed SPARK-2368, on attach disregard current fills in cover. 2019-05-01 15:14:25 -04:00
Jake Norbut 064ed5b692 OB/mini - Completed order status now h ows with a lighter tint of light slate gray 2019-05-01 14:00:12 -05:00
Pedro Pinto bddb152d76 Working towards a composite order. 2019-05-01 14:03:34 -04:00
Pedro Pinto e2f3bfd61a Fixe user manual generation script. 2019-05-01 14:03:34 -04:00
Pedro Pinto 791774490c Clang build fixes. 2019-05-01 14:03:34 -04:00
Taha Afzal c4f984c612 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-05-01 14:00:25 -04:00
Taha Afzal 04eee0f4b7 added logs in Lean to detect the out-of-the-gate fill situation 2019-05-01 14:00:18 -04:00
Jake Norbut aa9846962b SPARK-2354 - remove ugly yellow working color; we might introduce some new colors depending on order state, so i just have just commented out these setters for now 2019-05-01 12:17:29 -05:00
Jake Norbut 881c3a5c1d badge - order abbreviation and hedge indicator should respect FontSize setting 2019-05-01 11:39:02 -05:00
Taha Afzal 59ba9dc129 Attach added to cover test 2019-05-01 11:54:17 -04:00
Taha Afzal b9efaef0a5 detach test 2019-05-01 11:11:11 -04:00
Michael Harding 3e822bcca1 Merge branch 'master' into spreader_test 2019-05-01 10:58:30 -04:00
Michael Harding 0732034dbe Fix build 2019-05-01 10:50:26 -04:00
Jake Norbut 8dde9bf3c5 print descriptive workspace info after selecting a user's workspace to copy 2019-05-01 08:39:37 -05:00
John Colvin 667a482fba SPARK-2353 Hide Price and Quantify parameters in attach dialog for Top, Lean, Cover, and Join 2019-04-30 22:23:35 -05:00
John Colvin 6be8dae2d1 SPARK-2336 Attaching with different price, qty on child orders 2019-04-30 19:10:46 -05:00
John Colvin f478b2c9f2 SPARK-2355 Attaching to a running studio algo results in the wrong order type being displayed 2019-04-30 18:24:18 -05:00
John Colvin 300c5c9431 OrderBookVM - when sending attached orders, get snapshot of selected orders before calling api (i.e., selected_orders.ToList()) 2019-04-30 18:24:18 -05:00
Jake Norbut 9c066ca703 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-30 18:21:21 -05:00
Jake Norbut 9946c4716f Default PIQ column to not visible 2019-04-30 18:13:22 -05:00
John Colvin ee0e719e76 Merged in SPARK-2361-piq-bid-bug (pull request #221)
SPARK-2361 ladder - hidden PIQ column causes bids not to work

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2019-04-30 23:13:03 +00:00
John Colvin 5d5761288f SPARK-2361 ladder - hidden PIQ column causes bids not to work 2019-04-30 18:04:10 -05:00
Jake Norbut 0a683dae15 Merge branch 'master' of bitbucket.org:bluetradesys/spark into piq_ladder_dirty 2019-04-30 16:58:37 -05:00
John Colvin afb0288672 SPARK-2359 (+SPARK-2352) AlgoParamVM does not need to derive from DisposableViewModelBase (keep it light) 2019-04-30 16:41:28 -05:00
John Colvin aefe248034 remove unusued method 'OrderBookVM._attach_orders' 2019-04-30 16:41:28 -05:00
Taha Afzal 398382c3a5 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-30 17:26:43 -04:00
Taha Afzal cf2c16564f attach manual test 2019-04-30 17:26:38 -04:00
Eric Harding 48ee5e1699 advance externals 2019-04-30 17:12:46 -04:00
Eric Harding 27c9ec8dab Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-30 17:11:11 -04:00
Eric Harding 31e767208e merge master 2019-04-30 17:10:38 -04:00
Pedro Pinto 0c2943e3da Removed obsolete files. 2019-04-30 16:10:02 -04:00
Pedro Pinto 99ea02ca40 Guarded against the possibilty of algos not supporting attach/detach. 2019-04-30 15:26:19 -04:00
Pedro Pinto 8ceab51228 Removed obsolete class. 2019-04-30 15:26:19 -04:00
John Colvin 4a69a9204b OperableHierarchicalCollection was not sorting root nodes 2019-04-30 13:56:59 -05:00
John Colvin 0e136637e0 OrderBookVM - remove unused type alias 2019-04-30 13:56:59 -05:00
John Colvin d803622c01 SPARK-2315 / SPARK-2338 hook up AlgoTreeDisplayMode setting in OrderBookVM 2019-04-30 13:56:58 -05:00
John Colvin 2e49534483 move OrderBookGridSettingsManager into its own file 2019-04-30 13:56:58 -05:00
John Colvin 8210f4f74a make INode<T> covariant, bc might as well 2019-04-30 13:56:58 -05:00
Jake Norbut 69e2bcb164 badge - misc refactoring 2019-04-30 12:51:29 -05:00
Jake Norbut e4ae42e80d badge - summary no longer needs to be something that implements IRaiseNotifyPropertyChanged 2019-04-30 12:27:31 -05:00
Jake Norbut ad3950e2c5 SPARK-2348 redux - eliminate public orders accessor; fixes order modifies, i.e. drag and drop badge, in ladder 2019-04-30 11:32:11 -05:00
Taha Afzal 6bab882f89 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-30 12:30:20 -04:00
Taha Afzal b6709b426c new tests and 'find_qty_at_price_level' fixed 2019-04-30 12:30:04 -04:00
Eric Harding 77f5723936 Apply lazyView to the limits tables 2019-04-30 12:23:18 -04:00
Jake Norbut 898574130c SPARK-2348 - badge operations should be on root orders; getting rid of unused code 2019-04-30 10:57:25 -05:00
Jake Norbut 11faa6ca36 SPARK-2347 - badge mutliple orders to iterate over root orders, not orders 2019-04-30 10:43:45 -05:00
John Colvin 01dc0c004b SPARK-2344 Child orders are being duplicated in OB, MiniOB, possibly ladder? 2019-04-30 10:08:51 -05:00
Eric Harding 6dbeaa14e2 SPARK-2343 Update.h does not correctly track 'all' collection breaking limits 2019-04-30 10:49:30 -04:00
John Colvin 9271d4e2ea fix last commit (NodeExtensions.filtered) 2019-04-29 23:51:57 -05:00
John Colvin 06ca1461f4 simple optimization for NodeExtensions.filtered() 2019-04-29 23:42:03 -05:00
John Colvin f84c931d5d NodeExtensions.filtered() should filter root nodes too (not just children) 2019-04-29 23:40:56 -05:00
John Colvin 1d2d7099c4 SPARK-2340 OrderBook - order lineage breaks when there is a generational gap (created by filter) 2019-04-29 23:17:28 -05:00
John Colvin 00540435eb OrderBook - assign sorting-data to save-state 2019-04-29 17:02:12 -05:00
John Colvin b5552a858e SPARK-2315 / SPARK-2337 add AlgoTreeDisplayMode enum prop to OrderBookGridSettings 2019-04-29 16:41:33 -05:00
John Colvin fa110b027b BooleanToObjectConverter uses object Equals to determine bool returned in ConvertBack 2019-04-29 16:41:33 -05:00
John Colvin 7ce7ebbf96 remove erroneous/legacy RadioButton default style from SimpleGridSettingsDialog.xaml 2019-04-29 16:41:33 -05:00
John Colvin 98315d8e15 refactor 'GridOrLadderSettingsSelectorKeys' to 'GridSettingsTemplateKeys' 2019-04-29 16:41:33 -05:00
Eric Harding 6066e2162c SPARK-2197 Correction to PIQ calculation per taha (+test coverage) 2019-04-29 17:28:23 -04:00
Eric Harding 4279e3d388 Fix broken managed md unit test 2019-04-29 16:36:16 -04:00
Eric Harding 221814cc11 Add OrderStress tool for perf testing 2019-04-29 16:30:18 -04:00
Jake Norbut 2d4e2c84b5 manual test for mini ob 2019-04-29 13:48:50 -05:00
Taha Afzal e84c7d4f8c Cleaning up lean again 2019-04-29 13:01:31 -04:00
Jake Norbut da1ae1b410 badge - the rest of SPARK-2291 - no more parent info 2019-04-29 09:25:22 -05:00
Jake Norbut 147783666c badge - woops, not all orders should have hedge indicators 2019-04-29 08:52:11 -05:00
Jake Norbut 2c68458762 badge - hedge indicator is now a glyph run rather than a bunch of lines 2019-04-29 08:46:58 -05:00
Jake Norbut 1682cc004e miniob - tooltips for A/D buttons 2019-04-29 08:25:17 -05:00
Taha Afzal d6a8925655 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-28 23:01:49 -04:00
Taha Afzal 1dba1034b6 Lean simplified a bit more 2019-04-28 23:01:42 -04:00
John Colvin 437efc393d OrderBookView - menu expander button for right panel 2019-04-28 18:53:55 -05:00
John Colvin b27fb045fd remove ToolTipService.InitialShowDelay from MenuExpanderButton style 2019-04-28 18:33:43 -05:00
John Colvin cc3d916dd3 factor out core of MenuExpanderButton style into MenuExpanderButtonBase 2019-04-28 18:33:23 -05:00
John Colvin b1659b0aa9 fix typeo in CheckedStateDefinition static constructor 2019-04-28 18:33:23 -05:00
John Colvin 3051cff3c8 OrderBookView - put update content (params/buttons) in scrollviewer 2019-04-28 18:33:23 -05:00
John Colvin bf01bf4ad6 AlgoEditorGridView - hook up execution strategy toggle for spreader orders 2019-04-28 18:33:23 -05:00
John Colvin 0151b0e16c DIYGrid - column header sort arrows are all the way to the right when filter is inactive 2019-04-28 18:33:23 -05:00
John Colvin c30e922c38 new ensure_onscreen logic should account for window being above/left of work area too (not just below/right) 2019-04-28 18:33:23 -05:00
Taha Afzal 0417dd7e08 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-28 16:31:45 -04:00
Taha Afzal 158c1cbcfd Lean fixes 2019-04-28 16:31:36 -04:00
John Colvin a5bd9b25c6 increase 'infinite loop' counter threshold in ensure_onscreen 2019-04-27 12:03:21 -05:00
John Colvin fbe6b38edc simplify ensure_onscreen code (since end of cycle approaches, disable by default with config) 2019-04-27 12:03:13 -05:00
John Colvin 6d4ece0667 ensure_onscreen for new shell windows 2019-04-27 11:09:39 -05:00
John Colvin b19380c91c SPARK-2334 when opening new shell windows, don't make them visible until initial async measurement is finished 2019-04-27 11:05:46 -05:00
John Colvin dc5585cd8d MarketViewWindow - don't use hidden columns in initial width calculation 2019-04-27 10:47:47 -05:00
John Colvin b53163d1e1 SPARK-2234 / SPARK-2333 disable child algo dropdowns in orderbook/ladder 2019-04-26 22:23:20 -05:00
John Colvin 63c3e2c93a fix orderbook 'attach' popup (was stretching past x bounds) 2019-04-26 21:45:44 -05:00
John Colvin 3d96bc98e2 SPARK-2234 / SPARK-2332 ladder - toggle conf to display algo params in a grid (like orderbook) 2019-04-26 21:16:38 -05:00
John Colvin e5bffc3e89 AlgoEditorGridView - set both column widths to '*' 2019-04-26 21:10:54 -05:00
John Colvin e613c5626e OrderModVM - use ReactiveThresholdMonitor to drive 'is_dirty' prop 2019-04-26 17:53:57 -05:00
John Colvin 704a0cc1aa SPARK-2243 Clean up algo offerings 2019-04-26 17:38:00 -05:00
John Colvin 85447ccfb4 CollectionViewSource - set CollectionViewType in default constructor 2019-04-26 17:35:34 -05:00
Jake Norbut bd643aa525 don't draw piq on every tick; might save some cycles 2019-04-26 14:50:16 -05:00
John Colvin 1d419a0bd6 SPARK-2330 Dynamic orders should not show the dyn type as a parameter in the update dialog 2019-04-26 13:10:37 -05:00
John Colvin e6815f95c4 SPARK-2325 Dynamic orders no longer seem to be getting any parameters from the UI 2019-04-26 12:42:22 -05:00
Pedro Pinto e2e5769767 Fixed Cover order attach crash. 2019-04-26 13:40:11 -04:00
Pedro Pinto 23bec87ad0 Improved comments on order plugin example. 2019-04-26 13:29:26 -04:00
Pedro Pinto 992b64c08a Changed dynamic order example to use a Join and the new API. 2019-04-26 13:16:42 -04:00
Pedro Pinto 85b6103944 Fixed typo in comment. 2019-04-26 13:14:07 -04:00
John Colvin 9a376d5209 OrderBookVM - detach_selected_orders_cmd passes the selected orders' parentkeys (accidentally broke again) 2019-04-26 11:45:07 -05:00
John Colvin cee4d65d38 SPARK-2293 / SPARK-2323 change right panel toolbar button click mode to 'Pressed' (i.e., select on mouse down) 2019-04-26 11:25:24 -05:00
Jake Norbut b81459cdf7 badge - Hedge order is now represented with H on the right side of badge rather than a single vertical line on left side 2019-04-26 09:55:01 -05:00
Jake Norbut 136a75f526 Removing IsExchangeOrder and associated badge decoration 2019-04-26 09:13:58 -05:00
Jake Norbut 406a2c5bc9 badge summary is only used in log messages, so simplify string 2019-04-26 09:05:07 -05:00
Jake Norbut b1b0d23839 part of SPARK-2291; paired with John, thx John! 2019-04-26 08:55:35 -05:00
Jake Norbut 495621a014 miniob price and quantity now set focus upon going into edit mode and mouse is over them 2019-04-26 08:25:45 -05:00
Jake Norbut d37f3a4646 Escape key when cursor in price or quantity now reverts changed value 2019-04-26 07:58:49 -05:00
John Colvin a9e3443cab SPARK-2293 / SPARK-2324 disable attach button when not applicable 2019-04-25 23:33:36 -05:00
John Colvin a8db5dae3d SPARK-2293 / SPARK-2322 disable detach button when not applicable 2019-04-25 23:27:14 -05:00
John Colvin 76af564057 SPARK-2293 / SPARK-2321 change 'Attach To Selected Orders' to 'Attach To Selected' 2019-04-25 23:07:41 -05:00
John Colvin f6df5cc2ec SPARK-2293 / SPARK-2320 change 'Detach And Cancel Selected Orders' to 'Detach Selected' 2019-04-25 23:07:28 -05:00
John Colvin 747cde4a25 TickDisplayConverter - format price with default ContractSpec if spec is null (also accounts for nan formatting) 2019-04-25 22:56:45 -05:00
John Colvin 9b6a5ea943 remove hacky ILazyAlgo interface, replace functionality with subscribe_is_dirty extension 2019-04-25 19:05:03 -05:00
John Colvin 00b68a4408 remove file that was added by accident 2019-04-25 18:36:20 -05:00
John Colvin d14826ea96 IAlgoVM - remove unused 'child_params' member 2019-04-25 18:34:41 -05:00
John Colvin 4143a8cd05 TriggerOrderVM - correct 'price type' and 'payup ticks' prop name args 2019-04-25 18:24:30 -05:00
John Colvin 1ae3f0d3e0 made PriceMinMax filter for orderbook 2019-04-25 17:55:25 -05:00
Jake Norbut 52b762ee1c miniob: when cursor in price and quantity, enter key commits 2019-04-25 16:29:57 -05:00
Jake Norbut 5c2fdf022c mini ob - tab now cycles between price and qty when a row is selected 2019-04-25 14:51:11 -05:00
Jake Norbut f374570907 fix for if qty is changed via update popup and the edit textbox does not reflect it; accidentally merged out 2019-04-25 13:49:54 -05:00
Jake Norbut 8ea1243777 Merged SPARK-2306_miniob_nits into SPARK-1944-attach/detach/update 2019-04-25 13:17:32 -05:00
Jake Norbut 12141f6c1d Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-2306_miniob_nits 2019-04-25 13:15:24 -05:00
John Colvin d1a4ebda4c SPARK-2293 / SPARK-2312 attach params grid needs 'modified' animations (like update grid) 2019-04-25 13:14:56 -05:00
John Colvin 5f76759c35 SPARK-2293 / SPARK-2311 attach algo dropdown should only show attachable algos 2019-04-25 12:55:55 -05:00
Jake Norbut a1b32e3e6e combine attach and detach columns into 1 column to rule them all, and in the darkness bind them 2019-04-25 12:25:08 -05:00
John Colvin 744bad2bb0 MaskIndexInt32 was not actually using maskable bits 2019-04-25 12:17:53 -05:00
John Colvin 5a1dd952d2 SPARK-2293 / SPARK-2310 clean up main context menu 2019-04-25 11:57:55 -05:00
John Colvin 98580746e0 SparkMenu - add 'group' property to SparkCommand, implement group style in ContextMenu 2019-04-25 11:57:55 -05:00
John Colvin 7c39f1318b made AreEqualConverter (IMultiValueConverter) 2019-04-25 11:57:55 -05:00
John Colvin 28f8d62a1a made methods EnumerableExtensions.CountAtLeast/Most 2019-04-25 11:57:04 -05:00
Jake Norbut 4436cf9b04 adding new col to keys 2019-04-25 10:48:18 -05:00
Jake Norbut 2cd19a7984 bus.post for price and size in one 2019-04-25 10:47:41 -05:00
Jake Norbut f956ce1d2c adding new Command that can change size and price in one operation 2019-04-25 10:46:59 -05:00
Jake Norbut 1e578a5c77 creating new CommitColumn; modifying OnCommit properties of other up/downs to be bound to new command 2019-04-25 10:46:27 -05:00
Jake Norbut adaac94e9c StaysOpen should be false (was turned on for debugging) 2019-04-25 10:45:34 -05:00
Jake Norbut 5832034208 adding new Commit column 2019-04-25 10:45:01 -05:00
Jake Norbut d45366311a abandon tinkering with this fix handles the bug 2019-04-25 08:49:28 -05:00
Jake Norbut 395463dee9 Debounce the OnPreviewMouseWheel 2019-04-25 08:48:27 -05:00
Jake Norbut acfbf51209 abandoning TextBox ContentTemplate customization as the ScrollViewer bug is still present 2019-04-25 08:47:41 -05:00
Jake Norbut 9872e31a0a adding handy utility function for Deboucning and Throttling 2019-04-25 08:46:01 -05:00
John Colvin a80b650dcc SPARK-2309 crash in synth editor when editing (typing) ticks 2019-04-24 22:42:56 -05:00
John Colvin 0051d7f9d8 SPARK-2308 Mass updates are not applying to all rows 2019-04-24 21:03:03 -05:00
John Colvin 8f69e687ec SPARK-2305 It should not be possible to attach to orders that have live parents 2019-04-24 18:10:10 -05:00
Pedro Pinto e0438c25b0 Added test helper. 2019-04-24 18:57:02 -04:00
Pedro Pinto 2a2d3eddd3 Moved lean order to refactored API. 2019-04-24 18:23:25 -04:00
Pedro Pinto bb14b8ca3d Moved join, cover, iceberg to refactored API. 2019-04-24 17:43:31 -04:00
Jake Norbut 6768c5be02 trying styles 2019-04-24 16:30:17 -05:00
Pedro Pinto 80b19b616a Added refactoring of order API, moved Top order to use it. 2019-04-24 14:46:53 -04:00
Jake Norbut 98ab218b10 no rockers 2019-04-24 13:06:14 -05:00
Taha Afzal ee67f14f25 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-24 11:50:26 -04:00
Taha Afzal fb741fbf46 LeanOrder2 is mostly working, needs more tests 2019-04-24 11:50:08 -04:00
Pedro Pinto d7fe1c43b7 Removed obsolete iceberg2 2019-04-24 08:39:41 -04:00
John Colvin c402c898be TableUpdate.ParentDependent - fix added iter 2019-04-23 22:59:16 -05:00
John Colvin e6f4c9c1ad TableUpdate.FilterWithRaw - remove temp diagnostic code added by accident 2019-04-23 22:59:16 -05:00
John Colvin b6e86c1ffd TableUpdate.ParentDependency - update the parent_to_children 'child' entry when a value changes 2019-04-23 22:59:16 -05:00
John Colvin f18914764a TableUpdate.ParentDepend - refactor TSource to T, 'o'(order) to 'v'(value) 2019-04-23 22:59:16 -05:00
John Colvin 7f600e361a SPARK-2302 (+SPARK-2300) Mini OB refresh_is_attachable/detachable no longer being called 2019-04-23 21:49:08 -05:00
John Colvin 4a2edadf6d in TableUpdate filter-pred-changed method, when old-pred == new-pred clear out table's changes before returning it 2019-04-23 21:49:08 -05:00
John Colvin a32283a8bd DictionaryHelper.empty should definitely not be mutable 2019-04-23 21:49:08 -05:00
John Colvin a46e7e62cc move (Sorted)SetMap into spark_core 2019-04-23 21:48:52 -05:00
Taha Afzal 9c465baac9 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-23 20:02:57 -04:00
Taha Afzal d8f118cfe9 Lean adhering to the new API 2019-04-23 20:02:40 -04:00
Michael Harding 8a53863218 Merge branch 'master' into spreader_test 2019-04-23 16:16:19 -04:00
Pedro Pinto 4cc61057d2 Removed bogus pause in iceberg. 2019-04-23 14:42:10 -04:00
Pedro Pinto 9fafc2d8b5 Minor refactoring 2019-04-23 14:32:51 -04:00
Pedro Pinto 0b7e3e2c17 Fixed a bug in the handling of failed attachment. 2019-04-23 14:32:34 -04:00
Pedro Pinto 2e4413f44f Made sim more resiliant to daily session reset. 2019-04-23 12:43:38 -04:00
Pedro Pinto 65056c7eef Changing defaults in sim connection to try to connect forever. 2019-04-23 12:43:38 -04:00
Pedro Pinto ccfdb6b7b5 Adding buffering of cancels/updates to native order. 2019-04-23 12:43:38 -04:00
Jake Norbut d6da4e7a26 Defaulting many OB columns for Right alignment 2019-04-23 10:52:26 -05:00
Jake Norbut 2893233a03 adding some margin to Update button 2019-04-23 10:34:44 -05:00
Jake Norbut 0f3988c8e2 extend cancel ability to Disconnected orders 2019-04-23 10:13:02 -05:00
Jake Norbut 47e272702c adding helper function for joining banding path; more use of nameof in OBColumns instead of vanilla strings 2019-04-23 09:48:09 -05:00
Jake Norbut 428a51b071 updating prosepctive quantity field when the quantity has changed underneath us 2019-04-23 08:22:47 -05:00
Jake Norbut 19a9c27d9c Merged 1944-miniob_update into SPARK-1944-attach/detach/update 2019-04-23 07:42:27 -05:00
Jake Norbut 45a4bcf8b5 fix merge 2019-04-23 07:41:49 -05:00
Jake Norbut 00c2ac4aeb changing from button to dots; moving column 2019-04-23 07:39:32 -05:00
John Colvin f03db0180b MarketViewVM - 'size' cols should use UIntMinMax filter templates (not UShort) 2019-04-22 23:36:24 -05:00
John Colvin 95755f53a1 fix up TickUpDownNullableULong in the same manner as TickUpDownInt, UInt, etc. 2019-04-22 23:31:03 -05:00
John Colvin de5b0a4264 fix up TickUpDownULong in the same manner as TickUpDownInt, UInt, etc. 2019-04-22 23:31:03 -05:00
John Colvin f1f9503b3a fix up TickUpDownNullableUShort in the same manner as TickUpDownInt, UInt, etc. 2019-04-22 23:31:03 -05:00
John Colvin c144631759 fix up TickUpDownUShort in the same manner as TickUpDownInt, UInt, etc. 2019-04-22 23:24:47 -05:00
John Colvin 9fafe6bae5 FilterTemplates.xaml - line endings 2019-04-22 23:05:22 -05:00
John Colvin 757fc0eab5 OperableHierarchicalCollection - reword some comments 2019-04-22 22:38:15 -05:00
John Colvin 227179087d OperableHierarchicalCollection - in 'collapse_row,' return after removing continuous block (no need to continue enumeration) 2019-04-22 22:37:56 -05:00
John Colvin 33140fa901 OperableHierarchicalCollection - in 'collapse_row,' only call '_sync' if refresh is deferred 2019-04-22 22:25:18 -05:00
John Colvin 0d94377f36 OrderBookRowVM - need to pass 'is_expanded' prop name arg when calling 'Set' outside of the property setter 2019-04-22 22:18:43 -05:00
John Colvin 66b92c26a1 OperableHierarchicalCollection - actually Clear rows before re-adding on reset 2019-04-22 22:17:13 -05:00
John Colvin c74941b9c1 remove obsolete OrderBookRowCollection 2019-04-22 17:53:04 -05:00
John Colvin a705235a99 OrderBookVM - replace OrderBookRowCollection with new generic OperableHierarchicalCollection 2019-04-22 17:53:04 -05:00
John Colvin 6e5ae8b9e0 OrderBookRowVM implements IHierarchicalRow 2019-04-22 17:53:04 -05:00
John Colvin df53c9810b made OperableHierarchicalCollection 2019-04-22 17:53:04 -05:00
John Colvin e84ea8b8e5 move 'RXOperableCollection' into spark_core, rename 'OperableCollection' 2019-04-22 17:53:04 -05:00
John Colvin 9e347a858c move CollectionHelper into spark_core 2019-04-22 17:53:04 -05:00
John Colvin f53b791703 SortedTree - 'Remove' returns bool 2019-04-22 17:53:03 -05:00
John Colvin efa267d5fb move I(ReadOnly)ObservableCollection into spark_core 2019-04-22 17:53:03 -05:00
John Colvin fb1e60229e OrderBookRowCollection - optimize expand/collapse all 2019-04-22 17:53:03 -05:00
John Colvin 00fa662883 SPARK-2196 OB filter 2019-04-22 17:53:03 -05:00
John Colvin 4ef8aaa689 DIYGrid - don't invoke SelectionChanged callback unless it is actually coming from the DIYGrid (i.e., not some random child control) 2019-04-22 15:56:23 -05:00
Jake Norbut 64164db22e so that john can take a look 2019-04-22 14:59:22 -05:00
John Colvin dfb2178fd8 TableUpdate.Filter extension supports null filters 2019-04-22 12:47:13 -05:00
John Colvin 2be2163b7e DeferrableActionWrapper uses flags to support multiple actions 2019-04-22 12:47:13 -05:00
John Colvin 711ec49d88 move DeferrableActionWrapper into bts.utils namespace 2019-04-22 12:47:13 -05:00
John Colvin 710eaf5ef8 move DeferrableActionWrapper into spark_core 2019-04-22 12:47:13 -05:00
John Colvin ccddf3b1cd OrderBookRowVM doesn't need to derive from DisposableViewModelBase (keep it super light) 2019-04-22 12:47:13 -05:00
John Colvin 3aefa57578 OrderBookRowVM - fix typeo that excluded prop-name argument when reassigning is_expanded 2019-04-22 12:47:13 -05:00
John Colvin ea4b330062 SPARK-2299 - Update panel modify/revert buttons stay enabled when they shouldn't 2019-04-22 12:41:32 -05:00
John Colvin da6b7d5011 lazy init for DialogButtonStyle 2019-04-22 11:57:05 -05:00
John Colvin add897f4a6 fix QuantityMeasureControl visual state transition bug 2019-04-20 12:21:11 -05:00
John Colvin 5cb535e5df OrderBookView - don't hide params when 0 rows selected (was conflicting with selected tab) 2019-04-18 16:39:29 -05:00
Jake Norbut 106c9a6922 refining is_detachable to not show for terminal orders; i bet taha was using a non attach/detach server when this came up 2019-04-18 15:57:30 -05:00
Jake Norbut 1c87d414e0 mini ob - adding red and green borders around attach detach buttons 2019-04-18 15:57:30 -05:00
Pedro Pinto 62b0f1938b Moved Iceberg to new API. 2019-04-18 16:47:17 -04:00
Pedro Pinto a7e04aa7b0 Fixed cover order bug. 2019-04-18 16:47:17 -04:00
Jake Norbut aa9f81f5c9 mini ob - hide last change, show time, hide symbol 2019-04-18 14:56:55 -05:00
John Colvin df18758643 OrderBookToolBar - fix VisualState transitions 2019-04-18 11:37:32 -05:00
John Colvin b351fa9531 made Mouse/CheckedStateDefinitions modal 2019-04-18 11:37:32 -05:00
Pedro Pinto 5303d2fd89 Fixed a cover bug found by Brian. 2019-04-18 09:49:24 -04:00
Pedro Pinto 3e29427679 Beggining to move IB over to new API. 2019-04-18 09:07:30 -04:00
Pedro Pinto d240b75b95 Improved AlgoOrder API. 2019-04-18 08:59:47 -04:00
Pedro Pinto cb6621ac06 Cleaned up docs for AlgoOrder 2019-04-18 08:15:47 -04:00
John Colvin cc1d796695 OrderBook - put modify/revert buttons directly beneath the params grid (requires less mouse movement) 2019-04-17 21:47:02 -05:00
John Colvin ed6f2d1c28 OrderBookView - fix typeo in xaml element name 2019-04-17 21:42:06 -05:00
Pedro Pinto 54d23ac2d3 By default attached orders are subscribed to. 2019-04-17 17:48:07 -04:00
Pedro Pinto 2348e1bf31 Refactor AlgoOrder, completed Cover order. 2019-04-17 17:41:53 -04:00
John Colvin ad09a6ad4b OrderBook wasn't restoring 'show_right_panel' when loading workspace 2019-04-17 15:24:58 -05:00
John Colvin d8a47267f6 OrderBookRowCollection - remove unneccesary using, unify line endings 2019-04-17 13:10:54 -05:00
John Colvin c882fb1018 SPARK-2292 OrderBook - make right panel toolbar buttons look like tabs in tab mode 2019-04-17 12:51:47 -05:00
John Colvin 5777685bf3 made RemainingDimensionConverter (will be used in OrderBookToolBar) 2019-04-17 12:51:46 -05:00
John Colvin a1053cc41e made new ModalStateDefinition class (will be used in OrderBookToolBar) 2019-04-17 12:51:46 -05:00
John Colvin a934021a5a CheckedStateDefinition - reset State when CheckableSource changes 2019-04-17 12:39:08 -05:00
John Colvin 24f5a54756 RadioButtonGroupManager - syntax change (don't needlessly wrap Action) 2019-04-17 11:20:59 -05:00
John Colvin 43af64b298 OrderBookToolBar - UseLayoutRounding=True 2019-04-17 11:16:18 -05:00
John Colvin 314bbd4f06 MouseStateDefinition - refresh visual state when MouseSource changes 2019-04-17 00:02:35 -05:00
John Colvin e8adb8fc68 StateManger - remove 'AlwaysTransitionOnLoaded' hack, replace with real fix 2019-04-16 19:56:19 -05:00
John Colvin 49de685cd4 OrderBookVM - fix stackmode 'visible_components' bit arithmetic 2019-04-16 19:28:42 -05:00
John Colvin d4ee0db19b OrderBookView - in param grid ContextMenu, use grid.SelectedItem to get the target row 2019-04-16 14:24:49 -05:00
John Colvin 2171a31e5a OrderBookVM - fix 'revert when selection becomes inactive' behavior 2019-04-16 14:23:09 -05:00
John Colvin b35c52ae27 QuantityMeasureControl - visual state gets lost after the control is unloaded/(re)loaded 2019-04-16 13:15:17 -05:00
John Colvin 055e27f9aa SPARK-2288 improve ladder startup performance 2019-04-16 13:15:17 -05:00
Michael Harding 6ad12e922c Submit and confirm working spreader order 2019-04-16 13:03:36 -04:00
Pedro Pinto 489af5a703 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-16 11:23:49 -04:00
Pedro Pinto 1f03f4b839 Moved JoinOrder to new API. 2019-04-16 11:22:58 -04:00
John Colvin 464dada0c2 OrderBook - param grid exits edit mode after committing modifications via the context menu 2019-04-15 23:44:12 -05:00
John Colvin fb73752d5c AlgoEditorGridView - change price control 'Value' binding UpdateSourceTrigger to 'PropertyChanged' 2019-04-15 23:19:38 -05:00
John Colvin 766c9d10c4 OrderBook - fix typeo in modify button tooltip 2019-04-15 23:02:16 -05:00
John Colvin bd54a9fd01 OrderBookView - increase margin between modify/revert buttons 2019-04-15 23:01:39 -05:00
John Colvin 9ff4673935 OrderBook - contextmenu for params grid (Revert Param, Revert All, Commit All) 2019-04-15 20:08:02 -05:00
John Colvin 282771f9f5 OrderBook - revert edited values when user selects an inactive order 2019-04-15 20:04:54 -05:00
John Colvin d60986c5f0 AlgoEditorGridView - ctrl+up/down arrow key increments/decrements value in TickUpDown cells 2019-04-15 20:04:54 -05:00
Pedro Pinto 12fc59af41 Added extra assert. 2019-04-15 18:25:12 -04:00
Pedro Pinto 19fca8304d Simplified TopOrder, deleted TopOrder2. 2019-04-15 18:08:37 -04:00
Pedro Pinto 81a0e1376c Refactored. 2019-04-15 13:43:40 -04:00
Pedro Pinto 6e5451e314 Moved top order to use new AlgoOrder API. 2019-04-15 13:33:21 -04:00
Pedro Pinto a58d7b3a97 Added some documentation to AlgoOrder 2019-04-15 13:33:21 -04:00
Pedro Pinto b7b0b46709 Added a new AlgoBase class that should serve as the new Order API 2019-04-15 13:33:21 -04:00
Pedro Pinto ee4f74aad5 Cleaned up algos a bit. 2019-04-15 13:33:21 -04:00
Pedro Pinto ab8d8b9717 Made NativeOrder cancel idempotent.
This makes AlgoBase simpler.
2019-04-15 13:33:21 -04:00
Pedro Pinto 669790f12f Made attachment to disconnected orders illegal.
This seems sensible and it simpliefies AlgoOrder
2019-04-15 13:33:21 -04:00
John Colvin c5bd45a429 AlgoEditorGridView - focus TextBox in when QuantityMeasure cells enter edit mode 2019-04-15 11:32:58 -05:00
John Colvin 8ef977e468 OrderTypeVMBase - no need to raise definition_changed when 'editing_permissions' changes 2019-04-15 11:06:07 -05:00
John Colvin 8d38f3aae5 OrderTypeVMBase - defer raising definition_changed during 'load_from()' 2019-04-14 23:00:24 -05:00
John Colvin 83b7c8ae8d AlgoEditorGridView - shorten 'cross the market' tooltips 2019-04-14 22:30:19 -05:00
John Colvin b71c5fb27d SPARK-2275 / SPARK-2284 remove erroneous ToolTip binding AlgoTreeViewItemContainerStyleBase 2019-04-14 19:15:24 -05:00
John Colvin c536300020 SPARk-2275 / SPARK-2283 remove animation binding in OrderBook's security column view_factory 2019-04-14 19:07:10 -05:00
John Colvin 37a054800b SPARK-2275 / SPARK-2282 don't apply DialogSkirtButtonStyle to Save/Revert buttons in AlgoStudioView (dsb style assumes datacontext is DialogSkirtItem) 2019-04-14 19:02:17 -05:00
John Colvin 8ceb3a4c77 SPARK-2275 / SPARK-2281 use CastConverter on MouseSource/CheckableSource bindings 2019-04-14 18:56:49 -05:00
John Colvin 91146fdfb9 made simple CastConverter class (IValueConverter) 2019-04-14 18:51:39 -05:00
John Colvin 8f5ca8d783 SPARK-2275 / SPARK-2280 don't apply DialogSkirtButtonStyle to Save/Revert buttons in AlgoStudioView (dsb style assumes datacontext is DialogSkirtItem) 2019-04-14 18:51:38 -05:00
John Colvin 2364c1123c SPARK-2275 / SPARK-2279 remove bad Background binding in DIYGrid's GridViewColumnHeader ControlTemplate 2019-04-14 18:12:55 -05:00
John Colvin 3b27f560fb SPARK-2275 / SPARK-2278 remove erroneous Horizontal/VerticalContentAlignment bindings in 'AlgoTreeViewItemContainerStyleBase' 2019-04-14 18:12:42 -05:00
John Colvin c741093ab9 SPARK-2275 / SPARK-2277 remove DIYGrid hidden edit-control (a vestige from AW2, unused in Spark) 2019-04-14 17:48:54 -05:00
John Colvin 405685ece3 SPARK-2275 / SPARK-2276 remove erroneous 'start_command' binding 2019-04-14 17:34:04 -05:00
John Colvin 6de078473b SPARK-2208 / SPARK-2274 non-uniform price is displayed as '0,' should be '-' 2019-04-14 15:37:41 -05:00
John Colvin f9a131a480 CommonValueMonitor - actually use 'default_value' param 2019-04-14 15:35:17 -05:00
John Colvin 16ba5c42ab OrderBookToolBar - change 'IsTabStop=False' to 'IsFocusable=False' in RightPanel.MenuItem style (menu-items were stealing focus on hover) 2019-04-14 15:13:52 -05:00
John Colvin 0e892817bf OrderBookToolBar - remove unselect-when-mouse-leaves trigger in ToolBarOverflowButtonStyle 2019-04-14 15:11:12 -05:00
John Colvin 0d01f7f365 SPARK-2269 UI Crash (null ref in EnumDescriptionConverter.Convert) 2019-04-14 15:00:41 -05:00
John Colvin f67669a830 Ladder - clicking a favorite button that is already selected pops the selection back to the last algo that was selected via the dropdown 2019-04-14 14:56:11 -05:00
John Colvin 4f5ff80199 made 'SetStack' class and corresponding unit tests 2019-04-14 14:55:01 -05:00
John Colvin c84b3c576b SortedList - made 'RemoveAt' method 2019-04-14 14:39:32 -05:00
John Colvin f4490623b3 rename GenericCollectionExtensions.cs -> EnumerableExtensions.cs 2019-04-14 11:44:18 -05:00
John Colvin 2b00c00459 move ObservableCollectionExtensions into its own file 2019-04-14 11:43:43 -05:00
John Colvin 7e5ea37782 remove unused HashSetExtensions class 2019-04-14 11:37:59 -05:00
John Colvin 9ad140d774 consolidate 2 DictionaryExtensions classes into 1 2019-04-14 11:21:45 -05:00
John Colvin 67a457cf98 (4/4) consolidate 2 CollectionExtensions classes into 1 - remove TryGetItem extension 2019-04-14 11:16:48 -05:00
John Colvin e3305bc3f5 (3/4) consolidate 2 CollectionExtensions classes into 1 - move within_range methods/create ArrayExtensions class 2019-04-14 11:10:20 -05:00
John Colvin 171fcab814 (2/4) consolidate 2 CollectionExtensions classes into 1 - move SetItems method 2019-04-14 10:56:41 -05:00
John Colvin 0723f0ae1e (1/4) consolidate 2 CollectionExtensions classes into 1 - move AddRange methods 2019-04-14 10:55:44 -05:00
John Colvin c1d0c159af consolidate 2 ListExtensions classes into 1 2019-04-14 10:35:28 -05:00
John Colvin 65ec3f6d0a move GenericCollectionExtensions.cs into spark_core 2019-04-14 10:30:14 -05:00
John Colvin 009e709384 OrderBook - move RightPanel.MenuItem style to OrderBookToolBar.xaml, replace ControlTemplate triggers with VisualStates 2019-04-13 18:29:44 -05:00
John Colvin c124e3dfd2 made new StateDefinitions: MouseStateDefinition, CheckedStateDefinition 2019-04-13 18:28:25 -05:00
John Colvin c6f98630dc RadioButtonGroupManager - factor out ToggleButton/MenuItem subscribe-checked impl 2019-04-13 18:26:27 -05:00
John Colvin d270411578 SPARK-2208 / SPARK-2270 params grid should stretch to the width of the panel 2019-04-13 14:50:25 -05:00
John Colvin eea68f438b DataGridEditHelper - ignore usual togglebutton behavior when QuantityMeasureControl cell enters edit mode 2019-04-13 14:50:25 -05:00
John Colvin 575b2c0bb6 QuantityMeasureControl - made new ratio-checkbox ControlTemplate 2019-04-13 14:50:25 -05:00
John Colvin e024edb6f7 QuantityMeasureCaseDisplayTextConverter - made public static convert method 2019-04-13 14:50:25 -05:00
John Colvin ef4bcfb1b8 TickUpDown - set TemplateBindings for (Min/Max)Width props 2019-04-13 14:50:24 -05:00
John Colvin 94e5b8410c AlgoEditorGridView - bbo price hyperlink cmds payup 'n' ticks if number key is pressed when clicked 2019-04-13 14:50:24 -05:00
John Colvin 9edb0f2b9c TickUtils - tickup/down support negative input (just ticks in opposite direction) 2019-04-13 14:43:05 -05:00
John Colvin 440f0f1e85 DIYGridRowsPresenter - set 'IsTextSearchEnabled' to false (in default style) 2019-04-13 11:35:57 -05:00
John Colvin 707e6013cc SPARK-2208 / SPARK-2271 params grid - make 'Enter' key behave like 'Tab' 2019-04-12 20:05:12 -05:00
John Colvin 80ee8d1afa SPARK-2208 / SPARK-2272 params grid - get rid of 'Pen' cursor 2019-04-12 19:55:38 -05:00
John Colvin 9923cd4b9b SPARK-2208 / SPARK-2273 keyboard shortcuts for modify/revert 2019-04-12 19:46:02 -05:00
John Colvin 3a4ec52766 replaced SparkWindowView.HeaderConverter with CommandHelper.DescriptionConverter (can be used w/ non-spark cmds as well) 2019-04-12 16:56:22 -05:00
John Colvin 00548ea6dd AlgoEditorGridView - best bid/ask hyperlink tooltips don't say 'click to assign' when disabled 2019-04-12 16:11:10 -05:00
John Colvin 6e654049bd AlgoParamRowVM - 'set_value_cmd' can only execute when param.permissions == 'ReadWrite' 2019-04-12 16:11:10 -05:00
John Colvin 71c94b8c8d OrderBookVM - update algo param permissions when 'selected-orders-all-active' changes 2019-04-12 16:11:10 -05:00
John Colvin cd9875f67a add 'permissions_changed' event to IAlgoParamVM 2019-04-12 16:02:05 -05:00
John Colvin 4a118c76ac ViewModelBase - overload 'Set' method to out previous value 2019-04-12 15:59:31 -05:00
John Colvin 8d263cdd37 remove redundant 'SubscribeHookLatest' method, redirect callers to 'SubscribeEach' 2019-04-12 13:55:02 -05:00
John Colvin f56d1a570a AlgoEditorGridView - 'Click to edit' tooltip 2019-04-12 13:55:02 -05:00
John Colvin 63dd6ea835 OrderBook/AlgoEditorGridView - display bbo in price/size param rows 2019-04-12 13:55:01 -05:00
John Colvin 6c19f959fa initialize AlgoParamVM.has_value 2019-04-12 13:45:26 -05:00
John Colvin c4a180f936 AlgoParamRowVM implements IEditableObject 2019-04-12 13:45:26 -05:00
John Colvin 2c272e936f SPARK-2208 / SPARK-2266 remove price/size controls, add them as the first 2 param rows instead 2019-04-12 13:45:26 -05:00
John Colvin 55c859e928 OrderBook right panel toolbar should not have any tab stops 2019-04-12 13:45:26 -05:00
John Colvin 98411ad036 template selector infrastructure 2019-04-12 13:45:26 -05:00
John Colvin d7719cf79c AlgoEditorGridView - minor tweaks to streamline tab navigation editing 2019-04-12 13:45:26 -05:00
John Colvin e7c606495b AlgoEditorGridView - define price cell templates 2019-04-12 13:45:25 -05:00
John Colvin 73cc7ceec3 AlgoParamTemplateSelector - infrastructure for explicit/keyed templates (price cell needs this) 2019-04-12 13:45:25 -05:00
John Colvin 475d5e3ff1 DataGridEditHelper - refactor to use static 'OneOf.Match' (one-liner) 2019-04-12 13:45:25 -05:00
John Colvin 33f399893e OneOf - made static Match() method 2019-04-12 13:45:25 -05:00
John Colvin 015be785e1 move AlgoParamVM into its own file 2019-04-12 13:45:25 -05:00
John Colvin 446a0175c6 ObservableAppendCollection implements IReadOnlyObservableCollection 2019-04-12 13:45:25 -05:00
John Colvin a5a438976f OrderBookView - remove unused resource in xaml 2019-04-12 13:45:25 -05:00
Taha Afzal 26f0593702 manual test for lean 2019-04-12 12:24:53 -04:00
Taha Afzal 85c73783f3 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-11 17:24:25 -04:00
Taha Afzal 79ddb8ca16 new basic lean test 2019-04-11 17:24:20 -04:00
Michael Harding 3171ae0afb Create a Synthetic Security 2019-04-11 16:06:59 -04:00
Jake Norbut 6f97ddd57d Only create the Adorner when the SelectionMode is not Single 2019-04-11 14:06:24 -05:00
Jake Norbut ce4f850cc4 DIYGrid - revert to previous select mode when on Multi SelectionMode grids
There are 2 reasons for this reversion.

John mentioned that when selecting many rows in MV, the scolling of selected rows progressively
worsened as more rows were selected.

Next, if you were selecting many rows in the grid by way of clicking and
then dragging your mouse over the bottom area of the window outside of
the actual grid, then releasing the mouse button while your mouse was
still outside of the grid, we would not select the pages of rows,
instead only the last one would be selected. This is different from how
the Adorner selection works now.
2019-04-11 14:06:24 -05:00
John Colvin dfcef93882 SPARK-2208 / SPARK-2265 when editing multiple orders, ignore params with non-uniform values (use each order's current value) 2019-04-11 13:10:18 -05:00
John Colvin 667234b05b convert some algo params/props to nullable<T>s (null indicates 'non-uniform-value' when mutliple orders are selected) 2019-04-11 13:10:18 -05:00
John Colvin c2c460875d check correct algo type and that child param exists for Dynamic case OrderDataHelper.get_child_algo_data 2019-04-11 12:53:19 -05:00
John Colvin 7df7d05628 move OrderDataHelper.cs and OrderUtils.cs into 'Orders' dir 2019-04-11 12:50:16 -05:00
Jake Norbut 61a4ade2f4 Merged in SPARK-2207 (pull request #213)
SPARK-2207 mini ob ui review

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2019-04-11 16:54:40 +00:00
Jake Norbut 594c2e47b0 PR suggestion - Count() be gone 2019-04-11 11:43:57 -05:00
Jake Norbut b6df4b64b8 test should actually return the right nodes 2019-04-11 11:17:55 -05:00
Jake Norbut 7190d949f5 PR suggestion - only get children when replace actually does something; add test 2019-04-11 11:14:11 -05:00
Jake Norbut 51db2e75f7 PR suggestion - One RaisePropertyChanged call FTW! 2019-04-11 10:10:12 -05:00
Jake Norbut 992d56303c PR suggestion - RaisePropertyChanged when non-terminal status changes, not indiscriminantly; great find john 2019-04-11 10:04:29 -05:00
Taha Afzal 84f249439c Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-11 10:56:51 -04:00
Taha Afzal 7d09a906c6 Updating the tests 2019-04-11 10:56:43 -04:00
Jake Norbut 80cb260082 PR suggestion - ort -> osam; just updating variable three-letter-acronym to match the actual type 2019-04-11 09:50:59 -05:00
Jake Norbut fc47d16800 PR suggestion - Capitalizing 'K' in 'OK' 2019-04-11 09:31:20 -05:00
Jake Norbut ae237d36b6 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-2207 2019-04-11 07:27:15 -05:00
John Colvin 4182ab1093 OrderBook - fix order-params 'IsReadOnly' binding 2019-04-10 23:34:47 -05:00
John Colvin 9ba7b77a7a SPARK-2250 OrderBook - put fills/ladder/params controls in separate tabs 2019-04-10 23:34:47 -05:00
John Colvin c8adf1c972 RadioButtonGroupManager - support MenuItems as honorary 'checkable' toggles (they don't actually derive from ToggleButton) 2019-04-10 19:08:25 -05:00
John Colvin 05275c5d0d OrderBookSettings - put 'right-panel' settings in their own class 2019-04-10 19:08:25 -05:00
John Colvin 3c5e7bfef9 move OrderBook settings classes into separate files 2019-04-10 19:08:25 -05:00
John Colvin 5724d13b7d OrderBook - in 'attach' popup, don't animate 'is_dirty' since the user is creating a new order 2019-04-10 19:08:25 -05:00
Taha Afzal 0491b4b153 Top order test 2019-04-10 18:40:57 -04:00
Taha Afzal dc1d045716 join basic test 2019-04-10 17:17:24 -04:00
Jake Norbut 484fb7b93f SPARK-2261 - canceled orders from detachment no longer display as if they can be canceled 2019-04-10 16:13:44 -05:00
Jake Norbut e7286856c9 SPARK-2260 - show edit mode for only non terminal orders 2019-04-10 16:06:04 -05:00
Jake Norbut 33804a8cb2 SPARK-2255 - middle click will now close an open mini ob 2019-04-10 15:16:26 -05:00
Jake Norbut 9f6a894ddf Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-2207 2019-04-10 14:57:33 -05:00
Jake Norbut 37160cc477 fix root node not updating after descendant detach 2019-04-10 14:53:50 -05:00
Jake Norbut be7553956f detach should detach on this rows parent, not the root of the tree 2019-04-10 14:21:02 -05:00
Jake Norbut 23a1eb323f update is_attachable definition so that if an order is in a terminal state, then it is not attachable 2019-04-10 14:19:43 -05:00
Jake Norbut 3d7f469708 popup should not stay open, leftover from debugging 2019-04-10 12:19:25 -05:00
Jake Norbut 0bf9939d17 SPARK-2259 - changing single Send Attached orders button to Ok and Cancel, and using the same DialogSkirt. Thx John for the help on this one, several nuances here in getting things just right 2019-04-10 12:17:19 -05:00
Taha Afzal 0da056f282 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-10 10:02:13 -04:00
Taha Afzal 129da00daa Cover test 2019-04-10 10:02:03 -04:00
John Colvin 962f41b5d6 AlgoEditorGridView - for param cell 'write' templates, UpdateSourceTrigger=PropertyChanged 2019-04-09 17:16:10 -05:00
John Colvin cdc0fee934 QuantityMeasureControl - internal bindings UpdateSourceTrigger=PropertyChanged 2019-04-09 17:16:10 -05:00
John Colvin 589227a97f made TickUpDownUInt adopt the textbox behavior/implementation logic of TickUpDownDouble 2019-04-09 17:16:10 -05:00
John Colvin bde9f6e565 made TickUpDownNullableUInt adopt the textbox behavior/implementation logic of TickUpDownDouble 2019-04-09 17:16:10 -05:00
John Colvin 7b6bab1767 made TickUpDownInt adopt the textbox behavior/implementation logic of TickUpDownDouble 2019-04-09 17:16:09 -05:00
John Colvin 729cb5cddc made TickUpDownNullableInt adopt the textbox behavior/implementation logic of TickUpDownDouble 2019-04-09 17:16:09 -05:00
John Colvin 220a4e6424 added TextUtils.RegexFilters.'SignedInteger' regex 2019-04-09 17:16:09 -05:00
John Colvin 37356b2b59 rename TextUtils RegexFilter.'Integer' -> 'UnsignedInteger' (it's always prevented negative numbers) 2019-04-09 17:16:09 -05:00
John Colvin 42ce4f1bf8 OrderBook - modify/revert buttons are not enabled until user changes a param 2019-04-09 17:16:01 -05:00
John Colvin 455515f658 added 'is_dirty_changed' event to IAlgoParamVM 2019-04-09 16:55:21 -05:00
John Colvin ad61783e15 AlgoEditorGridView - replace 'AlgoVM' dp with three props: AlgoRoots, SelectedAlgoRoot, and ParamRows (the rows needed to move to the vm) 2019-04-09 16:55:10 -05:00
Jake Norbut c8d958506c SPARK-2257 - swap edit and checkbox locations in edit mode for better alignment than what we have now 2019-04-09 15:15:41 -05:00
Jake Norbut c528685283 SPARK-2256 - ob to get a new OPP via create_common 2019-04-09 14:30:44 -05:00
Jake Norbut 4c6522d33d SPARK-2256 - mini ob to get a new OPP via create_common 2019-04-09 14:30:36 -05:00
Jake Norbut eb0930b140 SPARK-2256 - adding a create_common method so changes between OB and Mini OB are kept in sync 2019-04-09 14:30:23 -05:00
John Colvin 31105a1acb add default constructor to CollectionViewSource 2019-04-09 14:07:09 -05:00
Jake Norbut f2421133cb When DIYGrid is in Single SelectionMode, dragging the mouse should not crash it. 2019-04-09 13:57:06 -05:00
Jake Norbut 3fa291fa67 SPARK-2254 - change mini ob to Single SelectionMode 2019-04-09 13:56:08 -05:00
Jake Norbut be3bdc44bc SPARK-2253 - order attachability now excludes terminal orders that don't have parents 2019-04-09 13:35:48 -05:00
Pedro Pinto d7faa92027 Updated cert secure logon file. 2019-04-09 14:11:37 -04:00
Pedro Pinto ddb926d9fa Added top, cover, lean to list of orders that can attach 2019-04-09 13:23:06 -04:00
Taha Afzal 7e4f70d046 removing the hard coded tick size 2019-04-09 12:29:08 -04:00
Jake Norbut bb9b934ef4 Revert "tweaking row_vm attach/detach commands to be bulk-operation aware"
This reverts commit a34cd44967.
2019-04-09 11:28:20 -05:00
Jake Norbut a34cd44967 tweaking row_vm attach/detach commands to be bulk-operation aware 2019-04-09 11:15:07 -05:00
Taha Afzal 044d7725dc simplified reporting 2019-04-09 12:12:15 -04:00
Jake Norbut 8f63db7d19 setting order_param property to true so that user can select algo 2019-04-09 10:53:53 -05:00
Jake Norbut f0cd761559 Merged SPARK-2251 into SPARK-2207 2019-04-09 10:50:24 -05:00
Jake Norbut 22f8287359 assinging Attach button CommandProperty to new row_vm.attach_command 2019-04-09 10:48:29 -05:00
Jake Norbut 6dc979d35f adding some comments regarding the attach button DataContext binding 2019-04-09 10:48:00 -05:00
Jake Norbut e62b936fc5 adding an attach_command to the row_vm for mini ob binding 2019-04-09 10:47:12 -05:00
John Colvin 6c7867917d AlgoEditorGridView - minor style tweaks 2019-04-09 09:41:19 -05:00
Pedro Pinto 9d7093ff9d Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-09 09:32:26 -04:00
John Colvin 625c1cd5c8 OrderBook - in right panel, visible components must occupy the topmost rows (allows GridSplitters to be dragged freely) 2019-04-08 18:38:59 -05:00
Taha Afzal 250fd8f570 comments and variable name change 2019-04-08 18:50:01 -04:00
Jake Norbut 70ff4fa09d Merged in SPARK-2245 (pull request #210)
DIYGrid rows are selected on mouse move
2019-04-08 22:02:31 +00:00
Jake Norbut 79dbf11107 Merged SPARK-2247 into SPARK-2207 2019-04-08 16:59:24 -05:00
Jake Norbut 2e21b623a4 row_selectin_changed, sometimes event args are not OrderBookRowVM, so change Cast to OfType 2019-04-08 16:58:02 -05:00
Jake Norbut 64b5102f7e Merged SPARK-2246 into SPARK-2207 2019-04-08 16:24:02 -05:00
Jake Norbut f20d9b6ca9 remove vm level detach command; refactor detach orders method to allow operation over something other than selected_rows 2019-04-08 16:22:34 -05:00
Jake Norbut 51d02e215a add detach_command to row_vm so that it does not require a grid selection 2019-04-08 16:21:06 -05:00
Jake Norbut 8d6b7d3b2f MiniOB detach is now hooked to detach on row_vm 2019-04-08 16:20:12 -05:00
Taha Afzal c9e61c588e deleted all def cons 2019-04-08 15:46:14 -04:00
Taha Afzal 87ad211a82 deleting == operator on the order 2019-04-08 15:22:07 -04:00
Taha Afzal 377c1c5890 Moved status calls into SpreaderChildOrderState, and made QHOOrderManager call them 2019-04-08 15:00:22 -04:00
Taha Afzal a3ac5c2519 Added more asserts, and introduced pruning 2019-04-08 13:22:40 -04:00
Jake Norbut 79646bb941 OB rows are selected as the mouse moves intsead of on mouse up; adorner region is no longer drawn 2019-04-08 11:35:27 -05:00
Jake Norbut 6804d4f605 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-2207 2019-04-08 09:53:54 -05:00
Pedro Pinto e7bfd07c0d Merged with master 2019-04-08 10:45:00 -04:00
John Colvin a7ae1a89ac OrderBook - hook up mini fills grid sorting/filter settings 2019-04-07 21:02:40 -05:00
John Colvin 90499bd9fa OrderBookVM - remove unused '_state' field 2019-04-07 18:47:04 -05:00
John Colvin 8e73069a2e OrderBookView - mini fills grid DataContext needs to be fills vm container, else sorting doesn't work 2019-04-07 18:34:48 -05:00
John Colvin b3fe85a95a ObservableFilterCollection - fix 'get_insertion_index()' 2019-04-07 18:30:42 -05:00
John Colvin 9862eb88bb FillsVMContainer - columns wrapper and internal fillsvm columns reflect each other's changes (full duplex) 2019-04-07 18:29:43 -05:00
John Colvin 5497f74534 OrderParamsPanel - price and size entry control arrow key navigation is Ctrl+arrow (plain arrow ticks up/down) 2019-04-07 15:43:03 -05:00
John Colvin c8da0fb6c7 SPARK-2208 / SPARK-2239 add a 'Revert' button to discard pending edits 2019-04-07 15:17:39 -05:00
John Colvin f6aa45168f AlgoEditorGridView - param cell 'Pen' cursor 2019-04-07 13:04:34 -05:00
John Colvin b4c846a170 SPARK-2185 Algo update modified parameters 2019-04-07 12:53:06 -05:00
John Colvin 840bb983af AlgoEditorGridView - forgot to define 'NullableIntTemplate' in xaml 2019-04-07 12:52:20 -05:00
John Colvin d6888aa2e9 TriggerOrderVM - made 'is_timout_checked' and 'is_priceout_checked' nullable bools 2019-04-07 11:40:09 -05:00
John Colvin 4652c3006f AlgoEditorGridView - made nullable bool param cell template 2019-04-07 11:39:31 -05:00
John Colvin 0ef0d6c238 OrderParamsPanel - diy arrow-key navigation (default TraversalResquests were all over the place) 2019-04-07 11:27:27 -05:00
John Colvin e24ba90096 PriceEntryControl - enable arrow-key traverse 2019-04-06 14:52:36 -05:00
John Colvin f436471c70 OrderBook - remove some tab stops to streamline keyboard navigation 2019-04-06 14:23:15 -05:00
John Colvin 8c1e43e9c8 Badge - override 'IsTabStop' property metadata, default to false 2019-04-06 14:21:26 -05:00
John Colvin 7395be6ea4 OrderBook - default settings for mini fills grid 2019-04-06 13:27:48 -05:00
John Colvin 64be45e01c fix FillsWindow default 'Size' column header typeo (was 'Side') 2019-04-06 13:27:48 -05:00
John Colvin 9b6ac5dfe9 OrderBook - hide 'save as defaults' button in mini fills grid settings dialog 2019-04-06 13:27:48 -05:00
John Colvin 825651c274 DIYGrid - stretch column header content when no operators (filter/sort) are active 2019-04-06 13:27:48 -05:00
John Colvin 7789748210 DIYGrid - set GridViewColumnHeader background to transparent to remove mouse-hover 'dead zone' (no visual feedback) 2019-04-06 13:27:48 -05:00
John Colvin ae20a98577 DIYGridResources.xaml - normalize spacing (not actual code change) 2019-04-06 13:27:48 -05:00
John Colvin c1fd5a5d27 when OrderBook is closed, mini fills grid settings dialog is closed as well 2019-04-06 13:27:40 -05:00
John Colvin cceb3bd147 Grid Settings Dialog - made 'ActionMenu' thinner and reduced vertical margin between it and main content 2019-04-06 10:14:55 -05:00
John Colvin bdc0e529f7 OrderBook - hook up fills grid settings 2019-04-05 23:29:51 -05:00
John Colvin ddf22c09a6 OrderBook - refactor 'fills_slave_grid' members to 'fills' 2019-04-05 23:12:32 -05:00
John Colvin d4ed08c104 TriggerOrderVM can represent non-uniform values 2019-04-05 22:17:05 -05:00
John Colvin 5a77bb31ce DynamicOrderParamVMs can represent non-uniform values 2019-04-05 22:09:35 -05:00
John Colvin 8f287e939b AlgoEditorGridView - added string param template 2019-04-05 19:32:48 -05:00
John Colvin 23dbb51e4b AlgoEditorGridView - alphabetize param cell templates 2019-04-05 19:32:48 -05:00
John Colvin 1ee035e865 made 'ValueSubstitutionConverterWrapper' (IValueConverter) 2019-04-05 19:32:48 -05:00
John Colvin 9b4a4a9698 ValueMap uses KeyedCollection for quick lookups (was using flat list) 2019-04-05 19:32:48 -05:00
John Colvin 259c0164ba added new 'KeyedCollection' class and corresponding unit tests 2019-04-05 19:32:48 -05:00
John Colvin 55bdeea2f0 DynamicOrderVM - enumerated params cannot assign null to 'EnumValue' in protobuf 2019-04-05 19:32:48 -05:00
John Colvin a5e2ff48c2 OrderBookView - OrderParamsPanel goes into 'readonly' mode (instead of begin completely disabled) when non-working orders are selected 2019-04-05 19:32:48 -05:00
John Colvin a7a07687d6 OrderParamsPanel - added IsReadOnly dp, and corresponding VisualState 2019-04-05 19:32:48 -05:00
John Colvin 3fe0306864 AlgoEditorGridView - added IsReadOnly dp, and corresponding VisualState 2019-04-05 19:32:48 -05:00
John Colvin f76aed7671 SPARK-2208 / SPARK-2240 show '-' for params with non-uniform values (from multiple selected orders) 2019-04-05 19:32:48 -05:00
Pedro Pinto 7d7ddf53d9 Cover supports attach/detach/update. 2019-04-05 17:34:42 -04:00
Pedro Pinto 81394135f6 merged master 2019-04-05 14:23:12 -04:00
Pedro Pinto bd2c2d6aa8 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-05 14:22:25 -04:00
John Colvin 692010528a OrderParamsPanel - fix 'ShowBBO' visual state binding 2019-04-05 10:20:48 -05:00
John Colvin 8074cabba1 AlgoEditorGridView - 'Algo' label text VerticalAlignment=Center 2019-04-05 10:19:19 -05:00
Pedro Pinto 5e1e2d7812 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-05 11:12:11 -04:00
John Colvin 510e6008b6 OrderBookVM - attach_vm should not load params for the selected order (just default to iceberg) 2019-04-05 09:18:11 -05:00
Pedro Pinto c23a3ee626 Cover 2019-04-05 10:02:43 -04:00
Pedro Pinto 28cfa8ca29 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-04-05 09:10:15 -04:00
John Colvin 15a36fb40a changed QuantityMeasureTextConverter text for null/none 2019-04-04 23:24:49 -05:00
John Colvin ac11c76829 LeanOrderVM - 'trade toward behavior' and 'max chase ticks' are nullable props 2019-04-04 23:23:25 -05:00
John Colvin e9dc0f05dc AlgoEditorGridView - nullable enum and uint param-cell templates 2019-04-04 23:21:27 -05:00
John Colvin 82d8a45d87 AlgoEditorGridView - enum template ComboBox ItemTemplate is EnumDescriptionTemplate 2019-04-04 23:10:50 -05:00
John Colvin 98b1193619 IAlgoVM - implement 'load_many' (OrderBook will use to aggregate selected order params) 2019-04-04 22:32:23 -05:00
John Colvin 0f0d300edc fix CommonValueGroupMonitor (need to maintain ref counts of equivalent values) 2019-04-04 22:28:34 -05:00
John Colvin fee59c5e90 RefCountMap - overload increment/decrement methods to accept 'count' arg 2019-04-04 21:56:21 -05:00
John Colvin 0142b24884 RefCountMap - actually pass the key-comparer to the dictionary 2019-04-04 21:52:15 -05:00
John Colvin 3936783d74 OrderBookVM/View - refactor modify/attach 'algos' members to modify/attach 'orders' 2019-04-04 18:16:24 -05:00
John Colvin 4e3bdad812 QuantityMeasureControl - change QuantityMeasure prop to nullable QuantityMeasureImmutable 2019-04-04 18:16:23 -05:00
Taha Afzal 6c7c84edd7 Merge branch 'master' into queue_holder_order 2019-04-04 17:44:34 -04:00
Taha Afzal 1a36af2922 removed QHOOrderManager::SubOrder 2019-04-04 17:17:56 -04:00
Jake Norbut 5b6a7844e3 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-2207 2019-04-04 15:40:59 -05:00
John Colvin 3492bcdbd5 accidentally overwrote fix for grid-settings-manager crash in miniob (re-applying) 2019-04-04 15:37:38 -05:00
Pedro Pinto 96f1066a56 Cover. 2019-04-04 16:18:56 -04:00
Jake Norbut 1d2682224f fixing merge with main attach/detach branch 2019-04-04 14:52:23 -05:00
Jake Norbut cc4a9511b5 Merged SPARK-2237 into SPARK-2207 2019-04-04 14:45:57 -05:00
Jake Norbut 5e245dff8c OrderBookRowCollection now uses the Tree overrides that provide children, for iteration over said children for refreshing of attach/detach 2019-04-04 14:43:08 -05:00
Jake Norbut 6bb9374103 adding get_parent method 2019-04-04 14:41:49 -05:00
Jake Norbut fb5466d6e6 removing use of can_detach from row_vm.update 2019-04-04 14:40:13 -05:00
Jake Norbut f57013d9b5 refining is_attach/detachable; adding refresh attach/detach 2019-04-04 14:39:33 -05:00
Jake Norbut d25a5e3e3c OBVM can now lookup a row's parent 2019-04-04 14:35:18 -05:00
Jake Norbut 67f64de7fd refining detach_selected_algos to use is_detachable rather than can_detach 2019-04-04 14:30:28 -05:00
Taha Afzal a3a789568c better qty management 2019-04-04 15:28:47 -04:00
Jake Norbut 120d2bbee5 binding attach_button visibility to our new property 2019-04-04 14:28:02 -05:00
Jake Norbut 4a5dc21427 adding Insert and TryReplace overloads that return the associated child collection as an out parameter 2019-04-04 14:20:14 -05:00
Michael Harding c1fe0b3191 SPARK-2066: Create book and common setup 2019-04-04 11:56:05 -04:00
John Colvin d00d92dac9 SPARK-2208 / SPARK-2238 remove Account and TimeInForce controls since they cannot be modified 2019-04-04 10:48:59 -05:00
Michael Harding a071b974c8 Merge branch 'master' into system_tests 2019-04-04 10:23:50 -04:00
Jake Norbut ba7261e062 Merged SPARK-2236 into SPARK-2207 2019-04-04 07:53:38 -05:00
Jake Norbut 786f4253b9 adding GetParent unit test 2019-04-04 07:50:52 -05:00
Jake Norbut aade53eaeb Adding GetParent to SortedTree 2019-04-04 07:50:26 -05:00
John Colvin 25a1d86eec OrderBookVM - right panel automatically shows order params if everything
is hidden when opened
2019-04-03 23:44:49 -05:00
John Colvin b4d36c8104 OrderBook - mini ladder auto scroll behavior 2019-04-03 23:38:32 -05:00
John Colvin 7507595bb9 minor style changes to OrderParamsPanel and AlgoEditorGridView 2019-04-03 22:27:57 -05:00
John Colvin a47e34a5b6 SPARK-2208 / SPARK-2235 hide column headers in algo editor grid 2019-04-03 20:48:56 -05:00
John Colvin 0e52613e47 OrderBookRowVM - add a couple more columns to the 'invalidate/dirty' mask 2019-04-03 20:39:44 -05:00
John Colvin cd4c3ae99f OrderBookVM - refresh algo-param-rows when user changes selecttion to a row with the same algo structure 2019-04-03 20:39:44 -05:00
John Colvin 8cd8b85022 made CommonValueGroupMonitor (will be used by OrderBookVM) 2019-04-03 20:39:44 -05:00
John Colvin a26b44b2cc SetMap implements IReadOnlyDictionary (was only a few methods short) 2019-04-03 20:19:57 -05:00
John Colvin 11652ac021 SPARK-2209 / SPARK-2231 Remove all controls in the top of panel except for price and quantity 2019-04-03 18:50:47 -05:00
John Colvin 861ea5b426 TickUpDown - changed ControlTemplate root to a Border (was a Grid, which caused alignment issues in parent layouts) 2019-04-03 18:50:47 -05:00
John Colvin 1b731a852a OrderBook - added current row description tooltip/hint text in right panel 2019-04-03 14:14:46 -05:00
John Colvin 730cf023aa OrderBookView.xaml.cs - remove unused IValueConverter 2019-04-03 13:53:45 -05:00
John Colvin 7e88ffa5bf SPARK-2208 / SPARK-2229 don't indent param rows 2019-04-03 13:46:42 -05:00
John Colvin 1885cef7f1 AlgoEditorGridView.xaml.cs - remove unused dp 2019-04-03 13:46:42 -05:00
John Colvin 162ae01605 OrderBook - in right panel, convert 'show blah' checkboxes to a toolbar-style menu 2019-04-03 13:46:42 -05:00
John Colvin 52e0c9c7b6 OrderBookVM - rename a couple private fields 2019-04-03 13:46:42 -05:00
John Colvin 2b7135d421 OrderBook - apply initial min height for right panel controls (order params, ladder, fills) 2019-04-03 13:46:42 -05:00
John Colvin f60435eb32 SPARK-2209 / SPARK-2212 do not allow change of structure in attach params (hide child nodes) 2019-04-03 13:46:42 -05:00
John Colvin c0155047a8 made DoubleSubtractionConverter (IValueConverter) and DoubleDynamicSubtractionConverter (IMultiValueConverter) 2019-04-03 13:46:42 -05:00
John Colvin fe50e40524 rename IntGreaterThanToBoolConverter -> IntGreaterThanConverter 2019-04-03 13:46:42 -05:00
John Colvin 198fd145e7 IntSubtractionConverter - ValueConversion attribute specified wrong types 2019-04-03 13:42:33 -05:00
John Colvin 045e0bc421 LadderSettings - default row height was clipping badges 2019-04-03 13:42:33 -05:00
John Colvin 4b8d66de97 OrderBookView - 'Modify Orders' button tooltip 2019-04-03 13:42:33 -05:00
John Colvin 45a4c966de OrderBook - touch up 'current row description' atop the right panel 2019-04-03 13:42:32 -05:00
John Colvin d18194c64f OrderBookGridView.ColumnKeys - use bit/flag values 2019-04-03 13:42:32 -05:00
John Colvin b535a855a5 OrderBook - show tooltips when mini ladder is inactive due to non-singular security selection 2019-04-03 13:42:32 -05:00
John Colvin 9a1e6c92f0 CommonValueMonitor - added 'plurality_changed' event, corresponding 'SubscribePluralityThrottled()' extension 2019-04-03 13:42:32 -05:00
John Colvin 5852827b98 OrderBook mini ladder - set 'CanUserSendOrders' to false 2019-04-03 13:42:32 -05:00
John Colvin 912ddf3a2a Ladder/MiniLadder - added 'CanUserSendOrders' dp, when false a 'No' cursor is displayed over bid/ask columns 2019-04-03 13:42:32 -05:00
John Colvin fa840eb0ac PopupAdorner - implement ClipToBounds behavior 2019-04-03 13:42:32 -05:00
John Colvin e368468ffa OrderBook - add 'MiniLadderView' to right panel 2019-04-03 13:42:32 -05:00
John Colvin 3ef87df0bc LadderGridHost - clear visuals when DataContext is null 2019-04-03 13:42:32 -05:00
John Colvin 036f53c849 create 'MiniLadderView' (will be added to OrderBook's right panel) 2019-04-03 13:42:32 -05:00
Jake Norbut d0f35657ca Merged SPARK-2230 into SPARK-2207 2019-04-03 12:56:25 -05:00
Jake Norbut 1e55443415 hiding detach button appropriately 2019-04-03 12:54:59 -05:00
Jake Norbut db31a4201c adding is_detachable property to row vm 2019-04-03 12:54:20 -05:00
Jake Norbut 22665958ca narrowing can_detach criteria 2019-04-03 12:53:50 -05:00
Pedro Pinto cdd3afd1ee Code review commit. 2019-04-02 16:47:48 -04:00
Pedro Pinto 992a023bde Merged with master 2019-04-02 16:38:54 -04:00
Pedro Pinto eb053ee99d Cleaned up algos. 2019-04-02 16:36:24 -04:00
Pedro Pinto 55ee293f96 Ice clang build fixes. 2019-04-02 16:18:29 -04:00
Jake Norbut fd3e98bca7 Merged SPARK-2226 into SPARK-2207 2019-04-02 13:51:36 -05:00
Jake Norbut 449731fb35 switch attach/detach buttons in mini_ob to same as those use in ob 2019-04-02 13:49:43 -05:00
Pedro Pinto f0199a0bd4 Lean order seems complete. 2019-04-02 14:08:18 -04:00
Pedro Pinto 1f0b71353d Merged 2019-04-02 13:15:05 -04:00
Pedro Pinto 1cfdd5e86f Cleaned up lean order 2019-04-02 13:13:57 -04:00
Jake Norbut f9ea4c23d8 Merged SPARK-2210 into SPARK-2207 2019-04-02 11:27:03 -05:00
Jake Norbut fb024ea3c2 changing attach ui from being in a ContextMenu to a Popup b/c of focus interaction issues w/ the new attach ui 2019-04-02 11:07:42 -05:00
Jake Norbut d02e9cf0f9 removing ContextMenu customization since we're switching to a standard Popup 2019-04-02 11:05:43 -05:00
John Colvin f09607caaf Merge remote-tracking branch 'origin/master' into SPARK-1944-attach/detach/update 2019-04-02 09:32:24 -05:00
Pedro Pinto 96ac0f30ca Merged 2019-04-02 08:50:27 -04:00
John Colvin 3b18791e90 SPARK-2209 / SPARK-2211 move attach ui to popup 2019-04-01 11:04:11 -05:00
John Colvin c57bd6a1a9 change OrderParamsPanel ControlTemplate root to Border (enables Padding/BorderThickness etc.) 2019-04-01 11:04:11 -05:00
John Colvin 1fd45a39ac PopupAdorner - add 'OpenRelativeToMouse' property/functionality (in adv. of AttachOrders popup) 2019-04-01 11:04:11 -05:00
John Colvin 60fdc57490 OrderBook - rename 'algo_editor_is_visible' to 'show_right_panel' 2019-04-01 11:04:11 -05:00
John Colvin de73443303 move LadderWindowFactory to Ladder dir 2019-04-01 11:04:11 -05:00
Taha Afzal 9c4e1b5250 fixing build 2019-04-01 09:48:01 -04:00
John Colvin a37b514af7 finally move Ladder dir to Modules/Trading/ 2019-03-31 10:44:56 -05:00
Pedro Pinto 19f54e23f4 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-03-29 17:54:38 -04:00
Jake Norbut a2c11f2572 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-03-29 15:13:55 -05:00
Jake Norbut 1a491351a8 fix size preset binding 2019-03-29 15:13:48 -05:00
John Colvin 5d28d619f9 OrderParamsPanel - remove noisy tab stops 2019-03-29 15:11:21 -05:00
John Colvin 15915ce7b2 SparkWindowView.xaml - unify spacing/tab size 2019-03-29 14:08:30 -05:00
John Colvin 1b53e5833e consolidate common SparkCommand keygestures into static class 'SparkCommands.KeyGestures' 2019-03-29 14:08:30 -05:00
John Colvin ef32988e36 OrderBookVM - in 'row_selection_changed(),' defer updates from common-vals monitor until all changes have been processed 2019-03-29 14:08:30 -05:00
John Colvin 4840d694ce CommonValueMonitor - add method to defer updates (useful for bulk changes) 2019-03-29 14:08:30 -05:00
John Colvin f1acc0c6b9 AlgoStructureKey - null check '_structure' member in Equals/GetHashCode impl 2019-03-29 14:08:30 -05:00
Jake Norbut a26c6127d0 Merged in SPARK-2186 (pull request #200)
SPARK-2186 - detach at child

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2019-03-29 19:06:17 +00:00
Jake Norbut 241efe6a24 perfect use case for ?. 2019-03-29 14:04:17 -05:00
John Colvin cb5e0503f7 fix tab switch 2019-03-29 10:41:44 -05:00
Pedro Pinto 1b1fbef655 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-03-29 09:17:37 -04:00
John Colvin 65f982c5ad Keyboard shortcuts - Ladder, Ticket, Fills, and Create Synth 2019-03-28 22:48:17 -05:00
John Colvin 4957727877 AlgoEditorView - streamline tab navigation 2019-03-28 22:16:42 -05:00
John Colvin 0366082e8e fix auto-abbreviations for algo types 2019-03-28 16:12:05 -05:00
Pedro Pinto 5139e50cf2 Made some progress on lean order with attach/detach/update. 2019-03-28 16:51:20 -04:00
John Colvin 459255f20f temp fix for infinite-loop during login (disabled auto-abbreviations for algo types) 2019-03-28 15:51:14 -05:00
John Colvin 5eb890844d added config to disable 'FixNestedScrolling' behavior (in case there is some unforseen problem, as this affects every ScrollViewer in our App) 2019-03-28 13:55:27 -05:00
John Colvin 31d2adade7 AlgoEditorGridView - in DataGrid, set VirtualizingPanel.ScrollUnit='Pixel' so it scrolls smoothely when spreader order strategy params are expanded 2019-03-28 13:45:40 -05:00
John Colvin c22d1eeeee generic.xaml - add ScrollViewer default style with ScrollViewerCorrector.FixNestedScrolling='True' 2019-03-28 13:45:31 -05:00
John Colvin 6ee4aea794 made attached dp 'ScrollViewerCorrector.FixNestedScrolling' to prevent nested ScrollViewers from eating MouseWheel events (effectively blocking parents from scrolling) 2019-03-28 13:45:11 -05:00
John Colvin eef792d148 made ThreadStaticActivator util (a static generic class that creates ThreadStatic instances) 2019-03-28 13:38:20 -05:00
John Colvin 247a41de23 AlgoEditorGridView.xaml - remove commented-out garbage xaml 2019-03-28 13:38:20 -05:00
Jake Norbut 1892fcec69 Adding DetachColumn to mini ob 2019-03-28 12:26:34 -05:00
Jake Norbut 1d1423494b adding new DetachColumn 2019-03-28 12:26:34 -05:00
Jake Norbut e1c1ce78eb adding Detach as possible enum value 2019-03-28 12:26:33 -05:00
Jake Norbut e4880a6715 Adding detach_command for easy binding 2019-03-28 12:26:33 -05:00
Jake Norbut 8716fff482 Adding Detach to DefaultColumnHeaders 2019-03-28 12:26:33 -05:00
John Colvin 6c9e05b9eb SPARK-2183 Algo update (Tree vs Sequential layout) 2019-03-28 11:52:32 -05:00
John Colvin c0a69f26a2 made TimeConverter in advace of AlgoEditorGridView (will be used by various time-related param DataTemplates) 2019-03-28 11:52:32 -05:00
John Colvin 544ddff37f made EnumItemsSourceConverter in advance of AlgoEditorGridView (will be used by enum param DataTemplate) 2019-03-28 11:52:32 -05:00
John Colvin 81ed77d4c9 made ObservableAggregateCollection.Aggregator more robust by changing impl from IList<ObservableCollection<T>> to IList<(IReadOnlyCollection<T>,INotifyCollectionChanged)> 2019-03-28 11:52:32 -05:00
John Colvin c4f29bd2cf made 'block_reentrancy()' extension method for IProperty<T> (blocks reentrancy in value setter) 2019-03-28 11:52:32 -05:00
John Colvin ee498f1ad0 VisualDictionary - fix typeo in index setter, remove 'ICollection' casts in CopyTo/Remove(KeyValuePair) methods 2019-03-28 11:52:32 -05:00
John Colvin a899a225a4 made IReadOnlyObservableCollection<T> covariant 2019-03-28 11:52:31 -05:00
John Colvin 75d7ee1827 abbreviate algo types - hard coded abbrevs for known types, logical abbrevs for dynamic types 2019-03-28 11:52:31 -05:00
John Colvin 8d02819060 TabItem should not be Focusable, nor a TabStop 2019-03-28 11:52:31 -05:00
John Colvin 8eeca4a9ba GridLengthConverter - add 'Multiplier' property in advance of new 'AlgoEditorGridView' (need to indent content by n pixels per hierarchical level) 2019-03-28 11:52:31 -05:00
John Colvin 45cfcd2c3d made 'QuantityMeasureTextConverter' in advance of new 'AlgoEditorGridView' (will need display text for QuantityMeasure param cells) 2019-03-28 11:52:31 -05:00
John Colvin 20ddc43a2e OrderTypeItem - internal 'Badge' control should not be Focusable, nor a TabStop 2019-03-28 11:52:31 -05:00
John Colvin 1325d388ca DataGridExtensions - made TryGetRowView/TryGetCurrentRowView extension methods 2019-03-28 11:52:31 -05:00
John Colvin 085f309e1f ViewModelBase - Property/UserProperty implement new interfaces IProperty/IUserProperty respectively 2019-03-28 11:52:31 -05:00
John Colvin fa95ac6bfd ProtoHelper - made generic 'get_oneof_case<TOneofEnum>' method (returns oneof-case of an IMessage obj) 2019-03-28 11:52:31 -05:00
John Colvin b09ad7748f Oneof - overloaded 'Match()' method to accept Action<T>'s, added static 'OneOf' factory methods 2019-03-28 11:52:31 -05:00
John Colvin 67ba1499f0 WPFExtensions - made 'try_get_ancestor_of_type<T>()' and 'try_get_descendent_of_type<T>()' extension methods 2019-03-28 11:52:31 -05:00
John Colvin f277ea56dc made read-only ObservableCollection wrapper 2019-03-28 11:52:31 -05:00
John Colvin d112880c58 DislpayNameHelper - 'word tree' logical abbreviations 2019-03-28 11:52:31 -05:00
John Colvin e691a26eee OrderBookVM - 'fills_slave_predicate_src' accesses selected_rows on dispatcher thread 2019-03-28 11:52:31 -05:00
John Colvin 184ceaa649 DIYGrid.HighPerformanceRow - unsubscribe to grid.ColumnLayoutChanged on Unloaded 2019-03-28 11:52:31 -05:00
Jake Norbut 3c202e1597 Merged SPARK-2180 into SPARK-1944-attach/detach/update 2019-03-28 09:55:11 -05:00
Jake Norbut f407e52703 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-2180 2019-03-28 09:51:12 -05:00
Jake Norbut 34e22606be adding attach_vm binding path to constructor 2019-03-28 09:38:09 -05:00
Jake Norbut fee24542ee removing unused xmlns 2019-03-28 09:34:11 -05:00
Jake Norbut 4900a73eeb simplifying Attach columm insertion 2019-03-28 09:33:19 -05:00
Jake Norbut 33552c0720 changing class access modifier to Internal 2019-03-28 09:28:48 -05:00
Jake Norbut 401c657c0b Adding new AttachColumn to OrderBookColumns 2019-03-28 08:46:25 -05:00
Jake Norbut ea899b57a0 adding ContextMenu control template and styles to bend it to our will 2019-03-28 08:45:45 -05:00
Jake Norbut 63ef6b6457 exposing a new begin_attach_command for easy databinding 2019-03-28 08:43:57 -05:00
Jake Norbut a869598d5e Adding Attach header text 2019-03-28 08:36:16 -05:00
Jake Norbut 082e68d975 Inserting new AttachColumn into the column collection 2019-03-28 08:35:20 -05:00
Jake Norbut 6a70dd8357 adding Attach as an Enum value to Columns 2019-03-28 08:33:49 -05:00
Jake Norbut 3b335a8e73 adding Button behavior to automatically drop down its ContextMenu when clicked 2019-03-28 07:25:56 -05:00
Pedro Pinto f5354776f1 Top order support attach/detach and full update. 2019-03-27 11:06:04 -04:00
Pedro Pinto 9413eb2206 Removed incorrect assertion. 2019-03-26 09:20:31 -04:00
Pedro Pinto e986f77e72 Merged 2019-03-25 17:39:47 -04:00
Pedro Pinto 75095ce874 Moved Join to BaseAlgoOrder 2019-03-25 17:39:02 -04:00
Michael Harding dd7db5661b Utility for tests to verify the system is ready 2019-03-25 12:21:10 -04:00
Michael Harding 4b8d725528 Test scripts start sparkd. spreader sets config 2019-03-25 12:21:10 -04:00
Jake Norbut 20b7b8d01a Modifying OBVM to find the brach root row (for detach API call) for detaching at a child) 2019-03-22 11:27:21 -05:00
Jake Norbut 1e83af41d7 Adding utility function that makes use of the the tree's GetRootNode to grab the branch's root OrderbookRowVM 2019-03-22 11:25:19 -05:00
Jake Norbut e852d83139 Orders will now be detached at the child, so reverse this logic 2019-03-22 11:22:52 -05:00
Kevin Darby 89b5610aa2 make it so time slice doesn't crash, 2019-03-22 12:18:36 -04:00
Pedro Pinto f721098ae0 BaseBook order class is code complete. 2019-03-22 11:16:54 -04:00
Kevin Darby bfff6454ef Merged in twap (pull request #197)
just trying to get a gui for timeslice
2019-03-22 14:44:07 +00:00
John Colvin b90e5e4274 TickUpDownDouble - remove and sort usings 2019-03-21 12:02:00 -05:00
John Colvin bacd4558e1 TickUpDownDouble - don't require DataContext to implement IWillLetYouKnowWhenIDispose 2019-03-21 12:00:55 -05:00
John Colvin 008e698766 TickUpDownDouble allows user to enter '.' 2019-03-21 11:48:12 -05:00
Pedro Pinto ed7f066f20 Porting book algos to attach/detach/update API 2019-03-21 09:11:58 -04:00
Jake Norbut db1b2a63af the start of a lean test 2019-03-21 08:05:33 -05:00
Kevin Darby d7c81a08ef might be able to implement this tomorrow 2019-03-20 23:07:46 -04:00
John Colvin aadb6c86f3 DynamicOrderParamVM supports 'Enumerated' param type 2019-03-20 14:18:00 -05:00
Jake Norbut 6006a328fe Merge branch 'master' of bitbucket.org:bluetradesys/spark into system_tests 2019-03-20 08:45:18 -05:00
Kevin Darby 5653a66a31 just trying to get a gui for timeslice 2019-03-19 22:48:15 -04:00
John Colvin 99b9c51ad6 OrderTypeVMBase - initialize child_algos.is_expanded with this.is_expanded 2019-03-18 14:44:07 -05:00
John Colvin fe92389eab OrderBook - hide fills slave grid in 'attaching-orders' visual state 2019-03-18 14:20:37 -05:00
John Colvin 1ca5b217fc OrderBook - assign algo-template-key to OrderDefinition when sending attached orders (if template is selected) 2019-03-18 14:07:01 -05:00
Taha Afzal db20433d44 helper functions to QHOOrderManager 2019-03-18 13:58:42 -04:00
John Colvin 4e4ab52032 OrderBook - combine modify/attach visual state groups into 1 group of 'right-panel-states' 2019-03-18 12:20:58 -05:00
John Colvin 4cc513f1d3 RXOperableCollection - assign '_comparer' in constructor 2019-03-17 15:30:43 -05:00
John Colvin 7904065333 SPARK-2159 keep the attached order params and normal order params window the same size and animate the transition 2019-03-16 18:17:10 -05:00
John Colvin 961e06c31f SPARK-2153 Modify Algos -> Modify Orders 2019-03-16 12:23:14 -05:00
John Colvin 4132bdaba4 SPARK-2155 Send Attached Orders -> Attach To Orders 2019-03-16 12:20:05 -05:00
John Colvin 1c75f5f530 SPARK-2156 Dismiss attachment on click of grid 2019-03-16 12:17:46 -05:00
John Colvin d8e7dd7c3b OrderBooView - change element naming-scheme in xaml 2019-03-16 11:30:16 -05:00
John Colvin d0377475bb SPARK-2157 Detach Selected Orders -> Detach And Cancel Selected Orders 2019-03-16 11:20:38 -05:00
John Colvin 7826ff17c0 finish merge started by pedro (resolve conflicts in SpreaderOrderVM.cs) 2019-03-16 11:06:34 -05:00
John Colvin a46982333b SPARK-2158 Algo Studio Orders are Attachable 2019-03-16 11:06:34 -05:00
John Colvin e2cebaa5c8 add 'AlgoData' member to OrderDataHelper.Defauls (use in OrderBookVM) 2019-03-15 19:53:42 -05:00
John Colvin d03987591c OrderBook - order params ui populates 'account' with common selected value 2019-03-15 19:53:42 -05:00
John Colvin e909c37f88 OrderBookVM - refactor SelectedAlgosMon -> SelectedOrderParamsMon 2019-03-15 19:53:42 -05:00
Pedro Pinto e998edc20f Merged from master, note that SpreaderOrderVM.cs was not merged yet, so this client build will fail 2019-03-15 14:41:02 -04:00
Jake Norbut 5cb48fb517 Adding qty threshold test 2019-03-15 13:28:30 -05:00
Jake Norbut f2f8c258f9 run all tests within a test class 2019-03-15 13:22:37 -05:00
Pedro Pinto 1c1be2b334 Idempotent cancels for Join,Iceberg and Native orders. 2019-03-15 11:20:47 -04:00
John Colvin 5d89257643 OrderBook - make attach-order panel pop out less 2019-03-15 00:57:09 -05:00
John Colvin 897e134714 C# style cop changes 2019-03-14 21:15:29 -05:00
John Colvin e6cb97028b OrderBook - subscribe_common_sec_md notifies observer on subscribe 2019-03-14 21:13:31 -05:00
John Colvin 6c0dc80d43 OrderBook - sync attached-order-params with common values from selected orders 2019-03-14 21:13:31 -05:00
John Colvin ce3a4562d9 OrderBook - attach/detach logging 2019-03-14 21:13:31 -05:00
Pedro Pinto 217f0cd4cc Merged 2019-03-14 21:18:35 -04:00
Pedro Pinto 1e649ccce6 Made NativeOrder able to process full updates. 2019-03-14 21:17:15 -04:00
John Colvin cbc69ec5fd OrderBook - don't lose right-panel-width (when hidden) during attach-op 2019-03-14 20:12:22 -05:00
John Colvin 0d1eedc34a OrderBook - wrap 'send_attached_orders_async' in try/catch 2019-03-14 20:12:22 -05:00
John Colvin 4e85bf4520 OrderBook - refactor 'property-update-throttle' code to use CommonValueMonitor 2019-03-14 20:12:22 -05:00
Pedro Pinto 5d0023a80b Fixed a bug in detach handling in Join. 2019-03-14 21:09:16 -04:00
Pedro Pinto e300de2425 Fixed a few bugs in new Join 2019-03-14 20:13:18 -04:00
Pedro Pinto 569877752e Merged 2019-03-14 19:08:48 -04:00
Pedro Pinto 4846cf7693 Join with attach/detach and full update is code complete. 2019-03-14 19:04:51 -04:00
John Colvin 16a46955f4 OrderBook - attach buttons become disabled when selected order transitions from working -> non-working 2019-03-14 16:43:16 -05:00
Jake Norbut cb8dac05d5 if linux no Dispose for now b/c seg fault 2019-03-14 16:10:58 -05:00
Jake Norbut f6eb1e4745 QuickBusClientChanel diag logging for sub / unsub 2019-03-14 15:53:12 -05:00
Jake Norbut 16e8a6e26d adding .NET Core Remote Lauch and Attach debugging options 2019-03-14 15:51:19 -05:00
John Colvin 13a6d68b60 OrderBook - change 'Modify Order' keyboard shortcut Ctrl+Alt+M -> Ctrl+Shift+M 2019-03-14 13:05:15 -05:00
John Colvin 1affd992de OrderBook - reduce attach-ui panel width 2019-03-14 12:39:27 -05:00
John Colvin d951258891 OrderBook - bottom of attach-ui was getting chopped off 2019-03-14 12:32:40 -05:00
John Colvin f5dfb0264b OrderBook - change default fills slave grid height 400 -> 138 2019-03-14 11:19:36 -05:00
Jake Norbut 9744f53d65 shell script that will perform setup work; will probably need to chmod this 2019-03-14 11:07:57 -05:00
Jake Norbut b231a66fc7 need to make this test public 2019-03-14 11:06:59 -05:00
John Colvin 3b8f5c5093 OrderBook - minor style changes to right-panel 2019-03-14 00:04:34 -05:00
John Colvin 253d213818 OrderBook - unify attach/modify order param controls/vms 2019-03-13 21:41:49 -05:00
John Colvin f89cb3d61e OrderBook - prevent 'Modify Algos' button from hiding below visible bounds 2019-03-13 17:29:35 -05:00
Jake Norbut 65b3d4fffa refactoring OrderDefinition utils 2019-03-13 16:58:00 -05:00
Jake Norbut ba1b667ae8 basic join test 2019-03-13 16:47:23 -05:00
Jake Norbut e31b05042f refactoring common services into a base class; other misc refactoring 2019-03-13 16:46:58 -05:00
John Colvin eb6573399b OrderBook - add 'Cancel' button to attach-orders-panel 2019-03-13 15:10:05 -05:00
John Colvin 8ba8ce76ce CancelButton Style - add 'Cancel (Esc)' tooltip 2019-03-13 15:07:33 -05:00
John Colvin e65b8e9d4d OrderBook - disable 'send attached orders' button when selection is empty or has non-working orders 2019-03-13 14:27:33 -05:00
John Colvin 2fc379fbef OrderBookView - ensure fills slave grid min height in xaml (64) 2019-03-13 13:52:10 -05:00
John Colvin fb96a75a8e OrderBook - change attach-menu-item text from 'Attach Selected Orders' -> 'Attach To Selected Orders' 2019-03-13 13:46:03 -05:00
John Colvin 240d15af47 OrderBookView - remove attach-instructions text xaml 2019-03-13 13:46:03 -05:00
John Colvin 2530e43797 OrderBook - hide attach-instruction text, animate right panel 2019-03-13 13:09:28 -05:00
John Colvin b05d85d6c3 OrderBook - change 'Attach' prompt style 2019-03-13 12:22:04 -05:00
John Colvin 52f2e77310 OrderBook - change Attach/Detach icons 2019-03-13 12:16:57 -05:00
John Colvin bd00c6de3e SPARK-2093 Attach/Detach ui 2019-03-13 11:48:08 -05:00
Jake Norbut 84e443ee4b copying *.sh scripts for testing on linux 2019-03-13 11:40:16 -05:00
John Colvin 87aea4812f OrderBook algo editor would stop loading non-spreader algos once a spreader order was selected 2019-03-11 21:27:22 -05:00
John Colvin 21ae3b14c8 OrderBook attach/detach ui - use 'Pen' cursor when attachment is in progress 2019-03-11 20:20:29 -05:00
John Colvin 708ae185f1 rename OrderBookAttachTargetAdorner -> OrderBookAttachmentTargetAdorner (matches file) 2019-03-11 20:10:50 -05:00
John Colvin 5c6dbd7bd1 OrderBook - hook up attach/detach api calls 2019-03-11 20:07:29 -05:00
John Colvin f19a280b7f DIYGridRowHighlighterAdorner derives from new generic Adorner<T> class 2019-03-11 15:39:57 -05:00
Jake Norbut 3821dfb8ff Merge branch 'master' of bitbucket.org:bluetradesys/spark into system_tests 2019-03-11 14:45:10 -05:00
John Colvin 47cf9e2fe6 OrderBook - add 'Attached Target Order Key' column 2019-03-11 14:14:24 -05:00
Jake Norbut 01a8502b20 refactoring to resuse user and account between different tests 2019-03-11 14:04:40 -05:00
John Colvin df75518389 OrderBook - add parent-key-regex property to FilterStates proto (guess we accidentally omitted it) 2019-03-11 14:01:20 -05:00
John Colvin 90c371c72f OrderBookColumns - remove 'sort_member_path' (not needed bc orderbook handles sorting events internally) 2019-03-11 13:48:46 -05:00
Jake Norbut edfa079326 ib avg filled test 2019-03-11 13:46:00 -05:00
John Colvin 9609f7e6cf OrderBook FilterStates proto - override obj Equals/GetHashCode 2019-03-11 13:35:26 -05:00
Jake Norbut 316a617c65 more refactoring 2019-03-11 13:08:16 -05:00
John Colvin 30c83b7c72 OrderBookGridView - stack 'ColumnKeys' enum fields vertically (easier to read) 2019-03-11 13:02:17 -05:00
John Colvin 382d8b468c ObservableNode - assign child_node.parent when adding child 2019-03-11 12:58:43 -05:00
John Colvin cbe6d07a86 OrderBookStartupData - set default values for algo_editor_width/height/is_visible 2019-03-11 11:03:51 -05:00
John Colvin 6d67700ee6 OrderBookView - reduce 'attach-algo-instructions' text fadeout duration from 1 sec to 0.5 2019-03-11 10:49:12 -05:00
Jake Norbut 0e92a12e65 refactoring/reorg 2019-03-11 10:08:13 -05:00
John Colvin e9cf1db184 OrderBookView - Fills/AlgoEditor GridSplitter needs min height to prevent it from vanishing when fills grid is collapsed 2019-03-10 23:37:28 -05:00
John Colvin e4c98d947e Merge remote-tracking branch 'origin/SPARK-2079-trigger-cleanup' into SPARK-1944-attach/detach/update 2019-03-10 23:10:59 -05:00
John Colvin ab9de97d6e remove file that was accidentally checked in 2019-03-10 22:59:55 -05:00
John Colvin 4e425a3380 SPARK-2093 Attach/Detach ui 2019-03-10 22:57:52 -05:00
John Colvin ac67c84ce7 OrderBookWindow - remove individual SparkCommand fields (reduce scope to be local to thread_init()) 2019-03-10 15:04:03 -05:00
John Colvin 79120ea7a5 OrderBook 'fills slave grid' - init predicate hides all rows 2019-03-10 14:45:43 -05:00
John Colvin a85a7f624a refactor [TableUpdate]LeftJoin\Filter extensions to use AggregateLatest (share common code) 2019-03-10 14:27:39 -05:00
John Colvin 19e4da9936 StringBuilderEx - remove implicit 'StringBuilder' cast operators (seems sketch with reference types) 2019-03-09 22:09:27 -06:00
John Colvin 1494c37cc7 OrderBook - add 'fills-slave-grid' above the algo editor (shows fills for current row-selection) 2019-03-09 22:09:27 -06:00
John Colvin af1fbaf3ed implement TableUpdate filter with IObservable predicate 2019-03-09 22:09:27 -06:00
John Colvin 82a93e39f8 DIYGrid - use max 'visible' column's font size to determine min row height 2019-03-09 22:09:27 -06:00
John Colvin 36684f8ee5 ObservableFilterCollection - in 'reset(),' clear the target collection if source is empty 2019-03-09 22:09:27 -06:00
John Colvin c8cd2ae1ad fix 'sync_with_never_duplicate()' - should remove items from 'dest' that aren't in 'src' 2019-03-09 22:09:27 -06:00
John Colvin 93afb5b602 introduce FillsVMContainer 2019-03-09 22:09:27 -06:00
John Colvin 429b0bb8f8 ObservableAggregateCollection.Aggregator implements IList (not new functionality, just refactored some methods) 2019-03-09 22:09:27 -06:00
Pedro Pinto 80ce5447e5 Validate that IcebergOrders must have child definitions. 2019-03-08 17:56:27 -05:00
Pedro Pinto 609b0dceb1 Native orders now reflect new FIX tags when attached to. 2019-03-08 17:49:51 -05:00
Taha Afzal f9d276a908 SPARK 2090 2019-03-08 17:45:46 -05:00
Jake Norbut 5f4eb58db9 refactoring to promote eventual code reuse 2019-03-08 16:26:00 -06:00
Pedro Pinto ea47042816 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-03-08 17:25:30 -05:00
Pedro Pinto 4b23e17534 Updating native order FIX tags when attached. 2019-03-08 17:25:00 -05:00
John Colvin f69301611a OrdersModel - for non-algo updates, set Update.Custom to null instead of populating with existing order's CustomDef 2019-03-08 15:52:38 -06:00
John Colvin c04ac5cd48 OrderDataHelper - fix get_metadata() 'Spreader' switch-case 2019-03-08 14:36:39 -06:00
John Colvin aa39349fc4 SPARK-2092 Orderbook update ui supports spreader algos 2019-03-08 14:36:39 -06:00
John Colvin d1c915fef3 ObservableAutoWrapper - fix typeo (change 'Reset' to 'Remove') 2019-03-08 14:14:08 -06:00
John Colvin 69bdf07423 SPARK-2089 Strange null ref exception in OrderBook row-selection changed callback during application shutdown 2019-03-08 14:14:08 -06:00
John Colvin 4115a6c790 AlgoStructureKey - structure-serialization supports DynamicOrderDefinition 2019-03-08 14:14:08 -06:00
John Colvin 8f8aae57de remove IAlgoVM.can_edit_blah props; view binds to permission flags w EnumHasFlagsConverter; view uses VisualStates 2019-03-08 14:14:08 -06:00
John Colvin 362f75c8b2 OrderBookVM - null check an event, fix initial algo editing permissions 2019-03-08 14:14:07 -06:00
Pedro Pinto 39e855e691 Merged 2019-03-08 14:08:46 -05:00
Pedro Pinto 73303ccd33 Merge branch 'fix_warmup_limit_polution' into SPARK-1944-attach/detach/update 2019-03-08 14:02:23 -05:00
Jake Norbut f7cdb1f00b refactor/cleanup of some common functions 2019-03-08 10:36:51 -06:00
Jake Norbut 0caa6b6ee0 IB test 1 2019-03-08 08:53:00 -06:00
John Colvin 6826494898 EnumHelper - don't initialize 'ThreadStatic' fields 2019-03-07 13:37:02 -06:00
Pedro Pinto 69fcfbd6ce Removed over-zealous assertion. 2019-03-07 14:28:49 -05:00
Pedro Pinto 64973e1b51 Merged fix_om_reentrancy into queue_holder_order 2019-03-07 13:29:20 -05:00
Pedro Pinto c1d62a2ff6 Fixed OrderManager reentrancy problem reported by Taha 2019-03-07 13:27:05 -05:00
Taha Afzal f4c0f24e80 reading qho queue size from ui 2019-03-06 20:02:38 -05:00
Pedro Pinto 1fa419eae2 Merge branch 'SPARK-1944-attach/detach/update' of bitbucket.org:bluetradesys/spark into SPARK-1944-attach/detach/update 2019-03-06 14:42:16 -05:00
Pedro Pinto e3a3eb6d85 Redirected Iceberg factory to create Iceberg2. 2019-03-06 14:41:57 -05:00
Pedro Pinto 18a32f9a05 White space. 2019-03-06 14:41:26 -05:00
John Colvin b7c127b2d8 EnumHasFlagsConverter - ContentProperty = 'Flags' 2019-03-06 12:59:16 -06:00
John Colvin f83f94fb07 rename [class]EnumFlagsToBooleanConverter -> EnumHasFlagsConverter (matches filename) 2019-03-06 12:55:28 -06:00
John Colvin 23f76a1c80 remove some redundant\unused code 2019-03-06 12:49:08 -06:00
John Colvin cc0f6ea448 OrderBook Algo Panel - show inactive order algos, but disable controls 2019-03-06 12:48:58 -06:00
Taha Afzal 37b211532e merging from master 2019-03-06 11:53:02 -05:00
John Colvin cbb6936553 OrderBookView - animate GridSplitter background on mouse over 2019-03-05 23:30:28 -06:00
John Colvin 633433cab9 Colors.xaml - fix line endings 2019-03-05 23:24:35 -06:00
John Colvin 3ab7ed0b71 AlgoEditorView - remove ugly ToString() text when Limit, Market etc., are selected 2019-03-05 23:10:54 -06:00
John Colvin fc1adf8c28 OrderBookWindow - include 'show/hide algo editor command' in toolbar (not just context menu) 2019-03-05 20:31:24 -06:00
John Colvin 79cf313b5b SPARK-2014 Add OB support for full updates 2019-03-05 20:18:24 -06:00
John Colvin 00834a668c ObservableDistinctCollection - actually use equality comparer arg 2019-03-05 20:18:23 -06:00
John Colvin 32c9c23d7e fix typeo in MapAggregate() extension (actually use key_comparer arg) 2019-03-05 20:18:23 -06:00
John Colvin 5ac8d2ada6 AlgoSelector - set selected_algo_changed hook in constructor (not 'load_from' method) 2019-03-05 20:18:23 -06:00
John Colvin 54eb6ceaa9 fix typeo in LazySubscription 2019-03-05 20:18:23 -06:00
John Colvin 540db91311 add config to enable\disable multi-threading for ISharedWindows 2019-03-05 20:18:05 -06:00
John Colvin 1ee2193c88 ObservableAutoWrapper.connect_item_actions - invoke action for existing items 2019-03-05 20:16:48 -06:00
John Colvin 84877af947 SPARK-2079 [pr edit]child algo 'name' overlapped expander button 2019-03-05 20:16:48 -06:00
John Colvin 388cf92a70 SPARK-2079 [pr edit]remove #define ALLOW_FIXED_PRICE_TRIGGER 2019-03-05 20:16:48 -06:00
John Colvin 840441a749 SPARK-2079 Trigger Clean up (simplify) 2019-03-05 20:16:48 -06:00
John Colvin c39a510d09 refactor\cleanup OrderTypeVMBase and impls 2019-03-05 20:16:48 -06:00
Pedro Pinto 299d46aa2c Attach/Detach passes initial tests. 2019-03-05 15:44:01 -05:00
Pedro Pinto 345f6ed9fe Added detach handling to iceberg 2019-03-04 14:44:42 -05:00
Pedro Pinto 31aa9d9bf6 Merge remote-tracking branch 'origin/master' into SPARK-1944-attach/detach/update 2019-03-04 14:30:32 -05:00
Pedro Pinto 0c063a3876 Iceberg with attach is code complete. 2019-03-04 14:13:39 -05:00
Pedro Pinto af253198d2 Iceberg that can attach. 2019-03-04 08:55:36 -05:00
Pedro Pinto cc381d605c Merged with master 2019-03-04 08:55:06 -05:00
Kevin Darby 39a1252084 added Queue Holder Length gui to spreader for Taha 2019-02-28 14:08:46 -05:00
Pedro Pinto c2f954a79d More changes to attach/detach API 2019-02-28 13:07:41 -05:00
Jake Norbut 6fb8fdc9f6 updating test timeout 2019-02-27 14:28:45 -06:00
Pedro Pinto e79a73147a Merged with master 2019-02-27 15:15:06 -05:00
Pedro Pinto 6a921ae900 Merge remote-tracking branch 'origin/master' into SPARK-1944-attach/detach/update 2019-02-27 13:48:22 -05:00
Jake Norbut b7fcd17ef0 first test; create retrieve update delete of algo definitions 2019-02-27 11:05:23 -06:00
Pedro Pinto 0f13d27efe Improvements on attach/detach API 2019-02-27 10:08:55 -05:00
Michael Harding 482ca6b484 SPARK-2065: Move order functions to OrderUtilities 2019-02-26 14:02:09 -05:00
Pedro Pinto d52347cd71 Trigger state update on new definition in OM. 2019-02-26 11:57:11 -05:00
Pedro Pinto 0ab628fb6c Allow orders to change their full definition. 2019-02-26 11:43:10 -05:00
Michael Harding 7ed3a977ab SPARK-2065: Move AdminUtilities 2019-02-26 11:18:26 -05:00
Michael Harding 59cfe3dc8d SPARK-2067: Setup bash scripts for running system tests 2019-02-26 11:07:47 -05:00
Pedro Pinto 1a8a492ef7 Attach/detach tweaks. 2019-02-26 09:56:24 -05:00
Pedro Pinto 74066c0ffe Refactored OrderServices 2019-02-25 13:55:13 -05:00
Pedro Pinto 0269d5fa86 Merged with master 2019-02-25 12:50:06 -05:00
Pedro Pinto 84a5a11a9c attach/detach code complete perhaps. 2019-02-25 12:49:23 -05:00
Pedro Pinto a01e6df7fa Merged with master 2019-02-25 09:06:55 -05:00
Pedro Pinto d4caccdbfb Build. 2019-02-22 14:50:25 -05:00
Pedro Pinto 4ede8a62ea Merged with master 2019-02-22 11:31:05 -05:00
Pedro Pinto ba11248ac9 attach/detach 2019-02-21 09:03:26 -05:00
Taha Afzal 23d817d474 _cancelled_externally is marked true only when order has not been internally cancelled or filled 2019-02-20 23:25:24 -05:00
Pedro Pinto 22cde8aa0d Merge branch 'master' into SPARK-1944-attach/detach/update 2019-02-19 17:02:02 -05:00
Pedro Pinto 3dba49e899 More attach/detach design docs. 2019-02-19 10:18:27 -05:00
Taha Afzal ff53cf8b4d QHO working from the spreader 2019-02-16 22:33:47 -05:00
Taha Afzal 74f1cab23d SpreaderUtils can initiate a limit order or a QHO dependong on the spreader settings 2019-02-15 12:57:32 -05:00
Pedro Pinto 23d7a9e1ad Improved error handling in native order. 2019-02-15 11:27:25 -05:00
Pedro Pinto 7bd9d3f4ad Merge remote-tracking branch 'origin/master' into SPARK-1944-attach/detach/update 2019-02-15 09:07:28 -05:00
Taha Afzal 87a03a3759 merging master 2019-02-14 15:52:04 -05:00
Pedro Pinto 7740f64bfd Iceberg2 is working. 2019-02-14 12:40:15 -05:00
Pedro Pinto 7ccbb69f5c Made native order ignore any bad updates/cancels after it terminates. 2019-02-14 12:37:19 -05:00
Pedro Pinto b4ba527c12 Generalized dynamic order mechanism to support any order. 2019-02-14 12:34:08 -05:00
Taha Afzal dc5d4cf50a 1 for n of queue holder orders mean 2 total orders 2019-02-13 18:08:28 -05:00
Taha Afzal 03b4b2ec29 cleaning up across state handlers for when to cancel 2019-02-13 17:43:36 -05:00
Taha Afzal 4b45ece2aa added more error states (for child orders getting cancelled externally, child order errors as well as qho external cancels) 2019-02-13 11:54:13 -05:00
Taha Afzal 9b9192c7d4 simplified initialization 2019-02-12 17:33:14 -05:00
Pedro Pinto a62a41a0aa IB2 code complete. 2019-02-12 17:13:53 -05:00
Taha Afzal f09f32b1a8 qty changes 2019-02-12 15:49:46 -05:00
Taha Afzal f70003c821 simplified price modifications 2019-02-12 13:21:34 -05:00
Pedro Pinto 904023c5fa Added a few comments. 2019-02-12 09:50:32 -05:00
Pedro Pinto a9fc3d43a0 Fixed merge. 2019-02-12 09:36:57 -05:00
Taha Afzal b7a10f2712 on_transition was not getting called 2019-02-11 22:45:40 -05:00
Taha Afzal f37dc1eab2 buy qho orders working 2019-02-11 18:35:43 -05:00
Pedro Pinto 4febc07aa0 Merged with master 2019-02-11 18:35:29 -05:00
Pedro Pinto 198e76f722 Renamed crack detach. 2019-02-11 17:52:26 -05:00
Pedro Pinto 836301e75f Iceberg with update 2019-02-11 16:50:35 -05:00
Pedro Pinto ad37a652c5 Refactor 2019-02-11 16:50:35 -05:00
Pedro Pinto 5bed54c953 More SDK work. 2019-02-11 16:50:35 -05:00
Pedro Pinto 5675c4e117 Renamed crack to detach. 2019-02-11 16:50:35 -05:00
Jake Norbut 16f1e568a5 release notes - words 2019-02-11 16:50:35 -05:00
Jake Norbut e375274cbe Updating release notes describing Floating Order Book 2019-02-11 16:50:35 -05:00
Pedro Pinto cf3ef9323a dev_process.md edited online with Bitbucket 2019-02-11 16:50:35 -05:00
Pedro Pinto 7e2e2bf1b0 Dev doc 2019-02-11 16:50:35 -05:00
Kevin Darby 781c9dcc86 moved that using std::vector out of header 2019-02-11 16:50:35 -05:00
Kevin Darby a8a922160d move externals pointer to proto36, small changes in spark to support that 2019-02-11 16:50:35 -05:00
Pedro Pinto 367bd0e3b9 Added release notes 2019-02-11 16:50:35 -05:00
Eric Harding 4215d96ba9 Wait to give obvm obvm a chance to load rows and not flash 2019-02-11 16:50:35 -05:00
Jake Norbut 50d18eb10d PR edits - Style cop 2019-02-11 16:50:35 -05:00
Jake Norbut b65fedc781 PR edit - simplify LINQ expressions when using Where and FirstOrDefault 2019-02-11 16:50:35 -05:00
Jake Norbut 88c8663d29 PR - naming 2019-02-11 16:50:35 -05:00
Jake Norbut 8c7dd2460d PR edit - changing orderbook_vm property to method to better reflect the nature of the beast 2019-02-11 16:50:35 -05:00
Jake Norbut f20ee21ce7 PR edits - Simplifying XAML since UserControl is a ContentControl 2019-02-11 16:50:35 -05:00
Jake Norbut a61e21bd87 PR edits - Hashset for the win over List for Contains 2019-02-11 16:50:35 -05:00
Jake Norbut 39014696f3 PR edit; Set is your friend 2019-02-11 16:50:35 -05:00
Jake Norbut af278d2d87 PR edits - devolving qty/dirty and price/dirty 2019-02-11 16:50:35 -05:00
Jake Norbut 636e170f44 PR edits 2019-02-11 16:50:35 -05:00
Jake Norbut 3d4fe8f832 PR edit; columns setter is now private 2019-02-11 16:50:35 -05:00
Pedro Pinto 94285918bf Trying to fix the linux/win war on CMAkeDeps.cmake 2019-02-11 16:50:35 -05:00
Jake Norbut 2ba7f949cc MinWidth so that the vertical scrollbar does not cover up the content in the last column 2019-02-11 16:50:35 -05:00
Jake Norbut fc82ccf221 enforce max height on popop size 2019-02-11 16:50:35 -05:00
Jake Norbut e71b1518f0 move edit price immediately after edit qty 2019-02-11 16:50:35 -05:00
Jake Norbut 7738da7d16 remove ladder tooltip and backing property 2019-02-11 16:50:35 -05:00
Jake Norbut c91eedcca0 minor refactoring, some notes after talking w/ john 2019-02-11 16:50:35 -05:00
Jake Norbut 4f5f4c61f3 inline price ticker 2019-02-11 16:50:33 -05:00
Jake Norbut 2f31fb6284 qty ticker layout changes 2019-02-11 16:48:53 -05:00
Jake Norbut d18caa7ba1 auto close popup when no orders remain 2019-02-11 16:48:53 -05:00
Jake Norbut ac88586c8f darken popup border 2019-02-11 16:48:53 -05:00
Eric Harding 968dfbac2a Need spark_test.dll for system tests 2019-02-11 16:48:53 -05:00
Eric Harding 0501418da6 provide_command_line for system_tests -configserver 2019-02-11 16:48:53 -05:00
Eric Harding 4efd853c96 Simplify getting a security 2019-02-11 16:48:53 -05:00
Michael Harding a384eb9f73 Return int from sparkcmd 2019-02-11 16:48:53 -05:00
Eric Harding 4747fe5535 Make sure every sparkcmd command respects -configserver 2019-02-11 16:48:53 -05:00
Eric Harding 94eb1e37cb Add netcore publish to jenkinsbuild.cmd 2019-02-11 16:48:53 -05:00
Jake Norbut fd1ae60e49 remove checkin cruft 2019-02-11 16:48:53 -05:00
Jake Norbut 62d06180ea making row stay in edit mode when a change has not yet been committed but the selection has been moved off the row 2019-02-11 16:48:53 -05:00
Jake Norbut dba3b9371c hopefully use the right Dispatcher this time 2019-02-11 16:48:53 -05:00
Jake Norbut aadfe2a677 mini ob - inline qty tick up/down; formatting 2019-02-11 16:48:53 -05:00
Jake Norbut c0c34efc47 adding comment back in 2019-02-11 16:48:53 -05:00
Jake Norbut b66fb02174 adding single-click delete/cancel functionality 2019-02-11 16:48:53 -05:00
Jake Norbut 3cf026ff47 Properly Dispose of the OrderBookVM when the popup closes 2019-02-11 16:48:53 -05:00
Jake Norbut 8619084fa7 adding a border around the popup window 2019-02-11 16:48:53 -05:00
Jake Norbut f6df644ab1 MiniOB will have parent-child rows expanded by default 2019-02-11 16:48:53 -05:00
Jake Norbut 1183dee418 Create a new OrderbookVM each time; fixes crash when launching mini-ob from same badge multiple times 2019-02-11 16:48:53 -05:00
Jake Norbut ffaba90c96 Remove ContextMenu from Badge and setup new Popup 2019-02-11 16:48:53 -05:00
Jake Norbut 9eb760b3c7 Adding a Popup dep prop to the Badge. Note - I don't think Ineed AffectsRender here 2019-02-11 16:48:53 -05:00
Jake Norbut 70fcb30c67 DIYGrid - adding dep prop to allow rows horizontal scrollbar to be strictly collapsed 2019-02-11 16:48:53 -05:00
Jake Norbut 553ab7b858 in case my machine starts on fire 2019-02-11 16:48:53 -05:00
Kevin Darby e4c0e9d7b5 queue_holder_order gui 2019-02-08 16:43:07 -05:00
Kevin Darby 8232dcfe97 Merge branch 'master' of bitbucket.org:bluetradesys/spark into queue_holder_order 2019-02-08 16:04:24 -05:00
Taha Afzal f9886cfd44 code to move around orders 2019-02-08 11:42:19 -05:00
Taha Afzal b9c25dab4b helper functions to ove orders 2019-02-07 19:31:16 -05:00
Taha Afzal 580a6c5604 pointer to parent order in the state objects is const 2019-02-07 07:13:53 -05:00
Taha Afzal 48aa32a184 QHOEndStateWorker done 2019-02-06 23:12:35 -05:00
Taha Afzal 39b6a49b3d QHOWaitingForCancelAllStateWorker completed 2019-02-06 21:16:47 -05:00
Taha Afzal 2d7b97cf98 QHOWorkingStateWorker completed 2019-02-06 17:23:32 -05:00
Taha Afzal 7230550567 QHOWaitingForAckStateWorker completed 2019-02-06 16:34:04 -05:00
Taha Afzal 93a3ac55a1 Cleaning up QueueHolderOrder's state 2019-02-06 15:54:20 -05:00
Taha Afzal 7a586c075b QHOOrderManager is used by QueueHolderOrder 2019-02-06 15:05:23 -05:00
Taha Afzal 797611331b Added QHOOrderManager: 2019-02-06 12:35:43 -05:00
Taha Afzal c8c943f66e switching state code 2019-02-05 12:14:51 -05:00
Taha Afzal 10b3558bea cleaned up the Pimpl 2019-02-05 10:24:24 -05:00
Taha Afzal d9111520fc All state handlers added 2019-02-05 10:10:44 -05:00
Taha Afzal e58bbcc771 adding more state workers 2019-02-05 09:42:52 -05:00
Taha Afzal b7806a7193 comments in the qho worker interface 2019-02-04 23:32:41 -05:00
Taha Afzal 0bbf58638e added more logic to QueueHolderOrderStartStateWorker 2019-02-04 09:04:19 -05:00
Pedro Pinto 8dcf41e9cf Added attach/detach interface proposal. 2019-02-01 10:08:08 -05:00
Taha Afzal 173e44d992 QueueHolderOrderStartStateWorker sekeleton added 2019-02-01 09:59:22 -05:00
Taha Afzal 3fe6260fd4 IQueueHolderOrderStateWorker added 2019-02-01 09:09:02 -05:00
Taha Afzal 8d600e090f added send order and cancel 2019-01-31 19:44:56 -05:00
Taha Afzal 851459922f QueueHolderOrder initialization done 2019-01-31 18:18:16 -05:00
Taha Afzal f8aa5ffeac First checking, scaffolding 2019-01-30 15:07:44 -05:00
Pedro Pinto a0bf0e93f4 Fixed comment. 2019-01-30 14:48:00 -05:00
Pedro Pinto 5529e80e17 Tentative new order services interface. 2019-01-29 18:07:27 -05:00
Pedro Pinto 5df7cec55a Merge branch 'master' into SPARK-1944-attach/detach/update 2019-01-29 16:25:03 -05:00
Pedro Pinto 8f705d42b3 Merge branch 'master' into SPARK-1944-attach/detach/update 2019-01-29 10:44:50 -05:00
Pedro Pinto fc2f85b842 Refactored a bit. 2019-01-28 08:52:34 -05:00
Pedro Pinto ffe5eee584 Merged with ticker update branch 2019-01-18 10:21:48 -05:00
Pedro Pinto b7897fc7ac Documenting the iceberg design. 2019-01-16 14:00:16 -05:00
Pedro Pinto b4d18a62c9 Allowed async return out of order operations. 2019-01-16 09:41:06 -05:00
Pedro Pinto c6b450f45c Fixed broken perf traces. 2019-01-15 13:58:15 -05:00
Pedro Pinto 6a46049610 Merged 2019-01-15 09:21:20 -05:00
Pedro Pinto 21712711f7 Added Order API support for return result of order ops. 2019-01-14 12:48:14 -05:00
Pedro Pinto 9f1bb177b8 Fixed build. 2019-01-12 15:56:01 -05:00
Pedro Pinto dcd2cc22e1 Removed trace::CorID from most of the order code. 2019-01-12 15:45:08 -05:00
Pedro Pinto af91ee1512 Merge remote-tracking branch 'origin/master' into SPARK-1944-attach/detach/update 2019-01-11 12:50:12 -05:00
Pedro Pinto b4d63aa3cc Merge branch 'master' into SPARK-1944-attach/detach/update 2019-01-10 14:26:34 -05:00
Pedro Pinto d0e32911ad Made recovery of lost order use case insensitive op id and account.
Turns out CME upper cases operator id and clearing account when it
sends them back.
2019-01-10 11:23:20 -05:00
Pedro Pinto 75fa582774 Merged master 2019-01-09 17:51:21 -05:00
Pedro Pinto 69155162ca Improved replay of fix messages to be more robust. 2019-01-09 17:44:17 -05:00
Pedro Pinto 2c865c123a Fixed broken persistence of orders. 2019-01-09 17:43:41 -05:00
Pedro Pinto 0ec955cc90 Possibly fixed persistence failure when trying to duplicate fill. 2019-01-09 15:21:07 -05:00
Pedro Pinto 0c4e22537d Upgraded SDK example to support op result of order.activate() 2019-01-09 15:19:37 -05:00
Pedro Pinto 601eb4ece1 key needs to be setup before we can call on_init_from_exe_report 2019-01-09 15:18:34 -05:00
Pedro Pinto b84a638199 Fixed life time bug when retuerning a def from an execution report. 2019-01-09 15:17:50 -05:00
Pedro Pinto 34350fd469 Fixed utterly broken recovery of orders from execution reports. 2019-01-09 15:11:53 -05:00
Pedro Pinto a469da5cc6 Fixed race condition when obtaining thread name of perf log thread. 2019-01-09 14:45:35 -05:00
Pedro Pinto 8be8a67f18 Adding results to order operations. 2019-01-07 18:06:46 -05:00
Pedro Pinto abc2e55610 Added assert. 2019-01-02 14:37:23 +00:00
Pedro Pinto cfd15d3371 Allowed full order definition updates. 2018-12-31 17:46:00 +00:00
Pedro Pinto 44369bae43 Some work on supporting full order updates. 2018-12-28 22:13:38 +00:00
738 changed files with 38013 additions and 10831 deletions

No files matched your search

+177 -91
View File
@@ -1,83 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/build/ninja_debug/server/sparkd/sparkd",
"args": ["run"],
"stopAtEntry": false,
"cwd": "${workspaceRoot}/build/ninja_debug/src/server/sparkd",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
{
"name": "Client Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/spark.exe",
"args": [],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "SDK Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
"args": ["md","GLBX","Future","CL", "CLZ9", "--user=bts", "--pass="],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Excel Launch",
"type": "clr",
"request": "launch",
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
"args": ["${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Tests",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": ["test","test_filter"],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Attach",
"type": "clr",
"request": "attach",
"processId":"${command:pickProcess}"
},
{
"name": "SparkVM Launch",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
"args": ["run"],
"args": [
"run"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/server/",
"environment": [],
@@ -102,24 +33,113 @@
"description": "LD LIBRARY",
"text": "set env LD_LIBRARY_PATH=.",
"ignoreFailures": false
}
}
],
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": ["bts@sparkvm"],
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
}
},
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/build/ninja_debug/server/sparkd/sparkd",
"args": [
"run"
],
"stopAtEntry": false,
"cwd": "${workspaceRoot}/build/ninja_debug/src/server/sparkd",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
{
"name": "Client Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/spark.exe",
"args": [],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "SDK Launch",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
"args": [
"md",
"GLBX",
"Future",
"CL",
"CLZ9",
"--user=bts",
"--pass="
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Excel Launch",
"type": "clr",
"request": "launch",
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
"args": [
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Tests",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": [
"test",
"test_filter"
],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Attach",
"type": "clr",
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"name": "Spreader Tests",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test", "[spreader]"],
"args": [
"test",
"[spreader]"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
@@ -139,7 +159,9 @@
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": ["bts@sparkvm"],
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
@@ -151,7 +173,10 @@
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test", "[admin_service]"],
"args": [
"test",
"[admin_service]"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
@@ -171,7 +196,9 @@
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": ["bts@sparkvm"],
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
@@ -183,7 +210,10 @@
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test", "[limiter]"],
"args": [
"test",
"[limiter]"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
@@ -203,7 +233,9 @@
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": ["bts@sparkvm"],
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
@@ -215,7 +247,10 @@
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
"args": ["test", "[mbo]"],
"args": [
"test",
"SingleWriterConcurrentVector"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/server/",
"environment": [],
@@ -240,14 +275,16 @@
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": ["bts@sparkvm"],
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
}
},
{
{
"name": "SparkVM attach",
"type": "cppdbg",
"request": "attach",
@@ -255,16 +292,18 @@
"processId": "${command:pickRemoteProcess}",
"MIMode": "gdb",
"pipeTransport": {
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": ["bts@sparkvm"],
"debuggerPath": "/usr/bin/gdb"
"pipeCwd": "${workspaceRoot}",
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
"/home/bts/spark/": "${workspaceRoot}"
}
},
{
},
{
"name": "Python",
"type": "python",
"request": "launch",
@@ -274,12 +313,59 @@
"cwd": "${workspaceRoot}",
"env": {},
"envFile": "${workspaceRoot}/.env",
"args": [ "DEBUG" ],
"args": [
"DEBUG"
],
"debugOptions": [
"WaitOnAbnormalExit",
"WaitOnNormalExit",
"RedirectOutput"
]
},
{
"name": ".NET Core Remote Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickRemoteProcess}",
"pipeTransport": {
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/home/bts/vsdbg/vsdbg",
"pipeCwd": "${workspaceRoot}",
"quoteArgs": true
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
},
"justMyCode": true
},
{
"name": ".NET Core Remote Launch",
"type": "coreclr",
"request": "launch",
"program": "/usr/bin/dotnet",
"args": [
"sparkcmd.dll",
"test",
"test_join_create_and_takeout_market"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/server",
"pipeTransport": {
"pipeProgram": "c:\\program files\\git\\usr\\bin\\ssh.exe",
"pipeArgs": [
"bts@sparkvm"
],
"debuggerPath": "/home/bts/vsdbg/vsdbg",
"pipeCwd": "${workspaceRoot}",
"quoteArgs": true
},
"sourceFileMap": {
"/home/bts/spark/": "${workspaceRoot}"
},
"justMyCode": false
},
]
}
+1 -1
View File
@@ -1 +1 @@
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)
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Algos/_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/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
View File
@@ -7,7 +7,6 @@ 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
+98
View File
@@ -4,6 +4,104 @@ weight = 3
sort_by = "weight"
insert_anchor_links = "right"
+++
## 2.2.0
### User Visible Changes
- Made our orders easier to spot in MBO display
- Changed middle-click on center column to recenter ladder and introduced Alt middle-click on center to bring up the MBO popup
- Fixed Net/Absolute radio button in risk admin UI
- Fixed double counting of spread legs in limits
- Removed expired fills from the system
- Improved handling of orders with undiscovered securities
- Added feedback to the ladder when the user attempts to middle click on bid/ask columns using an order that does not have parameters
- Fixed display of algo studio algo types in order book
- Hid net change setting in synthetic ladders
- Fixed a number of problems with filtering on the Fills form
- On the Lean algo, OnToward and Away are now calculated with respect to the opposite inside market at time of the order launch
- Iceberg Spreaders with queue holders are not erroneously cancelled after the first fill
## 2.1.4
### User Visible Changes
- Fixed handling of cancel button in security picker
## 2.1.3
### User Visible Changes
- Fixed a simulator bug in the handling of 0 sized orders
## 2.2.1
### User Visible Changes
- Fixed order book form exception when display a spreader order for whichs the legs were not present yet
## 2.1.1
### User Visible Changes
- Added PIQ to GLBX Iceberg orders
- Corrected handling of stop limit orders
## 2.1.0
### User Visible Changes
- Added support for the GLBX Iceberg order type
- Allowed quick algo param edits by middle-clicking on either the bid/ask columns or a "favorite" button
- Added support for manual fills, either entered manually or imported from file. Manual fills are used only for P&L purposes, they have no impact on limits
- Allowed the LTQ and VAP values to be overlayed on the same ladder column
- Added day position information to the ladder
- Added PIQ to the mini order book
- Fixed a rare bug that could lead to server failure when requesting market data
- Fixed a bug that could cause incorrect price displays for certain types of synthetic securities
## 2.0.5
### User Visible Changes
- Fixed a bug in the handling of rejected QHO orders
- Implemented stricter position limit enforcement
- Fixed a bug that could cause server failure when trying to attach to already attached orders
## 2.0.4
### User Visible Changes
- Fixed performance regression in order manager
### Internal changes
- Improved market data diagnostics for bad configurations
- Changed market data default buffer size
## 2.0.3
### Internal changes
- Improved backwards compatibility in UI settings
## 2.0.2
### User Visible Changes
- Middle click the ladder price column to show the orders for that price level
- Middle click the bid or ask column in the ladder no longer sends an order
### Internal changes
- Improved market by order performance under heavy load
- Fixed a concurrency bug in SIM
## 2.0.0
### User Visible Changes
- Support for attaching and detaching algos to existing orders
- Support for display and update of all parameters in running algos
- Streamlining of order badge decorations in ladder
- New order search/filtering algorithm for order book
- Simpler representation of complex algo chains in order book and mini order book
- Visual and usability improvements to mini order book
- Streamlining of algo parameter dialog in ladder
- New spreader queue holder behavior
- Visual improvements to the order book
- New server side API
- Improved admin UI performance
- Optimized PIQ rendering
### Internal changes
- Exchange simulator no longer requires daily sequence number reset
- Improved MD resilience to corrupted shared memory
- Native orders can now buffer updates/cancels when disconnected or pending first acknowledgment
- Improved settings manager tool output
- Added diagnostics in the event MD memory corruption is detected
- Added systems tests for join,lean, iceberg and spreader orders
- Made exchange order recovery more resilient in the presence of database loss
## 1.6.2
### User Visible Changes
### Exchange simulator
- Exchange simulator now now produce position in queue data
## 1.6.0
### User Visible Changes
#### New Features
@@ -73,6 +73,7 @@ namespace bts {
window.Close();
message.complete(select_result(vm)).execute(_bus);
};
vm.cancel_action = () => window.Close();
window.move_to_mouse();
window.Show();
}
+27
View File
@@ -22,10 +22,14 @@ namespace mbts.ui {
AddBoxesGeometryKey = nameof(AddBoxesGeometryKey),
AddFileGeometryKey = nameof(AddFileGeometryKey),
AddFileThinGeometryKey = nameof(AddFileThinGeometryKey),
AttachAlgosKey = nameof(AttachAlgosKey),
CheckMarkGeometryKey = nameof(CheckMarkGeometryKey),
DataEditGeometryKey = nameof(DataEditGeometryKey),
DetachAlgosKey = nameof(DetachAlgosKey),
DiskGeometryKey = nameof(DiskGeometryKey),
DiskFileGeometryKey = nameof(DiskFileGeometryKey),
DocumentGeometryKey = nameof(DocumentGeometryKey),
DocumentAddGeometryKey = nameof(DocumentAddGeometryKey),
DocumentBrokenGeometryKey = nameof(DocumentBrokenGeometryKey),
DocumentCopyGeometryKey = nameof(DocumentCopyGeometryKey),
DocumentEmptyGeometryKey = nameof(DocumentEmptyGeometryKey),
@@ -35,10 +39,14 @@ namespace mbts.ui {
EditDocumentAltGeometryKey = nameof(EditDocumentAltGeometryKey),
EditDocumentGeometryKey = nameof(EditDocumentGeometryKey),
GearGeometryKey = nameof(GearGeometryKey),
LetterAKey = nameof(LetterAKey),
LetterDKey = nameof(LetterDKey),
OrdersGeometryKey = nameof(OrdersGeometryKey),
PaymentGeometryKey = nameof(PaymentGeometryKey),
PencilGeometryKey = nameof(PencilGeometryKey),
PlayGeometryKey = nameof(PlayGeometryKey),
ProhibitedGeometryKey = nameof(ProhibitedGeometryKey),
RubberStampGeometryKey = nameof(RubberStampGeometryKey),
StarGeometryKey = nameof(StarGeometryKey),
ToolsGeometryKey = nameof(ToolsGeometryKey),
TrashGeometryKey = nameof(TrashGeometryKey),
@@ -115,6 +123,7 @@ namespace mbts.ui {
public static ImageSource AddBoxes => create_icon(AddBoxesGeometryKey);
public static ImageSource AddEncircled { get { return load_resource_and_freeze("AddEncircled"); } }
public static ImageSource AddFile => create_icon(Smashicons.AddFileGeometryKey);
public static ImageSource AttachAlgos => create_icon(AttachAlgosKey);
public static ImageSource Beaker { get { return load_resource_and_freeze("Beaker"); } }
public static ImageSource Book { get { return load_resource_and_freeze("Book"); } }
@@ -138,10 +147,13 @@ namespace mbts.ui {
public static ImageSource ModifyOrder { get { return load_resource_and_freeze("ModifyOrder"); } }
public static ImageSource CheckMark => create_icon(CheckMarkGeometryKey);
public static ImageSource Clock { get { return load_resource_and_freeze("Clock"); } }
public static ImageSource DataEdit => create_icon(DataEditGeometryKey, Brushes.White, new Pen(Brushes.Black, 1).frozen());
public static ImageSource Defrag { get { return load_resource_and_freeze("Defrag"); } }
public static ImageSource DetachAlgos => create_icon_and_rotate(DetachAlgosKey, Brushes.Red);
public static ImageSource Disk => create_icon(DiskGeometryKey);
public static ImageSource DiskFile => create_icon(DiskFileGeometryKey);
public static ImageSource Display { get { return load_resource_and_freeze("Display"); } }
public static ImageSource DocumentAdd => create_icon(DocumentAddGeometryKey);
public static ImageSource DocumentBroken => create_icon(DocumentBrokenGeometryKey);
public static ImageSource DocumentCopy => create_icon(DocumentCopyGeometryKey);
public static ImageSource DocumentEmpty => create_icon(DocumentEmptyGeometryKey);
@@ -158,6 +170,8 @@ namespace mbts.ui {
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
public static ImageSource Payment => create_icon(PaymentGeometryKey);
public static ImageSource Pencil => create_icon(PencilGeometryKey);
public static ImageSource Prohibited => create_icon(ProhibitedGeometryKey);
public static ImageSource RubberStamp => create_icon(RubberStampGeometryKey);
public static ImageSource RedoCircular => create_icon(UndoCircularGeometryKey, Brushes.Black, null, new ScaleTransform(-1, 1));
public static ImageSource Spawn { get { return load_resource_and_freeze("Spawn"); } }
public static ImageSource Star => create_icon(StarGeometryKey);
@@ -186,6 +200,19 @@ namespace mbts.ui {
return rv;
}
public static ImageSource create_icon_and_rotate(string geometric_path_key, Brush brush = null, Pen pen = null, Transform transform = null, double degrees = 90) {
if (brush == null) {
brush = Brushes.Black;
}
var drawing = create_drawing(geometric_path_key, brush, pen, transform);
var dg = new DrawingGroup();
dg.Children.Add(drawing);
dg.Transform = new RotateTransform(degrees);
var rv = new DrawingImage(dg);
rv.Freeze();
return rv;
}
public static Drawing create_drawing(string geometric_path_key) =>
create_drawing(geometric_path_key, Brushes.Black);
+30
View File
@@ -5,6 +5,14 @@
xmlns:mbts="clr-namespace:mbts.ui"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<sys:String x:Key="{x:Static mbts:Icons.AttachAlgosKey}">
M10.519881,14.945064C11.300896,14.955074,12.079896,15.102078,12.819896,15.386075L10.878893,17.326085C9.6458764,17.201085,8.3688531,17.591071,7.4258485,18.533091L3.5977991,22.360102C1.9327704,24.026093 1.9317786,26.736124 3.5977991,28.402117 5.2638197,30.068108 7.9748359,30.068108 9.6398644,28.402117L13.467914,24.575106C14.410919,23.632109,14.800938,22.355097,14.675937,21.121083L16.615963,19.180096C17.523962,21.546103,17.034943,24.325104,15.126931,26.234108L11.298882,30.061119C8.7138591,32.64612 4.5238051,32.64612 1.9387672,30.062127 -0.64625573,27.477126 -0.64625573,23.287106 1.9387672,20.701098L5.7668166,16.875065C7.077837,15.564055,8.8018727,14.923061,10.519881,14.945064z M19.825993,10.418039L21.580012,12.17305 12.089907,21.665091 10.333873,19.911087z M25.383069,0C27.077088,3.0538786E-08 28.771109,0.64599859 30.063117,1.9390028 32.647133,4.5230274 32.647133,8.7120385 30.062109,11.298047L26.235068,15.125058C24.327055,17.033085,21.547022,17.52207,19.181973,16.614077L21.121998,14.674067C22.356022,14.799068,23.633046,14.409051,24.57605,13.466054L28.403094,9.6400513C30.068121,7.9740295 30.069129,5.2630205 28.403094,3.5980362 26.738064,1.9320143 24.027033,1.9320143 22.362034,3.5980362L18.533986,7.4240398C17.591958,8.3670363,17.201968,9.643042,17.32697,10.877056L15.385937,12.817066C14.478913,10.452035,14.968939,7.6730337,16.875945,5.7660436L20.703019,1.9390028C21.995026,0.64599859,23.689047,3.0538786E-08,25.383069,0z
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.DetachAlgosKey}">
M19.522852,25.255011L20.920847,25.256994 20.920847,29.729012 19.522852,29.729012z M22.826874,21.952002L25.989905,25.113989 25.001887,26.101996 21.838856,22.941015z M25.269894,19.506992L29.742937,19.509007 29.742937,20.906012 25.269894,20.907995z M10.519729,14.945011C11.300744,14.954991,12.079745,15.103001,12.818766,15.386997L10.878742,17.327C9.6457264,17.200016,8.368704,17.592014,7.4257004,18.532993L3.597655,22.359015C1.9326275,24.025 1.9316508,26.734991 3.597655,28.40299 5.2636887,30.068001 7.9747178,30.066017 9.6397144,28.40299L13.468767,24.575018C14.410794,23.633001,14.800783,22.355993,14.674774,21.122015L16.615807,19.181004C17.523806,21.547003,17.034817,24.325995,15.126776,26.233008L11.298761,30.061012C8.713709,32.644997 4.5236599,32.644997 1.9396465,30.061012 -0.64638097,27.475012 -0.64638097,23.286017 1.9386395,20.702002L5.7666852,16.875004C7.0777046,15.562992,8.8017227,14.922001,10.519729,14.945011z M2.1536381,10.959994L6.6257035,10.959994 6.6266801,12.359988 2.1536381,12.359988z M6.7686803,5.894014L9.9307338,9.0549947 8.9427158,10.044008 5.7806932,6.8819901z M10.976735,2.1390082L12.374761,2.1390082 12.375768,6.6130081 10.975728,6.6130081z M25.382902,0C27.076922,-1.8170738E-08 28.770941,0.6470033 30.063955,1.9389956 32.646992,4.5220043 32.646992,8.7130147 30.061941,11.297L26.234902,15.126011C24.326891,17.032993,21.546862,17.521,19.181845,16.614994L21.121868,14.674015C22.35586,14.798008,23.63389,14.407992,24.575886,13.467013L28.403933,9.6390096C30.067953,7.972994 30.069967,5.2619945 28.403933,3.5960094 26.738905,1.9310002 24.0269,1.9320073 22.361872,3.5960094L18.533827,7.4249893C17.591829,8.3659993,17.201811,9.6440145,17.32782,10.87601L15.385781,12.817996C14.477781,10.453005,14.968784,7.6730062,16.875818,5.7659928L20.702858,1.9379885C21.994864,0.64599622,23.688883,-1.8170738E-08,25.382902,0z
</sys:String>
<!--AddBoxes (the icon is free to use if we include this information in our credit section)
Source: https://www.flaticon.com/authors/gregor-cresnar
Ttile: Add free icon
@@ -40,6 +48,21 @@
</sys:String>
</x:Array>
<sys:String x:Key="{x:Static mbts:Icons.DocumentAddGeometryKey}">
M10.830011,12.05L13.334007,12.05 13.334007,16.535997 17.819998,16.535997 17.819998,19.03902 13.334007,19.03902 13.334007,23.525016 10.830988,23.525016 10.830988,19.03902 6.3460046,19.03902 6.3449975,16.53499 10.830988,16.535997z M2.2799975,2.4149805L2.2799975,29.580015 22.121998,29.580015 22.121998,8.500008 15.83599,8.500008 15.83599,2.4149805z M0,0L15.83599,0 17.125998,0 22.681996,5.439977 24.407,7.125007 24.407,32.000001 0,32.000001z
</sys:String>
<!--File (the icon is free to use if we include this information in our credit section)
Source: https://www.flaticon.com/authors/chanut
Ttile: Cancel free icon
Credit: Icon made by Chanut from www.flaticon.com-->
<sys:String x:Key="{x:Static mbts:Icons.ProhibitedGeometryKey}">
M32.759,0C14.696,0,0,14.695,0,32.759s14.695,32.759,32.759,32.759s32.759-14.695,32.759-32.759S50.822,0,32.759,0z
M6,32.759C6,18.004,18.004,6,32.759,6c6.648,0,12.734,2.443,17.419,6.472L12.472,50.178C8.443,45.493,6,39.407,6,32.759z
M32.759,59.518c-5.948,0-11.447-1.953-15.895-5.248l37.405-37.405c3.295,4.448,5.248,9.947,5.248,15.895
C59.518,47.514,47.514,59.518,32.759,59.518z
</sys:String>
<!--File (the icon is free to use if we include this information in our credit section)
Source: https://www.flaticon.com/authors/smashicons
Ttile: File free icon
@@ -88,6 +111,9 @@
s1-0.448,1-1v-5h5c0.552,0,1-0.448,1-1S52.052,46,51.5,46z
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.DataEditGeometryKey}">
M17.997977,17.396268C18.197928,17.396268,18.297904,17.496276,18.497975,17.596284L19.497972,18.295283C19.99797,18.595292,20.697919,19.195308,21.197917,19.495299L22.196937,20.195323C22.696935,20.495315,22.596959,20.995322,22.096961,21.195338L20.997966,21.695346C20.497968,21.89434,19.597947,22.294355,19.097949,22.494355L17.997977,22.994363C17.497979,23.194362,17.097957,22.894355,17.197932,22.394347L17.297908,21.195338C17.398005,20.595323,17.497979,19.695315,17.497979,19.195308L17.597955,17.995277C17.497979,17.695283,17.69793,17.396268,17.997977,17.396268z M1.799981,13.597213L19.697923,13.597213 17.997977,15.896244C17.797906,16.096259 17.69793,16.396253 17.69793,16.69626 17.197932,16.796268 16.79791,17.296275 16.697934,17.895284L16.597959,19.195308 1.799981,19.195308C0.79998477,19.195308,0,18.395291,0,17.396268L0,15.396237C0,14.396221,0.79998477,13.597213,1.799981,13.597213z M1.799981,6.7981106L21.796914,6.7981106C22.696935,6.7981106,23.49698,7.498118,23.596955,8.3981257L20.697919,12.397197 1.799981,12.397197C0.79998477,12.397197,0,11.597181,0,10.597165L0,8.5981418C0,7.598126,0.79998477,6.7981106,1.799981,6.7981106z M27.796891,4.3990708C27.996963,4.3990708,28.096938,4.3990708,28.296889,4.5990711L31.695924,7.0981179C31.995851,7.3981105,32.095948,7.7981258,31.895875,8.0981341L23.296908,19.795308C23.096957,20.095315,22.596959,20.195323,22.196937,19.895314L18.797902,17.396268C18.497975,17.096274,18.398002,16.69626,18.597951,16.396253L27.196918,4.6990786C27.396869,4.4990784,27.59694,4.3990708,27.796891,4.3990708z M1.799981,0L21.796914,0C22.79691,4.9549271E-08,23.596955,0.8000156,23.596955,1.800031L23.596955,3.7990554C23.596955,4.7990709,22.79691,5.5990864,21.796914,5.5990864L1.799981,5.5990864C0.79998477,5.4990941,0,4.6990786,0,3.6990629L0,1.7000236C0,0.8000156,0.79998477,4.9549271E-08,1.799981,0z
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.DiskGeometryKey}">
M403.213,20H0v472h512V128.787L403.213,20z M131,50h180v100h10h20h10V50h30v130H131V50z M381,462H131V315
c0-8.271,6.729-15,15-15h220c8.271,0,15,6.729,15,15V462z M482,462h-71V315c0-24.813-20.187-45-45-45H146
@@ -271,6 +297,10 @@
M0,0L25.194,16 0,32z
</sys:String>
<sys:String x:Key="{x:Static mbts:Icons.RubberStampGeometryKey}">
M1.8950181,29.607025L24.423994,29.607025 24.423994,31.103027C24.423994,31.601013,24.025009,32,23.526993,32L2.7920208,32C2.2929976,32,1.8950181,31.601013,1.8950181,31.103027z M0,19.838013L26.218001,19.838013 26.218001,27.813019 0,27.813019z M12.960014,0C16.549,0 19.538988,2.9909973 19.538988,6.57901 19.538988,8.0750122 19.041002,9.3710022 18.243,10.46701L18.043995,10.766022C17.345997,11.863007 16.647999,12.76001 16.647999,12.76001 15.052999,15.651001 15.253011,17.745026 15.253011,17.745026L10.667015,17.745026C10.667015,17.745026 10.866997,15.651001 9.2719967,12.76001 9.2719967,12.76001 8.5739996,11.863007 7.8760016,10.766022L7.6759898,10.46701 7.5769908,10.368011C6.7790176,9.3710022 6.3810074,7.9750061 6.3810074,6.57901 6.3810074,2.9909973 9.2719967,0 12.960014,0z
</sys:String>
<!--File (the icon is free to use if we include this information in our credit section)
Source: https://www.flaticon.com/authors/smashicons
Ttile: File free icon
@@ -1,7 +1,7 @@
using bts.api;
using bts.api.orderman;
using bts.collections;
using mbts.co2;
using mbts.collections;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.controls;
@@ -204,13 +204,15 @@ namespace mbts.spark {
}
}
void _show_order_book(Side side) =>
_bus.post(SparkTags.ShowOrderBookTag, new bts.shell.OrderBookWindowFactory.OrderBookStartupData(
new bts.shell.OrderBookWindowFactory.FilterStates {
statuses = new[] { OrderStatus.Working },
sides = new[] { side },
security_criteria = new SecurityCriteria(security)
}));
void _show_order_book(Side side) {
var ob_settings = bts.shell.orderbook.OrderBookStartupData.Default;
ob_settings.filters = new bts.shell.orderbook.FilterStates {
statuses = new[] { OrderStatus.Working },
sides = new[] { side },
security_criteria = new SecurityCriteria(security)
};
_bus.post(SparkTags.ShowOrderBookTag, ob_settings);
}
void _show_order_ticket(MarketViewView.Columns col) {
var order = _orders_model.factory.order(security);
@@ -1,4 +1,6 @@
using bts.api;
using bts.collections;
using bts.utils;
using mbts.persist;
using mbts.spark.filters;
using mbts.ui;
@@ -7,7 +9,6 @@ using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Windows.Data;
@@ -22,7 +23,6 @@ namespace mbts.spark {
readonly OrdersModel _orders_model;
IWindowSettingsManager<GridSettings> _settings_manager;
public MarketViewVM(IBus bus, OrdersModel orders_model, IObservable<StartupData> state, IWindowSettingsManager<GridSettings> settings_manager )
: this(bus, orders_model, state, state.SelectManyInSeries(s => s.security_criteria.subscribe_md(bus)), settings_manager) { }
@@ -40,7 +40,7 @@ namespace mbts.spark {
add_col(Columns.Security, "Security", r => r.security.ToString(), nameof(Row.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.Volume, "Volume", r => r.volume.ToString(), nameof(Row.volume), DefaultColumnWidths.Volume, volume_filter, DataGridFilterKnownTemplates.ULongMinMax);
add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UShortMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UIntMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right);
add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid, System.Windows.HorizontalAlignment.Right);
add_col(Columns.BidImpliedSize, implied_size_col_header, r => r.bid_implied_size.ToString(), nameof(Row.bid_implied_size), DefaultColumnWidths.ImpliedDirectMergedSize, bid_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
@@ -58,12 +58,12 @@ namespace mbts.spark {
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);
add_col(Columns.MarketDescription, "Market Description", r => r.security.market_description(), nameof(Row.market_description), DefaultColumnWidths.SecurityKey, market_description_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.ClearedAlias, "Cleared Alias", r => r.security.cleared_alias(), nameof(Row.cleared_alias), DefaultColumnWidths.SecurityKey, cleared_alias_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.ProductDescription, "Product Description", r => r.security.product_name(), nameof(Row.product_description), DefaultColumnWidths.SecurityKey, product_description_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.HubAlias, "Hub Alias", r => r.security.hub_alias(), nameof(Row.hub_alias), DefaultColumnWidths.SecurityKey, hub_alias_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.StripName, "Strip Name", r => r.security.strip_name(), nameof(Row.strip_name), DefaultColumnWidths.SecurityKey, strip_name_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.MicCode, "Mic Code", r => r.security.mic_code(), 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) {
@@ -81,7 +81,7 @@ namespace mbts.spark {
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.MViewPrice, foreground, background, DefaultSelectionHighlight, h_alignment);
void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UShortMinMax, DefaultColumnWidths.ImpliedDirectMergedSize, foreground, null, null, h_alignment);
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.ImpliedDirectMergedSize, foreground, null, null, h_alignment);
void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null, h_alignment);
@@ -180,6 +180,40 @@ namespace mbts.spark {
return grid_settings_manager.create();
}
public static IComparer<Row> get_column_comparer(Columns column) {
switch (column) {
case Columns.Security: return wrap(r => r.security);
case Columns.SecurityKey: return wrap(r => r.security.Id);
case Columns.Volume: return wrap(r => r.volume);
case Columns.Last: return wrap(r => r.last_price);
case Columns.LastSize: return wrap(r => r.last_size);
case Columns.Position: return wrap(r => r.position);
case Columns.WorkingBids: return wrap(r => r.working_bid_volume);
case Columns.BidImpliedSize: return wrap(r => r.bid_implied_size);
case Columns.BidDirectSize: return wrap(r => r.bid_direct_size);
case Columns.BidMergedSize: return wrap(r => r.bid_merged_size);
case Columns.Bid: return wrap(r => r.bid_price);
case Columns.Ask: return wrap(r => r.ask_price);
case Columns.AskMergedSize: return wrap(r => r.ask_merged_size);
case Columns.AskDirectSize: return wrap(r => r.ask_direct_size);
case Columns.AskImpliedSize: return wrap(r => r.ask_implied_size);
case Columns.WorkingAsks: return wrap(r => r.working_ask_volume);
case Columns.Settlement: return wrap(r => r.settlement);
case Columns.DayChange: return wrap(r => r.day_change);
case Columns.SessionLow: return wrap(r => r.session_low);
case Columns.SessionHigh: return wrap(r => r.session_high);
case Columns.MarketDescription: return wrap(r => r.market_description);
case Columns.ClearedAlias: return wrap(r => r.security.cleared_alias());
case Columns.ProductDescription: return wrap(r => r.security.product_name());
case Columns.HubAlias: return wrap(r => r.security.hub_alias());
case Columns.StripName: return wrap(r => r.security.strip_name());
case Columns.MicCode: return wrap(r => r.security.mic_code());
default: return null;
}
IComparer<Row> wrap<T>(Func<Row, T> selector) =>
Comparer<T>.Default.wrap(selector);
}
#region Filters
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_price_filter { get; } = new DoubleMinMax<Row>(r => r.bid_price, nameof(Row.bid_price));
@@ -1,7 +1,9 @@
using bts;
using bts.api;
using bts.shell;
using bts.shell.fills;
using bts.spark;
using bts.utils;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
@@ -11,12 +13,13 @@ using mbts.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows;
namespace mbts.spark {
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>, ISharedWindow {
const string _window_type = "MarketView", _thread_id = "MarketView", _window_type_display_name = "Market View";
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>, ISharedWindow, ISlowMeasureContentSize {
const string _window_type = "MarketView", _window_type_display_name = "Market View";
readonly OrdersModel _orders_model;
readonly AllowOnce _init_guard = new AllowOnce("MarketView");
readonly IWindowSettingsManager<GridSettings> _settings_manager;
@@ -25,6 +28,9 @@ namespace mbts.spark {
MarketViewWindowFactory.StartupData _state;
IReadOnlyList<SparkCommand> _commands;
IDisposable _subscriptions;
SecurityCriteria
_selected_security_criteria_backing,
_sorted_selected_security_criteria_backing;
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, WindowSettingsManager<GridSettings> settings_manager)
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
@@ -32,6 +38,7 @@ namespace mbts.spark {
_state = state;
_settings_manager = settings_manager;
Icon.Freeze();
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().market_view ? "MarketView" : null;
}
event Action<MarketViewWindowFactory.StartupData> _state_changed;
@@ -42,12 +49,53 @@ namespace mbts.spark {
public override MarketViewWindowFactory.StartupData SaveState =>
_state = new MarketViewWindowFactory.StartupData(_state.security_criteria, _vm.get_filter_states(), _vm.get_grid_save_state(), _vm.rows_view.get_sorting_criteria(_vm.columns));
public override double MinWidth => MarketViewWindowFactory.StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
public override double MinWidth => MarketViewWindowFactory.StartupData.DefaultGridSettings.columns.Where(c => c.is_visible).Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public string thread_id => _thread_id;
public string thread_id { get; }
SecurityCriteria _selected_security_criteria {
get {
if (_selected_security_criteria_backing == null) {
if (_sorted_selected_security_criteria_backing != null) {
_selected_security_criteria_backing = _sorted_selected_security_criteria_backing;
} else if (_vm != null)
_selected_security_criteria_backing = new SecurityCriteria(_vm.selected_rows.Select(r => r.security));
}
return _selected_security_criteria_backing;
}
}
SecurityCriteria _sorted_selected_security_criteria {
get {
if (_sorted_selected_security_criteria_backing == null) {
if (_vm != null) {
var sorting_criteria = _vm.rows_view.get_sorting_criteria(_vm.columns);
var comp_builder = new ComparerBuilder<MarketViewVM.Row>();
foreach (var sc in sorting_criteria) {
if (_vm.columns.TryGetFirst(col => string.Equals(col.id, sc.col_name, StringComparison.CurrentCultureIgnoreCase), out var match)) {
if (match is ui.controls.DIYGrid.IKeyedColumn<MarketViewView.Columns> kcol) {
var col_comp = MarketViewVM.get_column_comparer(kcol.key);
if (col_comp != null) {
comp_builder.add(sc.is_ascending ? col_comp : col_comp.reverse());
} else {
Log.Ui.warn($"{_window_type} is unable to find the row comparer for the '{sc.col_name}' column.");
}
} else {
Log.Ui.warn($"{_window_type} is unable find a column with id '{sc.col_name}.'");
}
}
}
var comp = comp_builder.to_comparer();
_sorted_selected_security_criteria_backing = new SecurityCriteria(_vm.selected_rows.Ordered(comp).Select(r => r.security));
}
}
return _sorted_selected_security_criteria_backing;
}
}
bool _has_selected_rows() => _vm != null && _vm.selected_rows.Count > 0;
public void thread_init() {
_init_guard.check_access();
@@ -55,17 +103,44 @@ namespace mbts.spark {
var state_src = Observable.FromEvent<Action<MarketViewWindowFactory.StartupData>, MarketViewWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
_vm = new MarketViewVM(_bus, _orders_model, state_src, _settings_manager);
_view = new MarketViewView { DataContext = _vm };
_view._grid.Sorting += on_grid_sorting;
/// The 'CanExecute' plumbing for the cmds below is designed to avoid potentially costly .ToList() operations on the selected_rows/securities.
/// Some of the cmds post ITags with data that is, or has, a SecurityCriteria member; the SecurityCriteria constructor creates a new List of the specified securities.
/// For these commands, the objective is to evaluate 'CanExecute' without instantiating the SecurityCriteria (or more importantly, without creating a new List).
// show time and sales
var show_tas_cmd = new DelegateCommand(() => {
var sc = _selected_security_criteria;
if (sc.is_defined())
_bus.post(SparkTags.ShowTimeAndSalesTag, sc);
}, _has_selected_rows);
var show_tas_actual_cmd = SparkCommands.ShowTimeAndSales;
var show_tas_cmd_wrapper = new SparkCommandCommand(show_tas_actual_cmd.title, show_tas_actual_cmd.icon, show_tas_cmd);
// show fills
var show_fills_cmd = new DelegateCommand(() => {
var sc = _selected_security_criteria;
_bus.post(SparkTags.ShowFillsTag, new FillsSettings(
new FillsFilterStates {
security = sc.is_defined() ? sc : _state.security_criteria
}));
});
var show_fills_actual_cmd = SparkCommands.ShowFills();
var show_fills_cmd_wrapper = new SparkCommandCommand(show_fills_actual_cmd.title, show_fills_actual_cmd.icon, show_fills_cmd, SparkCommands.KeyGestures.ShowFills);
// show synthetic create
var show_synth_create_cmd = new DelegateCommand(() => {
var sc = _sorted_selected_security_criteria;
if (sc.is_defined())
_bus.post(SparkTags.ShowSyntheticCreateTag, sc);
}, () => _vm.selected_rows.Count > 0 && _vm.selected_rows.Count <= 24);
var show_synth_create_actual_cmd = SparkCommands.ShowSyntheticCreate();
var show_synth_create_cmd_wrapper = new SparkCommandCommand(show_synth_create_actual_cmd.title, show_synth_create_actual_cmd.icon, show_synth_create_cmd, SparkCommands.KeyGestures.ShowSyntheticCreate);
var update_securities_cmd = SparkCommands.change_securities_callback(_window_type_display_name);
_commands = new List<SparkCommand> {
SparkCommands.ShowLadders.complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills.complete(_vm.selected_securities_observable(), _ => true,
secs => new FillsWindowFactory.StartupData(new FillsWindowFactory.FilterStates {
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
})
),
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.ShowSyntheticCreate.complete(_vm.sorted_selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs)),
SparkCommands.ShowLadders(SparkCommands.KeyGestures.ShowLadder).complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
SparkCommands.ShowOrderTicketFromSecurity(SparkCommands.KeyGestures.ShowOrderTicket).complete(_vm.current_security_md, md => md.HasValue, ConvertUtils.ValueOrDefault),
show_fills_cmd_wrapper,
show_tas_cmd_wrapper,
show_synth_create_cmd_wrapper,
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
SparkCommands.window_settings_command(new DialogCommand(() => {
@@ -86,8 +161,27 @@ namespace mbts.spark {
return dialog;
}))
};
_subscriptions = _bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(c =>
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.get_filter_states(), _vm.grid_settings_manager.create())));
//
_vm.selection_changed += on_selection_changed;
_subscriptions = new CompositeDisposable(_bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(c =>
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.get_filter_states(), _vm.grid_settings_manager.create())))) {
() => {
_vm.selection_changed -= on_selection_changed;
_view._grid.Sorting -= on_grid_sorting;
}
};
void on_selection_changed((IEnumerable<MarketViewVM.Row> removed, IEnumerable<MarketViewVM.Row> added) change) {
// invalidate selected security criteria
_selected_security_criteria_backing = null;
_sorted_selected_security_criteria_backing = null;
show_tas_cmd.RaiseCanExecuteChanged();
show_fills_cmd.RaiseCanExecuteChanged();
show_synth_create_cmd.RaiseCanExecuteChanged();
}
void on_grid_sorting(object sender, ui.controls.DIYGridSortingEventArgs e) =>
_sorted_selected_security_criteria_backing = null;
}
public override void Dispose() {
@@ -1,7 +1,6 @@
using bts.api;
using bts.co2;
using mbts.co2;
using mbts.collections.extensions;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -1,22 +1,23 @@
using bts.api;
using bts.co2;
using bts.collections;
using mbts.persist;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
using mbts.ui.spark;
using mbts.ui.utility;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Media;
using static bts.shell.TimeAndSalesWindowFactory;
using static mbts.ui.controls.DIYGrid;
using mbts.persist;
using mbts.ui.settings;
using System.Reactive.Linq;
namespace mbts.spark {
class TimeAndSalesVM : SecurityGrid<TimeAndSalesVM.Row, TradeInfo> {
@@ -1,6 +1,7 @@
using bts;
using bts.api;
using bts.shell;
using bts.shell.fills;
using bts.spark;
using mbts.co2;
using mbts.persist;
@@ -19,7 +20,7 @@ using static bts.shell.TimeAndSalesWindowFactory;
using static mbts.ui.controls.DIYGrid;
namespace mbts.spark {
class TimeAndSalesWindow : SparkWindow<StartupData>, ISlowWindow {
class TimeAndSalesWindow : SparkWindow<StartupData>, ISharedWindow {
const string _window_type = "TimeAndSales", _window_type_display_name = "Time And Sales";
readonly MDConsumer _consumer;
readonly TimeAndSalesWindowFactory _factory;
@@ -48,7 +49,7 @@ namespace mbts.spark {
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public string thread_id { get; }
public override StartupData SaveState => _state = _vm.value.create();
public override void Dispose() =>
@@ -62,9 +63,9 @@ namespace mbts.spark {
var current_smd_source = connectable_builder.create(vm => vm.current_security_md_observable(_bus)).OnDispatcher();
var selected_secs_source = connectable_builder.create(vm => vm.selected_securities_observable()).OnDispatcher();
_commands = new SparkCommand[] {
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills.complete(selected_secs_source, always_true, get_fills_startup_data),
SparkCommands.ShowLadder(SparkCommands.KeyGestures.ShowLadder).complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity(SparkCommands.KeyGestures.ShowOrderTicket).complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills(SparkCommands.KeyGestures.ShowFills).complete(selected_secs_source, always_true, get_fills_startup_data),
SparkCommands.ShowMarketView.complete(selected_secs_source, always_true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
@@ -92,8 +93,8 @@ namespace mbts.spark {
_initialize();
bool always_true<T>(T _) => true;
FillsWindowFactory.StartupData get_fills_startup_data(IReadOnlyList<SecurityImmutable> secs) =>
new FillsWindowFactory.StartupData(new FillsWindowFactory.FilterStates {
FillsSettings get_fills_startup_data(IReadOnlyList<SecurityImmutable> secs) =>
new FillsSettings(new FillsFilterStates {
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
});
void update_criteria(SecurityCriteria c) {
@@ -420,7 +420,6 @@
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<c:HelpButton Grid.Column="8" Margin="3,0,0,0" ManPage="bts_spark/spreader/#creating-a-synthetic" />
</Grid>
<c:DataGridEx
x:Name="PART_DataGrid"
@@ -1,4 +1,5 @@
using bts.api;
using bts.collections;
using bts.spark;
using mbts.spark;
using mbts.ui.adorners;
@@ -7,7 +8,6 @@ using mbts.ui.utility;
using mbts.user.settings;
using mbts.utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
@@ -12,6 +12,9 @@
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Modules/Spreader/ExecutionStrategyEditor.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="ScrollViewer">
<Setter Property="u:ScrollViewerCorrector.FixNestedScrolling" Value="False"/>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
@@ -16,7 +16,7 @@ using System.Windows;
using static bts.shell.SyntheticManagementWindowFactory;
namespace mbts.spark {
class SyntheticManagementWindow : SparkWindow<StartupData>, ISlowWindow {
class SyntheticManagementWindow : SparkWindow<StartupData>, ISharedWindow {
const string _window_type = "SyntheticManagement";
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
StartupData _state;
@@ -33,8 +33,20 @@ namespace mbts.spark {
_state = state;
_settings_manager = settings_manager;
Icon.Freeze();
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().synthetic_management ? "SyntheticManagement" : null;
}
public override string window_type => _window_type;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public override StartupData SaveState => _state = _vm.value.create();
public override double MinWidth =>
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public string thread_id { get; }
public void thread_init() {
Log.notnull(_state);
@@ -54,14 +66,15 @@ namespace mbts.spark {
), SparkCommand.create("Clone", Icons.CloneSynthetic, SparkTags.ShowSyntheticCloneTag).complete(
current_row_connectable_builder.create(observe_row_strategy),
rs => rs.HasValue, select_row_strategy_tuple
), SparkCommands.ShowLadder.complete(
), SparkCommands.ShowLadder(SparkCommands.KeyGestures.ShowLadder).complete(
vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ConvertUtils.ValueOrDefault
), SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData {
filters = new OrderBookWindowFactory.FilterStates {
security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security))
}
}
), SparkCommands.window_settings_command(new DialogCommand(() => {
), SparkCommands.ShowOrders.complete(() => {
var ob_settings = bts.shell.orderbook.OrderBookStartupData.Default;
ob_settings.filters = new bts.shell.orderbook.FilterStates {
security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security))
};
return ob_settings;
}), SparkCommands.window_settings_command(new DialogCommand(() => {
var vm = new WindowSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
_settings_manager,
us => us.synthetic_management_grid,
@@ -101,16 +114,6 @@ namespace mbts.spark {
else _view.DataContext = _vm.value;
}
public override string window_type => _window_type;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public override StartupData SaveState => _state = _vm.value.create();
public override double MinWidth =>
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public override void Dispose() {
Disposable.dispose(ref _vm);
Commands.OfType<IDisposable>().DisposeAll();
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,12 +1,10 @@
using mbts.ui.spark;
using bts.shell;
using bts.spark;
using PriceType = bts.spark.ConditionalQuoting.Types.PriceType;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
using bts.shell;
using PriceType = bts.spark.ConditionalQuoting.Types.PriceType;
namespace mbts.spark
{
namespace mbts.spark {
partial class SyntheticEditorVM
{
class ConditionalQuotingPriceTypeModel : RowModelBase<PriceType>
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,5 +1,4 @@
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
using System.Windows;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -0,0 +1,44 @@
using bts.spark;
using mbts.ui.spark;
using System.Collections.Generic;
using System.Windows;
namespace mbts.spark
{
partial class SyntheticEditorVM
{
class QuoteQueueHolderModel : RowModelBase<uint>, IHaveFontParams
{
IDictionary<uint, uint> _value_store = new Dictionary<uint, uint>();
public QuoteQueueHolderModel()
{
}
public override string display_name { get; } = "Queue Holder Length";
public override string help_text { get; } = "How many levels of queue to hold";
public override string man_page { get; } = string.Empty;
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote };
public FontWeight font_weight => FontWeights.SemiBold;
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.NQueueHolderOrders = _value_from_store(leg_tup);
public override uint get_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup).NQueueHolderOrders;
public override void set_value(LegSpreaderParamsTuple leg_tup, uint value)
{
_value_store[leg_tup.SecurityKey] = value;
leg_tup.leg_params.QuoteParams.NQueueHolderOrders = value ;
}
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup) != null;
QuoteParams _get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams;
uint _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => 0u);
}
}
}
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,6 +1,5 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
@@ -1,4 +1,5 @@
using bts.api;
using bts.collections;
using bts.shell;
using bts.spark;
using bts.utils;
@@ -72,7 +73,7 @@ namespace mbts.spark {
_has_working_orders = backing(nameof(has_working_orders), false);
contract_picker_model = new ContractPickerModel(bus, false, null, new SecurityCriteria(new[] {
new SecurityCriteriaClause() {
exchanges = EnumHelper.get_values_except(Venue.Bts).ToLArray()
exchanges = EnumHelper.get_values_except(Venue.Bts).ToArray()
}
}));
contract_picker_model.cancel_action = _close_new_leg_popup;
@@ -199,14 +200,16 @@ namespace mbts.spark {
var post_hedge_opposite_market_based_cross_threshold = new PostHedgeOppositeMarketVolumeBasedCrossThresholdModel();
var post_hedge_opposite_market_based_cross_cross_ticks = new PostHedgeOppositeMarketVolumeBasedCrossCrossTicksModel();
var is_quote_model = new IsQuoteModel();
rows = new IRowModel[] {
_quantity_model,
_price_mult_model,
new IsQuoteModel(),
is_quote_model,
//new EnableHedgeModel(), //TAHA_SPREADER
new HedgeRatioModel(),
//new MinLeanQtyModel(), //TAHA_SPREADER
//new QuoteHiddenModel(),
new QuoteQueueHolderModel(),
new MarketDataTypeModel(),
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
quote_throttle_inside_ticks,
@@ -318,9 +321,12 @@ namespace mbts.spark {
return string.Empty;
}
static IEnumerable<LegSpreaderParamsTuple> _get_legs(Security synthetic, ExecutionStrategyImmutable strategy) =>
synthetic.Synthetic.Legs
.Select(l => new LegSpreaderParamsTuple(l, strategy.LegParamsMap[l.Security.Id].Clone()));
static IEnumerable<LegSpreaderParamsTuple> _get_legs(Security synthetic, ExecutionStrategyImmutable strategy) {
foreach (var l in synthetic.Synthetic.Legs) {
if (strategy.LegParamsMap.TryGetValue(l.Security.Id, out var p))
yield return new LegSpreaderParamsTuple(l, p);
}
}
void _close_new_leg_popup() {
is_new_leg_popup_open = false;
@@ -1,10 +1,9 @@
using bts.api;
using bts.shell;
using bts.collections;
using bts.spark;
using mbts.persist;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
using System;
using System.Collections.Generic;
@@ -169,12 +168,13 @@ namespace mbts.spark {
void _show_ladder() =>
_bus.post(SparkTags.ShowLadderTag, md);
void _show_order_book() =>
_bus.post(SparkTags.ShowOrderBookTag, new OrderBookWindowFactory.OrderBookStartupData {
filters = new OrderBookWindowFactory.FilterStates {
security_criteria = new SecurityCriteria(security)
}
});
void _show_order_book() {
var ob_settings = bts.shell.orderbook.OrderBookStartupData.Default;
ob_settings.filters = new bts.shell.orderbook.FilterStates {
security_criteria = new SecurityCriteria(security)
};
_bus.post(SparkTags.ShowOrderBookTag, ob_settings);
}
}
}
}
@@ -19,8 +19,6 @@
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Controls/Order/AlgoTemplateTreeView.xaml"/>
<ResourceDictionary Source="/view/Controls/Order/AlgoPickerNodeView.xaml"/>
<ResourceDictionary Source="/view/Controls/Order/AlgoContentTemplateSelector.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!--Primitive Constants-->
<sys:Double x:Key="HorizontalPush1">4</sys:Double>
@@ -559,85 +557,27 @@
</Grid>
</RadioButton>
</Grid>
<GroupBox
x:Name="content_host"
<usp:AlgoEditorView
Grid.Row="2"
DataContext="{Binding content_source}"
Background="Transparent"
BorderBrush="{Binding content_source.algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
BorderBrush="{Binding algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
Margin="0,6,0,0"
Padding="12,8"
>
<GroupBox.Style>
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="BorderBrush" Value="Transparent">
<Setter Property="BorderThickness" Value="0"/>
</Trigger>
<Trigger Property="usp:AlgoStudioView.IsMouseOverNodeContent" Value="True">
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
</Style.Triggers>
</Style>
</GroupBox.Style>
<GroupBox.Header>
<ComboBox
Grid.Column="3"
ToolTipService.ShowOnDisabled="True"
SelectedItem="{Binding selected_definition.algo_root}"
ItemsSource="{Binding selected_definition.algos_view}"
Margin="{StaticResource Margin.LP1}"
Width="140"
>
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding content_source}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding selected_definition.templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:True}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding selected_definition.templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:False}">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="ToolTip" Value="Cannot change structure while templates are present."/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
<ComboBox.ItemTemplate>
<DataTemplate>
<usp:OrderTypeItem DisplayName="{Binding display_name}" BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</GroupBox.Header>
<ContentControl
Grid.IsSharedSizeScope="True"
Content="{Binding selected_definition.algo_root}"
ContentTemplateSelector="{StaticResource AlgoTemplateSelector}"
Margin="{StaticResource Margin.TP1}"
>
<ContentControl.Resources>
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}"/>
</ContentControl.Resources>
</ContentControl>
</GroupBox>
<c:DialogSkirt Grid.Row="3" Margin="{StaticResource Margin.TP2}" Background="Transparent">
<c:DialogSkirt.RightItems>
<x:ArrayExtension Type="{x:Type Button}">
<Button ToolTipService.ShowOnDisabled="True" Command="{Binding save_cmd}" CommandParameter="{Binding content_source}" Width="100" Style="{StaticResource {x:Static c:DialogSkirt.DialogSkirtButtonStyleKey}}" ToolTip="{Binding save_cmd_tooltip}" IsDefault="True">
<TextBlock Text="{Binding selected_definition.name, StringFormat='{}Save {0}'}"/>
</Button>
<Button ToolTipService.ShowOnDisabled="True" Command="{Binding revert_cmd}" CommandParameter="{Binding content_source}" Width="100" Style="{StaticResource {x:Static c:DialogSkirt.DialogSkirtButtonStyleKey}}" ToolTip="{Binding revert_cmd_tooltip}">
<TextBlock Text="{Binding selected_definition.name, StringFormat='{}Revert {0}'}"/>
</Button>
</x:ArrayExtension>
</c:DialogSkirt.RightItems>
</c:DialogSkirt>
/>
<StackPanel Grid.Row="3" HorizontalAlignment="Right" Orientation="Horizontal">
<StackPanel.Margin>
<Thickness Left="{StaticResource DialogSkirtPadding.Left}" Top="{StaticResource VerticalPush2}" Right="{StaticResource DialogSkirtButtonPadding.Right}" Bottom="{StaticResource DialogSkirtPadding.Bottom}"/>
</StackPanel.Margin>
<Button ToolTipService.ShowOnDisabled="True" Margin="{StaticResource DialogSkirtButtonMargin}" Command="{Binding save_cmd}" CommandParameter="{Binding content_source}" Width="100" ToolTip="{Binding save_cmd_tooltip}" IsDefault="True">
<TextBlock Text="{Binding selected_definition.name, StringFormat='{}Save {0}'}"/>
</Button>
<Button ToolTipService.ShowOnDisabled="True" Margin="{StaticResource DialogSkirtButtonMargin}" Command="{Binding revert_cmd}" CommandParameter="{Binding content_source}" Width="100" ToolTip="{Binding revert_cmd_tooltip}">
<TextBlock Text="{Binding selected_definition.name, StringFormat='{}Revert {0}'}"/>
</Button>
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</AdornerDecorator>
</UserControl>
</UserControl>
@@ -1,8 +1,6 @@
using mbts.spark;
using mbts.ui.converters;
using mbts.ui.extensions;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
@@ -18,26 +16,11 @@ namespace mbts.ui.spark {
public AlgoStudioView() {
InitializeComponent();
DataContextChanged += (s, e) => set_controller();
content_host.PreviewMouseMove += content_host_PreviewMouseMove;
content_host.MouseEnter += content_host_MouseEnter;
content_host.MouseLeave += content_host_MouseLeave;
set_controller();
// local methods
void set_controller() => Disposable.replace(ref _controller, AlgoStudioViewControllerFactory.create(DataContext, _tree_view, name_text_box, custom_badge_color_picker_button, custom_badge_color_picker_popup));
void content_host_PreviewMouseMove(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
void content_host_MouseEnter(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
void content_host_MouseLeave(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
void refresh_is_mouse_over_content_host(MouseEventArgs e) => content_host.set_current_if_changed(IsMouseOverNodeContentProperty, this.ChildAtPoint<GroupBox>(e.GetPosition(this)) == content_host);
}
#region IsMouseOverNodeContent
public static readonly DependencyProperty IsMouseOverNodeContentProperty =
DependencyProperty.RegisterAttached("IsMouseOverNodeContent", typeof(bool), typeof(AlgoStudioView), new PropertyMetadata(false));
public static bool GetIsMouseOverNodeContent(DependencyObject d) => (bool)d.GetValue(IsMouseOverNodeContentProperty);
public static void SetIsMouseOverNodeContent(DependencyObject d, bool value) => d.SetValue(IsMouseOverNodeContentProperty, value);
#endregion
public new AlgoStudioVM DataContext {
get => base.DataContext as AlgoStudioVM;
set => base.DataContext = value;
@@ -13,7 +13,7 @@ using System.Windows;
using StartupData = bts.shell.AlgoStudioWindowFactory.StartupData;
namespace mbts.spark {
internal class AlgoStudioWindow : SparkWindow<StartupData>, IShellWindow {
internal class AlgoStudioWindow : SparkWindow<StartupData>, ISharedWindow {
public const string
NAME = "Algo Studio",
TemplatesEnabledKey = nameof(TemplatesEnabledKey),
@@ -37,7 +37,7 @@ namespace mbts.spark {
: base(NAME, bus, factory, SparkCommands.ShowAlgoStudio.icon) {
_model = model;
_state = state;
thread_init();
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().algo_studio ? "AlgoStudio" : null;
}
public override StartupData SaveState => _state = _vm.create();
@@ -45,12 +45,12 @@ namespace mbts.spark {
public override string window_type => NAME;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public string thread_id => NAME;
public override double MinWidth =>
SPARK_MENU_COLLAPSED_WIDTH +
LAUNCHER_BUTTON_BORDER_WIDTH +
DEFAULT_CONTENT_WIDTH;
public override double MinHeight => DEFAULT_CONTENT_HEIGHT;
public string thread_id { get; }
bts.config.AlgoStudioOptions _ui_configuration => _model.algo_model.ui_configuration;
@@ -2,6 +2,7 @@
using bts.utils;
using mbts.spark.utilities;
using System;
using System.Windows.Media;
namespace mbts.spark {
struct AlgoData : IEquatable<AlgoData> {
@@ -36,4 +37,31 @@ namespace mbts.spark {
public static bool operator ==(AlgoData a, AlgoData b) => a.Equals(b);
public static bool operator !=(AlgoData a, AlgoData b) => !a.Equals(b);
}
struct AlgoDataVM : IEquatable<AlgoDataVM> {
public readonly AlgoData data;
public readonly AlgoDefinitionMetadata? meta;
public AlgoDataVM(AlgoData data, AlgoDefinitionMetadata? meta = null) {
this.data = data;
this.meta = meta;
}
public string name => data.name;
public Brush badge_fill => OrderUtils.badge_fill(meta, OrderDataHelper.get_metadata(data.value.CustomCase));
public AlgoDisplayGroup group => data.key.display_group();
public override string ToString() =>
data.name;
public static AlgoDataVM from_data(AlgoData data, AlgoDefinitionMetadata? meta) =>
new AlgoDataVM(data, meta);
public static AlgoDataVM from_data((AlgoData algo, AlgoDefinitionMetadata? meta) tup) =>
new AlgoDataVM(tup.algo, tup.meta);
public bool Equals(AlgoDataVM other) => data == other.data && meta == other.meta;
public override bool Equals(object obj) => obj is AlgoDataVM other && Equals(other);
public override int GetHashCode() => EqualityComparer.aggregate_hash(data.GetHashCode(), meta.GetHashCode());
}
}
@@ -1,9 +1,76 @@
using bts.api.orderman;
using bts.persist.algo;
using bts.utils;
using ProtoBuf;
using System;
namespace mbts.spark {
[ProtoContract]
struct AlgoID : IEquatable<AlgoID> {
[ProtoMember(1)]
readonly CustomOrderDefinition.CustomOneofCase? _factory_key;
[ProtoMember(2)]
readonly string _dynamic_key;
[ProtoMember(3)]
readonly uint? _persisted_key;
public AlgoID(CustomOrderDefinition.CustomOneofCase factory_key) {
_factory_key = factory_key;
_dynamic_key = null;
_persisted_key = null;
}
public AlgoID(string dynamic_key) {
_factory_key = null;
_dynamic_key = dynamic_key;
_persisted_key = null;
}
public AlgoID(uint persisted_key) {
_factory_key = null;
_dynamic_key = null;
_persisted_key = persisted_key;
}
public static AlgoID from_order_def(OrderDefinitionImmutable order_def) {
switch (order_def.CustomDef.CustomCase) {
case CustomOrderDefinition.CustomOneofCase.Composite: return new AlgoID(order_def.CustomDef.Composite.AlgoTemplateKey);
case CustomOrderDefinition.CustomOneofCase.Dynamic: return new AlgoID(order_def.CustomDef.Dynamic.DynamicOrderType);
default: return new AlgoID(order_def.CustomDef.CustomCase);
}
}
public static AlgoID from_algo_key(AlgoKey key) {
switch (key.mode) {
case AlgoKey.Mode.Factory:
if (key.factory.structure.structure.Count > 0)
return new AlgoID((CustomOrderDefinition.CustomOneofCase)key.factory.structure.structure[0]);
break;
case AlgoKey.Mode.Persisted:
return new AlgoID(key.persisted.id_uint);
}
return new AlgoID(CustomOrderDefinition.CustomOneofCase.None);
}
public bool Equals(AlgoID other) =>
_factory_key == other._factory_key &&
_dynamic_key == other._dynamic_key &&
_persisted_key == other._persisted_key;
public override bool Equals(object obj) =>
obj is AlgoID && Equals(obj);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(
_factory_key?.GetHashCode() ?? 0,
_dynamic_key?.GetHashCode() ?? 0,
_persisted_key?.GetHashCode() ?? 0);
public static bool operator ==(AlgoID a, AlgoID b) => a.Equals(b);
public static bool operator !=(AlgoID a, AlgoID b) => !a.Equals(b);
}
/// TODO: Remove <see cref="AlgoKey"/>, and redirect references to <see cref="AlgoID"/>.
struct AlgoKey : IEquatable<AlgoKey> {
readonly AlgoPersistedKey? _persisted_key;
readonly AlgoNameStructureKey? _factory_key;
@@ -83,4 +150,4 @@ namespace mbts.spark {
InvalidOperationException _unexpected_mode_ex() => new InvalidOperationException($"Unexpected Mode '{mode}.'");
}
}
}
@@ -1,8 +1,9 @@
using bts.api.orderman;
using bts.utils;
using mbts.spark.utilities;
using ProtoBuf;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark {
@@ -10,12 +11,6 @@ namespace mbts.spark {
public readonly string name;
public readonly AlgoStructureKey structure;
public AlgoNameStructureKey(CustomOrderDefinitionImmutable definition)
: this(definition.CustomCase.type_id(), definition) { }
public AlgoNameStructureKey(string name, CustomOrderDefinitionImmutable definition)
: this(name, new AlgoStructureKey(definition)) { }
public AlgoNameStructureKey(CustomOrderDefinition.CustomOneofCase base_type)
: this(base_type.type_id(), new AlgoStructureKey(base_type)) { }
@@ -31,71 +26,139 @@ namespace mbts.spark {
public static bool operator !=(AlgoNameStructureKey a, AlgoNameStructureKey b) => !a.Equals(b);
}
[ProtoContract]
struct AlgoStructureKey : IEquatable<AlgoStructureKey> {
[ProtoMember(1)]
readonly int[] _structure;
public AlgoStructureKey(CustomOrderDefinition.CustomOneofCase base_type)
: this(new[] { (int)base_type }) { }
public AlgoStructureKey(CustomOrderDefinitionImmutable definition) {
int[] s = new[] { (int)definition.CustomCase };
_build_structure(definition, ref s, 1);
_structure = s;
}
public AlgoStructureKey(CustomOrderDefinitionImmutable definition, IMaskIndexInt32<string> dynamic_type_index) =>
_structure = AlgoStructureSerializer.to_array(definition, dynamic_type_index);
public AlgoStructureKey(int[] structure) =>
_structure = structure;
public CustomOrderDefinitionImmutable build_def() {
int pos = 0;
var mdef = new CustomOrderDefinition();
_build_def(_structure, ref pos, ref mdef);
return new CustomOrderDefinitionImmutable(mdef);
}
public IReadOnlyList<int> structure => _structure ?? Array.Empty<int>();
public bool Equals(AlgoStructureKey other) =>
_structure.Length == other._structure.Length &&
_structure.SequenceEqual(other._structure);
public bool Equals(AlgoStructureKey other) {
if (_structure == null) {
return other._structure == null;
} else {
/// <see cref="Enumerable.SequenceEqual{TSource}(IEnumerable{TSource}, IEnumerable{TSource})"/> is not optimized to check Count/Length, since the args are <see cref="IEnumerable{T}"/>.
return other._structure != null && _structure.Length == other._structure.Length && _structure.SequenceEqual(other._structure);
}
}
public override bool Equals(object obj) =>
obj is AlgoStructureKey other && Equals(other);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(_structure);
_structure == null ? 0 : EqualityComparer.aggregate_hash(_structure);
public static bool operator ==(AlgoStructureKey a, AlgoStructureKey b) => a.Equals(b);
public static bool operator !=(AlgoStructureKey a, AlgoStructureKey b) => !a.Equals(b);
}
static void _build_structure(CustomOrderDefinitionImmutable definition, ref int[] structure, int pos) =>
_build_structure(definition.CloneToMutable(), ref structure, pos);
static class AlgoStructureSerializer {
const int
_dynamic_mask = 1 << 31,
_child_mask_offset = 30;
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, int pos) {
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);
int del_mask = 1 << (31 - di);
structure[pos++] = (int)child_algo.CustomCase | del_mask;
_build_structure(child_algo, ref structure, pos);
break;
}
}
ArrayHelper.trim(ref structure, pos);
public static CustomOrderDefinitionImmutable build_algoi(this AlgoStructureKey structure_key, DynamicOrderHelper dynamic_helper) =>
parsei(structure_key.structure, dynamic_helper);
public static CustomOrderDefinitionImmutable build_algo(this AlgoStructureKey structure_key, DynamicOrderHelper dynamic_helper) =>
parsei(structure_key.structure, dynamic_helper);
public static CustomOrderDefinition.CustomOneofCase order_type(this AlgoStructureKey structure_key) =>
structure_key.structure.Count > 0 ? (CustomOrderDefinition.CustomOneofCase)structure_key.structure[0] : CustomOrderDefinition.CustomOneofCase.None;
public static AlgoStructureKey structure_key(CustomOrderDefinitionImmutable algo, IMaskIndexInt32<string> dynamic_type_index) =>
new AlgoStructureKey(to_array(algo, dynamic_type_index));
public static AlgoStructureKey structure_key(CustomOrderDefinition algo, IMaskIndexInt32<string> dynamic_type_index) =>
new AlgoStructureKey(to_array(algo, dynamic_type_index));
public static int[] to_array(CustomOrderDefinitionImmutable algo, IMaskIndexInt32<string> dynamic_type_index) =>
to_array(algo.CloneToMutable(), dynamic_type_index);
public static int[] to_array(CustomOrderDefinition algo, IMaskIndexInt32<string> dynamic_type_index) {
int[] s = new[] { (int)algo.CustomCase };
_build_structure(algo, ref s, dynamic_type_index);
return s;
}
static void _build_def(int[] structure, ref int pos, ref CustomOrderDefinition algo) {
for (int i = pos; i < structure.Length; i++) {
public static CustomOrderDefinitionImmutable from_array(int[] structure, DynamicOrderHelper dynamic_helper) =>
parsei(structure, dynamic_helper);
public static CustomOrderDefinitionImmutable parsei(IReadOnlyList<int> structure, DynamicOrderHelper dynamic_helper) =>
new CustomOrderDefinitionImmutable(parse(structure, dynamic_helper));
public static CustomOrderDefinition parse(IReadOnlyList<int> structure, DynamicOrderHelper dynamic_helper) {
if (structure == null || structure.Count == 0)
return new CustomOrderDefinition();
int pos = 1;
var mdef = OrderDataHelper.create_algo((CustomOrderDefinition.CustomOneofCase)structure[0]);
_build_def(structure, ref pos, ref mdef, dynamic_helper);
return mdef;
}
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, IMaskIndexInt32<string> dynamic_type_index) {
if (structure == null || structure.Length == 0)
structure = new int[1];
structure[0] = (int)algo.CustomCase;
int p = 1;
_build_structure(algo, ref structure, ref p, dynamic_type_index);
ArrayHelper.trim(ref structure, p);
}
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, ref int pos, IMaskIndexInt32<string> dynamic_type_index) {
IEnumerable<(CustomOrderDefinition child_algo, int field_pos)> children;
if (algo.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic) {
ArrayHelper.ensure_capacity(ref structure, pos + 1);
structure[pos++] = dynamic_type_index.to_int(algo.Dynamic.DynamicOrderType) | _dynamic_mask;
children = algo.Dynamic.OrderParams.Where(kv => kv.Value.ValueCase == DynamicOrderParam.ValueOneofCase.AsOrderDef).Select((kv, i) => (kv.Value.AsOrderDef, i));
} else {
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) {
var type = (CustomOrderDefinition.CustomOneofCase)(structure[i] ^ mask);
var child_algo = OrderDataHelper.Defaults.CustomOrderDefinition(type);
pos++;
_build_def(structure, ref pos, ref child_algo);
cad[di].assign_child(algo, child_algo);
children = cad.Select((c, i) => (c.get_child(algo), i));
children = children.Where(c => c.child_algo != null);
}
foreach ((var child, int field_pos) in children) {
ArrayHelper.ensure_capacity(ref structure, pos + 1);
structure[pos++] = EnumMaskIndexInt32<CustomOrderDefinition.CustomOneofCase>.to_int(child.CustomCase) | _child_algo_mask(field_pos);
_build_structure(child, ref structure, ref pos, dynamic_type_index);
}
}
/// <summary>
/// Gets a mask for a particular child-algo field of a particular algo definition.
/// </summary>
/// <param name="field_pos">The index wrt the deterministic enumeration of child algos for a particular definition (e.g., Hedge = 0, Child = 1).</param>
static int _child_algo_mask(int field_pos) => 1 << (_child_mask_offset - field_pos);
static void _build_def(IReadOnlyList<int> structure, ref int pos, ref CustomOrderDefinition algo, DynamicOrderHelper dynamic_helper) {
var local_algo = algo;
IEnumerable<(Action<CustomOrderDefinition> assign_child, int field_pos)> children;
if (algo.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic) {
var dyn_def = algo.Dynamic;
dyn_def.DynamicOrderType = dynamic_helper.dynamic_type_index.from_int(structure[pos++] ^ _dynamic_mask);
dynamic_helper.add_dynamic_params(ref dyn_def);
children = dyn_def.OrderParams
.Where(kv => kv.Value.ValueCase == DynamicOrderParam.ValueOneofCase.AsOrderDef)
.Select((kv, i) => (new Action<CustomOrderDefinition>(child_algo => kv.Value.AsOrderDef = child_algo), i));
} else {
children = OrderDataHelper.get_child_algo_data(algo)
.Select((c, i) => (new Action<CustomOrderDefinition>(child_algo => c.assign_child(local_algo, child_algo)), i));
}
using (var children_en = children.GetEnumerator()) {
while (pos < structure.Count && children_en.MoveNext()) {
(var assign_child, int field_pos) = children_en.Current;
int mask = _child_algo_mask(field_pos);
if ((structure[pos] & mask) == mask) {
var type = EnumMaskIndexInt32<CustomOrderDefinition.CustomOneofCase>.from_int(structure[pos++] ^ mask);
var child_algo = OrderDataHelper.create_algo(type);
_build_def(structure, ref pos, ref child_algo, dynamic_helper);
assign_child(child_algo);
}
}
}
@@ -70,7 +70,7 @@ namespace mbts.spark {
is_hidden.GetHashCode(),
abbreviation == null ? 0 : abbreviation.GetHashCode(),
badge_color?.GetHashCode() ?? 0,
EqualityComparer.aggregate_hash_limited(_favorite_templates));
_favorite_templates == null ? 0 : EqualityComparer.aggregate_hash_limited(_favorite_templates));
public static bool operator ==(AlgoDefinitionMetadata a, AlgoDefinitionMetadata b) => a.Equals(b);
public static bool operator !=(AlgoDefinitionMetadata a, AlgoDefinitionMetadata b) => !a.Equals(b);
@@ -1,24 +1,37 @@
using bts.api.core;
using bts.api.orderman;
using bts.utils;
using mbts.collections;
using mbts.ui.utilities;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark {
static class AlgoCollectionHelper {
public static readonly IComparer<AlgoDefinitionVM> algo_definition_vm_comparer =
new ComparerBuilder<AlgoDefinitionVM>(
new [] {
Comparer<AlgoDefinitionVM>.Create(_CompareUtils.compare_modes),
new[] {
Comparer<AlgoDefinitionVM>.Create(_CompareUtils.compare_groups),
Comparer<AlgoDefinitionVM>.Create(_CompareUtils.compare_names)
}).to_comparer().to_safe();
public static readonly IComparer<AlgoData> algo_data_comparer =
new ComparerBuilder<AlgoData>(
new[] {
Comparer<AlgoData>.Create(_CompareUtils.compare_groups),
Comparer<AlgoData>.Create(_CompareUtils.compare_names)
}
).to_comparer();
public static readonly IComparer<AlgoDataVM> algo_data_vm_comparer =
algo_data_comparer.wrap<AlgoData, AlgoDataVM>(vm => vm.data);
public static readonly IComparer<IAlgoVM> algo_vm_comparer =
new ComparerBuilder<IAlgoVM>(
new [] {
Comparer<IAlgoVM>.Create(_CompareUtils.compare_modes),
new[] {
Comparer<IAlgoVM>.Create(_CompareUtils.compare_groups),
Comparer<IAlgoVM>.Create(_CompareUtils.compare_names)
}).to_comparer().to_safe();
@@ -34,29 +47,75 @@ namespace mbts.spark {
}
}
static class _CompareUtils {
public static int compare_modes(AlgoDefinitionVM a, AlgoDefinitionVM b) =>
_compare_modes(a.key.mode, b.key.mode);
public static void update_persisted_definition_wrappers(IDictionary<AlgoKey, IAlgoVM> target, TableUpdate<AlgoData, AlgoKey> update, Func<AlgoData, AlgoDefinitionVM> factory) {
foreach (var d in update.removed)
target.Remove(d.key);
foreach (var d in update.changed) {
var vm = target[d.key];
if (vm is AlgoVMWrapper wrapper) {
wrapper.value.load(d);
} else vm.load_from(d.value);
}
foreach (var d in update.added) {
if (target.TryGetValue(d.key, out IAlgoVM def)) {
def.load_from(d.value);
} else target.Add(d.key, new AlgoVMWrapper(factory(d)));
}
}
public static int compare_modes(IAlgoVM a, IAlgoVM b) =>
_compare_modes(a.id.mode, b.id.mode);
public static void commit_all(this IEnumerable<AlgoParamRowVM> algo_param_rows) =>
algo_param_rows.Select(r => r.param).commit_all();
public static void commit_all(this IEnumerable<IAlgoParamVM> algo_params) {
foreach (var p in algo_params)
p.mark_committed();
}
public static void revert_all(this IEnumerable<AlgoParamRowVM> algo_param_rows) =>
algo_param_rows.Select(r => r.param).revert_all();
public static void revert_all(this IEnumerable<IAlgoParamVM> algo_params) {
foreach (var p in algo_params)
p.revert();
}
public static AlgoDisplayGroup display_group(CustomOrderDefinition.CustomOneofCase order_type) => order_type.is_bts_algo_type() ? AlgoDisplayGroup.BTS : AlgoDisplayGroup.Exchange;
public static AlgoDisplayGroup display_group(this AlgoKey key) {
switch (key.mode) {
case AlgoKey.Mode.Factory: return display_group(key.factory.structure.order_type());
case AlgoKey.Mode.Unpersisted:
case AlgoKey.Mode.Persisted:
return AlgoDisplayGroup.Studio;
}
return AlgoDisplayGroup.BTS;
}
static class _CompareUtils {
static AlgoDisplayGroup[] _sorted_groups = new[] {
AlgoDisplayGroup.Exchange,
AlgoDisplayGroup.BTS,
AlgoDisplayGroup.Studio,
AlgoDisplayGroup.API
};
public static int compare_groups(AlgoDefinitionVM a, AlgoDefinitionVM b) =>
Array.IndexOf(_sorted_groups, a.group).CompareTo(Array.IndexOf(_sorted_groups, b.group));
public static int compare_groups(AlgoData a, AlgoData b) =>
Array.IndexOf(_sorted_groups, a.key.display_group()).CompareTo(Array.IndexOf(_sorted_groups, b.key.display_group()));
public static int compare_groups(IAlgoVM a, IAlgoVM b) =>
Array.IndexOf(_sorted_groups, a.group).CompareTo(Array.IndexOf(_sorted_groups, b.group));
public static int compare_names(AlgoDefinitionVM a, AlgoDefinitionVM b) =>
DisplayNameHelper.compare_alpha_numeric(a.name, b.name);
public static int compare_names(AlgoData a, AlgoData b) =>
DisplayNameHelper.compare_alpha_numeric(a.name, b.name);
public static int compare_names(IAlgoVM a, IAlgoVM b) =>
DisplayNameHelper.compare_alpha_numeric(a.display_name, b.display_name);
static int _compare_modes(AlgoKey.Mode a, AlgoKey.Mode b) {
// Factory mode is 'lowest', consider all other modes equivalent.
if (a == AlgoKey.Mode.Factory) {
if (b != AlgoKey.Mode.Factory)
return -1;
} else if (b == AlgoKey.Mode.Factory) {
return 1;
}
return 0;
}
}
}
}
}
@@ -1,10 +1,12 @@
using bts.api.core;
using bts.api.orderman;
using bts.collections;
using bts.proto;
using bts.utils;
using mbts.collections;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.controls.color_picker;
using mbts.ui.shell;
using mbts.ui.utility;
@@ -25,7 +27,8 @@ namespace mbts.spark {
None = 0,
Exchange = 1,
Spreader = 1 << 1,
DefaultPlaceholder = 1 << 2
Attachable = 1 << 2,
DefaultPlaceholder = 1 << 3
}
class AlgoDefinitionVM : DisposableViewModelBase, IAlgoStudioContentVM, IAlgoSelector, IEquatable<AlgoDefinitionVM> {
@@ -46,7 +49,8 @@ namespace mbts.spark {
readonly DeferrableActionWrapper _refresh_on_local_changes;
readonly ReactiveThresholdMonitor<AlgoDefinitionTemplateVM> _any_templates_dirty_monitor = new ReactiveThresholdMonitor<AlgoDefinitionTemplateVM>(t => t.observe_is_dirty());
readonly Property<AlgoFormats> _selected_format;
AlgoEditingPermissions _editing_permissions;
readonly EnumMaskInt32<AlgoEditingPermissions> _permissions_mask = new EnumMaskInt32<AlgoEditingPermissions>();
int? _favorite_index;
Brush _badge_fill;
Snapshot? _committed;
@@ -54,29 +58,43 @@ namespace mbts.spark {
TableUpdateBuffer<AlgoTemplateData, AlgoKey> _template_update_buffer;
bool _connected;
public static AlgoDefinitionVM create(OrdersModel model, AlgoData data, bool connect = false, AlgoFormats init_format = AlgoFormats.Exchange) {
var rv = new AlgoDefinitionVM(model, data, init_format);
public static AlgoDefinitionVM create(OrdersModel model, AlgoData data, bool connect = false, AlgoFormats init_format = AlgoFormats.Exchange, AlgoEditingPermissions editing_permissions = (AlgoEditingPermissions)int.MaxValue) {
var rv = new AlgoDefinitionVM(model, data, init_format, editing_permissions);
if (connect)
rv.connect();
return rv;
}
public static AlgoDefinitionVM create_placeholder(OrdersModel model, CustomOrderDefinitionImmutable def, string name, AlgoFormats init_format) =>
new AlgoDefinitionVM(model, new AlgoData(def, name, new AlgoKey(Guid.NewGuid())), init_format);
new AlgoDefinitionVM(model, new AlgoData(def, name, new AlgoKey(Guid.NewGuid())), init_format, (AlgoEditingPermissions)int.MaxValue);
AlgoDefinitionVM(OrdersModel model, AlgoData def_data, AlgoFormats init_format = AlgoFormats.Exchange) {
AlgoDefinitionVM(OrdersModel model, AlgoData def_data, AlgoFormats init_format, AlgoEditingPermissions init_permissions) {
// init core data
_model = model;
_permissions_mask.mask = init_permissions;
_permissions_mask.raw = init_permissions;
var resources = new CompositeDisposable();
if (!model.algo_model.ui_configuration.can_modify_definition_values)
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditValues | AlgoEditingPermissions.AllowSaveValues);
if (!model.algo_model.ui_configuration.can_modify_definition_structure)
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure);
else if (model.algo_model.templates_enabled()) {
resources.Add(_templates.Subscribe((s, e) => {
if (_templates.Count == 0) {
editing_permissions_mask |= AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure;
} else {
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure);
}
}));
}
key = def_data.key;
algos_view = ObservableAutoWrapper.create_sorted_wrapper(_algo_roots, AlgoCollectionHelper.algo_vm_comparer);
_template_update_buffer = new TableUpdateBuffer<AlgoTemplateData, AlgoKey>(t => t.key);
_templates_with_custom = new ObservableAppendCollection<AlgoDefinitionTemplateVM>(templates);
templates_view = (ListCollectionView)CollectionViewSource.GetDefaultView(_templates_with_custom);
custom_badge_color_palettes.AddRange(ColorPalette.all_presets(model.factory).Select(p => new ColorPaletteVM(p)));
_editing_permissions = _get_permissions();
_version = new VersionContainer<Snapshot>(_snapshot);
var conf = _model.algo_model.ui_configuration;
var resources = new CompositeDisposable();
// init backing props
_name = backing(nameof(name), def_data.name);
_abbreviation = backing(nameof(abbreviation), string.Empty);
@@ -87,7 +105,7 @@ namespace mbts.spark {
_is_selected = backing(nameof(is_selected), true);
_is_dirty = backing(nameof(is_dirty), () => key.mode == AlgoKey.Mode.Unpersisted || !_committed.HasValue || !_committed.Value.Equals(_version.value));
var factory_def_data = def_data.key.mode == AlgoKey.Mode.Factory ? def_data : new AlgoData(def_data.value);
_algo_root = backing(nameof(algo_root), _algo_roots.GetOrAdd(factory_def_data.key, _ => OrderTypeVMFactory.create(factory_def_data, _editing_permissions)));
_algo_root = backing(nameof(algo_root), _algo_roots.GetOrAdd(factory_def_data.key, _ => OrderTypeVMFactory.create(factory_def_data)));
_visible_algo_root = backing<IAlgoVM>(nameof(visible_algo_root));
_can_redo = backing(nameof(can_redo), () => _edit_stack.get_can_redo(_version.value));
_can_undo = backing(nameof(can_undo), () => _edit_stack.get_can_undo(_version.value));
@@ -113,28 +131,16 @@ namespace mbts.spark {
as_demuxer().subscribe_many(_refresh_on_local_changes.execute, nameof(this.name), nameof(abbreviation), nameof(badge_fill)),
_edit_pane_vs_model.add_source(VisualStateNames.BadgeSelector.GroupName, custom_badge_color.observe_nullable(true).Select(t => t.HasValue ? VisualStateNames.BadgeSelector.Active : VisualStateNames.BadgeSelector.Inactive)),
_edit_pane_vs_model.add_source(VisualStateNames.Abbreviation.GroupName, _abbreviation.observe_current(true).Select(a => string.IsNullOrEmpty(a) ? VisualStateNames.Abbreviation.Unpopulated : VisualStateNames.Abbreviation.Populated)),
templates.ObserveSelf().Select(_ => _get_permissions()).DistinctUntilChanged().OnDispatcher().Subscribe(on_editing_permissions),
_algo_root.observe_current(true).SubscribeHookLatest(vm => {
if (vm == null)
return Disposable.empty;
if (vm is ILazyAlgo la) {
bool synced_with_loaded = false;
return vm.subscribe_definition().Subscribe(_ => {
if (la.is_loaded) {
if (!synced_with_loaded) {
synced_with_loaded = true;
_algo_root.observe_current(true).SubscribeEach(vm =>
vm == null ? null :
Disposable.Create(
vm.subscribe_is_dirty().Subscribe(v => {
if (!v) {
_version.increment_version();
_on_committed_changed();
} else {
_refresh_on_local_changes.execute();
}
} else {
synced_with_loaded = false;
}
});
}
return vm.subscribe_definition().Subscribe(_ => _refresh_on_local_changes.execute());
}),
}),
vm.subscribe_definition().Subscribe(_ => _refresh_on_local_changes.execute()))),
ObservableAutoWrapper.connect_item_hooks(algo_roots, a => a as IDisposable ?? Disposable.empty, true),
ObservableAutoWrapper.connect_item_hooks(templates, t => new CompositeDisposable(_any_templates_dirty_monitor.join(t), t), true),
Disposable.Create(() => {
@@ -152,16 +158,23 @@ namespace mbts.spark {
_edit_pane_vs_model.set_state(VisualStateNames.Name.GroupName, VisualStateNames.Name.Inactive);
break;
}
resources.Add(_selected_format.observe_current(true).SubscribeHookLatest(f => {
using (_refresh_on_local_changes.defer_execution()) {
var algo_data = model.factory.default_algos(f);
var default_algo = new AlgoKey(OrderDataHelper.get_default_algo(f));
return this.load_algo_data_recursive(algo_data, default_algo);
}
}));
var permission_src = _permissions_mask.observe_result();
resources.Add(_selected_format.observe_current(true).CombineLatest(permission_src, (fmt, perms) => (fmt, perms))
.SubscribeEach(t => {
if ((t.perms & AlgoEditingPermissions.AllowEditStructure) != 0) {
using (_refresh_on_local_changes.defer_execution()) {
var algo_data = model.factory.default_algos(t.fmt);
var default_algo = new AlgoKey(OrderDataHelper.get_default_algo(t.fmt));
return this.load_algo_data_recursive(algo_data, default_algo);
}
}
return Disposable.empty;
}));
resources.Add(permission_src.Subscribe(_ => RaisePropertyChanged(nameof(editing_permissions))));
resources.Add(this.connect_data(permission_src));
resources.Add(this.connect_data(Observable.Return(model)));
resources.Add(_algo_root.Subscribe(_ => RaisePropertyChanged(nameof(IAlgoSelector.selected_algo), nameof(IAlgoParamVM.value), nameof(group))));
_dispose = resources;
on_editing_permissions(_get_permissions());
await algo_root.load_from_async(def_data.value);
_version.increment_version();
_edit_stack.reset(_version.value);
@@ -169,11 +182,6 @@ namespace mbts.spark {
}
}
// local methods
void on_editing_permissions(AlgoEditingPermissions eps) {
_editing_permissions = eps;
foreach (var vm in algo_roots)
vm.set_permissions(eps);
}
void on_can_redo_changed(bool oldval, bool newval) => can_redo_changed?.Invoke();
void on_can_undo_changed(bool oldval, bool newval) => can_undo_changed?.Invoke();
void on_local_changed() {
@@ -194,6 +202,7 @@ namespace mbts.spark {
public string name { get => _name.Value; set => _name.set(value); }
public string abbreviation { get => _abbreviation.Value; set => _abbreviation.set(value); }
public ObservableCollection<IAlgoVM> algo_roots => _algo_roots;
public IDictionary<AlgoKey, IAlgoVM> algo_roots_index => _algo_roots;
public IReadOnlyObservableCollection<IAlgoVM> algos_view { get; }
public IAlgoVM algo_root { get => _algo_root.Value; set => _algo_root.set(value); }
public IAlgoVM visible_algo_root => _visible_algo_root.Value;
@@ -210,6 +219,20 @@ namespace mbts.spark {
public bool is_selected { get => _is_selected.Value; set => _is_selected.set(value); }
public bool is_dirty => _is_dirty.Value;
public bool is_factory_definition => key.mode == AlgoKey.Mode.Factory;
public AlgoDisplayGroup group {
get {
switch (key.mode) {
case AlgoKey.Mode.Factory:
if (algo_root != null)
return algo_root.group;
break;
case AlgoKey.Mode.Unpersisted:
case AlgoKey.Mode.Persisted:
return AlgoDisplayGroup.Studio;
}
return AlgoDisplayGroup.BTS;
}
}
public bool can_redo => _can_redo.Value;
public bool can_undo => _can_undo.Value;
public EnabledValueVM<Color> custom_badge_color { get; } = new EnabledValueVM<Color>();
@@ -230,13 +253,51 @@ namespace mbts.spark {
// visual states
public ObservableCollection<string> edit_pane_visual_states => _edit_pane_vs_model.states;
public ObservableCollection<string> tree_view_visual_states => _tree_view_vs_model.states;
// editing permissions
public AlgoEditingPermissions editing_permissions_mask { get => _permissions_mask.mask; set => _permissions_mask.mask = value; }
public AlgoEditingPermissions editing_permissions => _permissions_mask.result;
#region IAlgoSelector
event PropertyChange<IAlgoVM> IAlgoSelector.selected_algo_changed { add => algo_root_changed += value; remove => algo_root_changed -= value; }
event PropertyChange<IAlgoVM> IAlgoSelector.selected_algo_changed {
add => algo_root_changed += value;
remove => algo_root_changed -= value;
}
event PropertyChange<IAlgoVM> IAlgoParamVM<IAlgoVM>.value_changed {
add => algo_root_changed += value;
remove => algo_root_changed -= value;
}
event PropertyChange<AlgoParamPermissions> IAlgoParamVM.permissions_changed { add { } remove { } }
event Action<bool> IAlgoParamVM.is_dirty_changed { add { } remove { } }
string IAlgoParamVM.id => key.ToString();
string IAlgoParamVM.display_name => "Algo";
ObservableKeyedCollection<IAlgoVM, AlgoKey> IAlgoSelector.algos => _algo_roots;
IAlgoVM IAlgoSelector.selected_algo { get => algo_root; set => algo_root = value; }
IAlgoVM IAlgoSelector.selected_algo {
get => algo_root;
set => algo_root = value;
}
IAlgoVM IAlgoParamVM<IAlgoVM>.value {
get => algo_root;
set => algo_root = value;
}
bool IAlgoParamVM.has_value => _algo_root != null;
bool IAlgoParamVM.is_enabled { get; set; } = true;
object IAlgoParamVM.value {
get => algo_root;
set => algo_root = (IAlgoVM)value;
}
AlgoParamPermissions _permissions = AlgoParamPermissions.ReadWrite;
AlgoParamPermissions IAlgoParamVM.permissions { get => _permissions; set => _permissions = value; }
void IAlgoSelector.load_algo_data(IReadOnlyDictionary<AlgoKey, AlgoData> algo_data, AlgoKey? default_algo) =>
_load_algo_data(algo_data, default_algo);
_load_algo_data(algo_data, default_algo);
void IAlgoParamVM.revert() { }
void IAlgoParamVM.mark_committed() { }
IObservable<IAlgoVM> IAlgoSelector.subscribe_selected_algo() =>
this.observe_algo_root();
#endregion
#region IObserver (algo data...)
@@ -248,7 +309,7 @@ namespace mbts.spark {
void _load_algo_data(IReadOnlyDictionary<AlgoKey, AlgoData> algo_data, AlgoKey? default_algo) {
var orig_algo = algo_root?.id;
_algo_roots.SyncWithDictionary(algo_data, d => OrderTypeVMFactory.create(d, _editing_permissions, true));
_algo_roots.SyncWithDictionary(algo_data, d => OrderTypeVMFactory.create(d, is_expanded: true));
if (!try_select(orig_algo) && !try_select(default_algo))
algo_root = algo_roots.FirstOrDefault();
//
@@ -298,6 +359,47 @@ namespace mbts.spark {
public IDisposable buffer_updates() => _template_update_buffer.buffer_updates();
public void load_def(CustomOrderDefinitionImmutable def) {
var key = OrderDataHelper.get_shallow_factory_key(def);
if (algo_root?.id == key) {
algo_root.load_from(def);
} else if (_algo_roots.TryGetValue(key, out IAlgoVM vm)) {
vm.load_from(def);
algo_root = vm;
} else {
algo_roots.Add(vm = _mk_algo_root(def));
algo_root = vm;
}
}
public void load_many(IEnumerable<CustomOrderDefinitionImmutable> defs) {
/// don't load composite algos; we don't support recursive composites (<see cref="OrderUtils.actual(CustomOrderDefinitionImmutable)"/>)
if (defs is IList<CustomOrderDefinitionImmutable> def_list) {
def_list = def_list.WrapSelector(d => d.actual(), null);
} else {
def_list = defs.Select(d => d.actual()).ToList();
}
switch (def_list.Count) {
case 0: algo_root = null; break;
case 1: load_def(def_list[0]); break;
default:
var def = def_list[0];
var key = OrderDataHelper.get_shallow_factory_key(def);
if (algo_root?.id == key) {
algo_root.load_many(def_list);
} else if (_algo_roots.TryGetValue(key, out IAlgoVM vm)) {
vm.load_many(def_list);
algo_root = vm;
} else {
OrderTypeVMFactory.create(key, def.display_name(), def.CustomCase, is_expanded: true);
algo_roots.Add(vm = _mk_algo_root(def));
vm.load_many(def_list);
algo_root = vm;
}
break;
}
}
public void load(AlgoData data) {
_assert_can_load(data.key);
if (algo_root != null)
@@ -309,7 +411,7 @@ namespace mbts.spark {
// pass a copy of the persisted state - rather than use local values - because if something goes wrong, it is better to be in sync with data fresh from the server/api.
public AlgoDefinitionVM to_persisted(AlgoData def) =>
new AlgoDefinitionVM(_model, def);
new AlgoDefinitionVM(_model, def, selected_format, editing_permissions_mask);
public AlgoData create_algo_def() =>
new AlgoData(algo_root.definition, name, key);
@@ -322,16 +424,6 @@ namespace mbts.spark {
public override int GetHashCode() => key.GetHashCode();
public override string ToString() => algo_root == null ? $"!:{name}" : $"{algo_root.order_type}:{name}";
AlgoEditingPermissions _get_permissions() {
var permissions = AlgoEditingPermissions.None;
if (_model.algo_model.ui_configuration.can_modify_definition_values) {
permissions |= AlgoEditingPermissions.AllowEditValues | AlgoEditingPermissions.AllowSaveValues;
}
if (_model.algo_model.ui_configuration.can_modify_definition_structure || (_model.algo_model.templates_enabled() && _templates.Count == 0))
permissions |= AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure;
return permissions;
}
void _assert_can_load(AlgoKey data_key) {
if (key.mode != data_key.mode)
throw new InvalidOperationException($"{typeof(AlgoDefinitionVM)} '{nameof(key)}.{nameof(AlgoKey.mode)}' must be {data_key.mode} (actually {key.mode}), to load specified data.");
@@ -359,10 +451,14 @@ namespace mbts.spark {
_committed = _version.value;
_is_dirty.refresh();
_refresh_undo_redo();
RaisePropertyChanged(nameof(committed_value), nameof(current_value));
}
public Snapshot? committed_value => _committed;
public Snapshot current_value => _version.value;
IAlgoVM _mk_algo_root(CustomOrderDefinitionImmutable definition) =>
OrderTypeVMFactory.create(new AlgoData(definition), editing_permissions: _editing_permissions, true);
OrderTypeVMFactory.create(new AlgoData(definition), is_expanded: true);
void _on_metadata(AlgoDefinitionMetadata metadata) {
using (_refresh_on_local_changes.supress_execution()) {
@@ -463,5 +559,17 @@ namespace mbts.spark {
public static bool operator ==(Snapshot a, Snapshot b) => a.Equals(b);
public static bool operator !=(Snapshot a, Snapshot b) => !a.Equals(b);
}
struct SnapshotTemplateEqualityComparer : IEqualityComparer<Snapshot> {
public bool Equals(Snapshot x, Snapshot y) =>
x.abbreviation == y.abbreviation &&
x.custom_badge_color == y.custom_badge_color &&
x.format == y.format &&
x.data.key == y.data.key &&
x.data.name == y.data.name &&
x.data.value.Equals(y.data.value);
public int GetHashCode(Snapshot obj) => throw new NotImplementedException();
}
}
}
@@ -0,0 +1,99 @@
using bts.utils;
using mbts.collections;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace mbts.spark {
class AlgoParamRowVM : DisposableViewModelBase, DIYGrid.IRowVM, IEditableObject, ITemplateSelectorKey, IEquatable<AlgoParamRowVM> {
bool _is_enabled;
string _template_key;
ICommand _set_value_cmd;
public AlgoParamRowVM(IAlgoVM algo, IAlgoParamVM param, AlgoParamRowVM parent, string template_key = null) {
this.param = param;
this.algo = algo;
this.parent = parent;
_template_key = template_key;
param.permissions_changed += on_permissions_changed;
_dispose = Disposable.Create(() => param.permissions_changed -= on_permissions_changed);
void on_permissions_changed(AlgoParamPermissions oldval, AlgoParamPermissions newval) => RaisePropertyChanged(nameof(permissions));
}
public event Action invalidated;
public IAlgoVM algo { get; set; }
public IAlgoParamVM param { get; }
public AlgoParamPermissions permissions => param.permissions;
public AlgoParamRowVM parent { get; }
public int level => parent == null ? 0 : parent.level + 1;
public string path_disambiguation {
get {
if (parent == null)
return string.Empty;
string pd = parent.path_disambiguation;
if (parent.algo.order_type == bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Cover) {
if (pd.Length > 0)
pd += ".";
pd += parent.param.display_name;
}
return pd;
}
}
public bool is_enabled { get => _is_enabled; set => Set(ref _is_enabled, value); }
public ICommand set_value_cmd {
get {
if (_set_value_cmd == null) {
var cmd = new DelegateCommand(v => {
var orig_val = param.value;
try {
param.value = v;
} catch (InvalidCastException) {
param.value = orig_val;
}
}, _ => param.permissions == AlgoParamPermissions.ReadWrite);
concated_disposable(param.subscribe_permissions().Subscribe(_ => cmd.RaiseCanExecuteChanged()));
_set_value_cmd = cmd;
}
return _set_value_cmd;
}
}
string ITemplateSelectorKey.key => _template_key;
public void raise_invalidated() => invalidated?.Invoke();
public bool Equals(AlgoParamRowVM other) =>
other != null &&
param.id == other.param.id &&
parent.safe_equals(other.parent);
public override bool Equals(object obj) =>
Equals(obj as AlgoParamRowVM);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(param.id.GetHashCode(), parent?.GetHashCode() ?? 0);
#region IEditableObject
object _committed_value;
void IEditableObject.BeginEdit() => _committed_value = param.value;
void IEditableObject.EndEdit() => _committed_value = null;
void IEditableObject.CancelEdit() {
if (_committed_value != null)
param.value = _committed_value;
_committed_value = null;
}
#endregion
}
class AlgoSelectorParamRowVM : AlgoParamRowVM {
readonly IAlgoSelector _selector;
public AlgoSelectorParamRowVM(IAlgoVM algo_vm, IAlgoSelector selector, AlgoParamRowVM parent) : base(algo_vm, selector, parent) =>
_selector = selector;
public ObservableKeyedCollection<IAlgoVM, AlgoKey> algos => _selector.algos;
}
}
@@ -0,0 +1,215 @@
using bts.api;
using bts.api.orderman;
using bts.collections;
using bts.spark;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
namespace mbts.spark {
class AlgoVMWrapper : DisposableViewModelBase, IAlgoVM,
IObserver<SecurityMD?>,
IObserver<SecurityImmutable?>,
IObserver<OrdersModel>,
IObserver<AlgoEditingPermissions> {
static readonly HashSet<string>
_algo_root_echo_props = new HashSet<string> {
nameof(is_expanded),
nameof(editing_permissions),
nameof(definition),
nameof(display_name)
};
static readonly IReadOnlyDictionary<string, string>
_value_echo_props = new Dictionary<string, string> {
{ nameof(AlgoDefinitionVM.name), nameof(display_name) },
{ nameof(AlgoDefinitionVM.abbreviation), nameof(abbreviation) },
{ nameof(AlgoDefinitionVM.badge_fill), nameof(meta) }
};
readonly ObservableAggregateCollection<IAlgoSelector> _child_algo_selectors = new ObservableAggregateCollection<IAlgoSelector>();
readonly ObservableAggregateCollection<IAlgoParamVM> _algo_params = new ObservableAggregateCollection<IAlgoParamVM>();
readonly DisposableContainer _value_hooks = new DisposableContainer();
Action _definition_changed = () => { };
_ObservedData? _data_cache;
AlgoDefinitionVM _value;
public AlgoVMWrapper(AlgoDefinitionVM algo_definition_vm) {
value = algo_definition_vm;
_dispose = new CompositeDisposable(_value_hooks) {
_child_algo_selectors.clear_collections,
_algo_params.clear_collections,
() => _value?.Dispose()
};
}
public event Action definition_changed { add => _definition_changed += value; remove => _definition_changed -= value; }
public AlgoDefinitionVM value {
get => _value;
set {
if (_value != value) {
_value?.Dispose();
_value = value;
_value_hooks.replace(_set_hooks(value));
RaisePropertyChanged(nameof(value));
}
}
}
public AlgoKey id => _value.key;
public CustomOrderDefinition.CustomOneofCase order_type => _algo_root?.order_type ?? CustomOrderDefinition.CustomOneofCase.None;
public OrderMetadata meta {
get {
if (_value != null) {
if (_value.custom_badge_color.enabled)
return new OrderMetadata { Color = value.custom_badge_color.value.rgb_color(), KeyBinding = value.abbreviation.if_null_or_empty(value.name) };
else if (_value.algo_root != null)
return _value.algo_root.meta;
}
return OrderDataHelper.get_metadata(order_type);
}
}
public AlgoEditingPermissions editing_permissions => _value?.editing_permissions ?? AlgoEditingPermissions.None;
public bool can_expand => _algo_root?.can_expand ?? false;
public bool is_expanded {
get => _value.algo_root?.is_expanded ?? false;
set {
if (_value != null)
_value.is_expanded = value;
}
}
public string display_name => _value?.name ?? string.Empty;
public string abbreviation => _value?.abbreviation;
public AlgoDisplayGroup group => _value?.group ?? AlgoDisplayGroup.BTS;
public CustomOrderDefinitionImmutable definition => _algo_root?.definition ?? new CustomOrderDefinitionImmutable(new CustomOrderDefinition());
public ObservableCollection<IAlgoSelector> child_algo_selectors => _child_algo_selectors;
[EditorBrowsable(EditorBrowsableState.Never)]
public ObservableCollection<IAlgoSelector> child_algo_selectors_view {
get {
_value?.algo_root?.ensure_init();
return _child_algo_selectors;
}
}
public IReadOnlyObservableCollection<IAlgoParamVM> algo_params => _algo_params;
[EditorBrowsable(EditorBrowsableState.Never)]
public dynamic children_vm => _algo_root?.children_vm;
IAlgoVM _algo_root => _value?.algo_root;
public void assign_to(CustomOrderDefinition value) => _algo_root?.assign_to(value);
public bool key_match(KeyEventArgs e) => _algo_root?.key_match(e) ?? false;
public void load_from(CustomOrderDefinitionImmutable value) => _algo_root?.load_from(value);
public void load_many(IEnumerable<CustomOrderDefinitionImmutable> values) => _algo_root?.load_many(values);
public Task<CustomOrderDefinitionImmutable?> load_from_async(CustomOrderDefinitionImmutable definition) => _algo_root is IAlgoVM vm ? vm.load_from_async(definition) : Task.FromResult<CustomOrderDefinitionImmutable?>(null);
void IAlgoVM.ensure_init() => _value?.algo_root?.ensure_init();
IDisposable _set_hooks(AlgoDefinitionVM vm) {
var algo_root_subs = new DisposableContainer();
vm.PropertyChanged += on_value_prop_changed;
return new CompositeDisposable(
vm.observe_algo_root().Subscribe(r => {
algo_root_subs.replace(null);
_child_algo_selectors.clear_collections();
_algo_params.clear_collections();
if (r != null) {
if (_data_cache.HasValue) {
_data_cache.Value.forward_to(r);
}
_child_algo_selectors.add_collection(r.child_algo_selectors);
_algo_params.add_collection(r.algo_params);
((INotifyPropertyChanged)r).PropertyChanged += on_algo_root_prop_changed;
algo_root_subs.replace(new CompositeDisposable(
r.subscribe_definition().Subscribe(_ => _definition_changed?.Invoke())) {
() => ((INotifyPropertyChanged)r).PropertyChanged -= on_algo_root_prop_changed
});
}
RaisePropertyChanged(nameof(group));
}),
algo_root_subs) {
() => vm.PropertyChanged -= on_value_prop_changed
};
void on_algo_root_prop_changed(object sender, PropertyChangedEventArgs e) {
if (_algo_root_echo_props.Contains(e.PropertyName))
RaisePropertyChanged(e.PropertyName);
}
void on_value_prop_changed(object sender, PropertyChangedEventArgs e) {
if (_value_echo_props.TryGetValue(e.PropertyName, out string wrapper_prop))
RaisePropertyChanged(wrapper_prop);
}
}
// permissions
void IObserver<AlgoEditingPermissions>.OnNext(AlgoEditingPermissions value) {
_data_cache = _data_cache?.with(value) ?? new _ObservedData(value, default, default, default);
(_algo_root as IObserver<AlgoEditingPermissions>)?.OnNext(value);
}
void IObserver<AlgoEditingPermissions>.OnError(Exception error) { }
void IObserver<AlgoEditingPermissions>.OnCompleted() { }
// model
void IObserver<OrdersModel>.OnNext(OrdersModel value) {
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, value, default, default);
(_algo_root as IObserver<OrdersModel>)?.OnNext(value);
}
void IObserver<OrdersModel>.OnError(Exception error) { }
void IObserver<OrdersModel>.OnCompleted() { }
// security
void IObserver<SecurityImmutable?>.OnNext(SecurityImmutable? value) {
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, default, value, default);
(_algo_root as IObserver<SecurityImmutable?>)?.OnNext(value);
}
void IObserver<SecurityImmutable?>.OnError(Exception error) { }
void IObserver<SecurityImmutable?>.OnCompleted() { }
// security md
void IObserver<SecurityMD?>.OnNext(SecurityMD? value) {
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, default, default, value);
(_algo_root as IObserver<SecurityMD?>)?.OnNext(value);
}
void IObserver<SecurityMD?>.OnError(Exception error) { }
void IObserver<SecurityMD?>.OnCompleted() { }
struct _ObservedData {
public readonly AlgoEditingPermissions permissions;
public readonly OrdersModel model;
public readonly SecurityImmutable? security;
public readonly SecurityMD? security_md;
public _ObservedData(AlgoEditingPermissions permissions, OrdersModel model, SecurityImmutable? security, SecurityMD? security_md) {
this.permissions = permissions;
this.model = model;
this.security = security;
this.security_md = security_md;
}
public void forward_to(IAlgoVM recipient) {
(recipient as IObserver<AlgoEditingPermissions>)?.OnNext(permissions);
if (model != null)
(recipient as IObserver<OrdersModel>)?.OnNext(model);
if (security_md.HasValue)
(recipient as IObserver<SecurityMD?>)?.OnNext(security_md);
if (security.HasValue)
(recipient as IObserver<SecurityImmutable?>)?.OnNext(security);
}
public _ObservedData with(AlgoEditingPermissions? permissions) =>
new _ObservedData(permissions ?? this.permissions, model, security, security_md);
public _ObservedData with(OrdersModel model) =>
new _ObservedData(permissions, model, security, security_md);
public _ObservedData with(SecurityImmutable? security) =>
new _ObservedData(permissions, model, security, security_md);
public _ObservedData with(SecurityMD? security_md) =>
new _ObservedData(permissions, model, security, security_md);
}
}
}
@@ -0,0 +1,622 @@
<UserControl
x:Name="me"
x:Class="mbts.ui.spark.AlgoEditorGridView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sp="clr-namespace:mbts.spark"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:usp="clr-namespace:mbts.ui.spark"
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Resources/DataGrid.xaml"/>
<ResourceDictionary Source="/Modules/Spreader/ExecutionStrategyEditor.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!--Primitive Constants-->
<sys:Double x:Key="HorizontalPush1">4</sys:Double>
<sys:Double x:Key="HorizontalPush2">8</sys:Double>
<sys:Double x:Key="HorizontalPush3">12</sys:Double>
<sys:Double x:Key="VerticalPush1">4</sys:Double>
<sys:Double x:Key="VerticalPush2">8</sys:Double>
<sys:Double x:Key="VerticalPush3">12</sys:Double>
<sys:String x:Key="NonUniformValueText">-</sys:String>
<Thickness x:Key="Margin.LP1" Left="{StaticResource HorizontalPush1}"/>
<Thickness x:Key="Margin.LP2" Left="{StaticResource HorizontalPush2}"/>
<Thickness x:Key="Margin.LP3" Left="{StaticResource HorizontalPush3}"/>
<Thickness x:Key="Margin.TP1" Top="{StaticResource VerticalPush1}"/>
<Thickness x:Key="Margin.TP2" Top="{StaticResource VerticalPush2}"/>
<Thickness x:Key="ComboBox.CellPadding" Top="2" Bottom="2"/>
<!--Converters-->
<uspcv:OrderTypeBrushConverter x:Key="OrderTypeBrushConverter" DefaultBrush="Transparent"/>
<cv:TimeConverter x:Key="TimeConverter"/>
<cv:GridLengthConverter x:Key="GridLengthPixelConverter" Multiplier="18" UnitType="Pixel"/>
<cv:EnumItemsSourceConverter x:Key="EnumItemsSourceConverter"/>
<cv:NullSubstitutionConverterWrapper x:Key="SimpleValueTextConverter" ValueForNull="{StaticResource NonUniformValueText}"/>
<cv:NullSubstitutionConverterWrapper x:Key="NullableEnumDescriptionConverter" Converter="{StaticResource EnumDescriptionConverter}" ValueForNull="{StaticResource NonUniformValueText}"/>
<cv:NullSubstitutionConverterWrapper x:Key="QuantityMeasureTextConverter" Converter="{x:Static cv:ValueConverters.QuantityMeasureTextConverter}" ValueForNull="{StaticResource NonUniformValueText}"/>
<cv:DoubleTextConverter x:Key="DoubleTextConverter" NaNText="{StaticResource NonUniformValueText}"/>
<cv:ValueSubstitutionConverterWrapper x:Key="StringTextConverter">
<cv:KeyValue Key="{x:Null}" Value="{StaticResource NonUniformValueText}"/>
<cv:KeyValue Key="{x:Static sys:String.Empty}" Value="{StaticResource NonUniformValueText}"/>
</cv:ValueSubstitutionConverterWrapper>
<cv:EnumHasFlagsConverter x:Key="ParamPermissionsToIsReadOnlyConverter">
<sp:AlgoParamPermissions>None</sp:AlgoParamPermissions>
<sp:AlgoParamPermissions>ReadOnly</sp:AlgoParamPermissions>
</cv:EnumHasFlagsConverter>
<!--Default Styles-->
<Style TargetType="uag:AlgoPickerNodeView" BasedOn="{StaticResource {x:Type uag:AlgoPickerNodeView}}">
<Setter Property="DropDownStyleKey" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorView}, Converter={x:Static usp:AlgoContentTemplateSelector.AlgoDropDownStyleKeyConverter}}"/>
</Style>
<!--Common Templates-->
<DataTemplate x:Key="AlgoVMItemTemplate">
<usp:OrderTypeItem DisplayName="{Binding display_name}" BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}"/>
</DataTemplate>
<DataTemplate x:Key="ExecutionStrategyTemplate">
<TextBlock>
<Hyperlink Click="_on_execution_strategy_toggle_click">
Show Strategy Params
</Hyperlink>
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="IndentedParamNameAndBadgeCellTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding level, Converter={StaticResource GridLengthPixelConverter}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<usp:OrderTypeItem Grid.Column="1" Focusable="False" IsTabStop="False" BadgeFill="{Binding Path='(sp:AlgoParamRowVM.algo).(sp:IAlgoVM.meta).Color', Converter={StaticResource ColorToBrushConverter}}" DisplayName="{Binding Path='param.(sp:IAlgoParamVM.display_name)'}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="NullableEnumDescriptionTemplate">
<TextBlock Text="{Binding Converter={StaticResource NullableEnumDescriptionConverter}}"/>
</DataTemplate>
<DataTemplate x:Key="ParamNameAndDisambiguationCellTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="disambig_text" Margin="0,0,2,0" FontWeight="Light" Text="{Binding path_disambiguation, StringFormat='[{0}]', Mode=OneWay}"/>
<TextBlock Text="{Binding Path='param.(sp:IAlgoParamVM.display_name)', Mode=OneWay}"/>
<TextBlock Text="*" Visibility="{Binding param.is_dirty, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding path_disambiguation}" Value="{x:Static sys:String.Empty}">
<Setter TargetName="disambig_text" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="SimpleValueTextTemplate">
<TextBlock x:Name="value_text" Text="{Binding param.value, Converter={StaticResource SimpleValueTextConverter}}"/>
</DataTemplate>
<!--Param Name Templates-->
<u:KeyedTemplateSelector x:Key="ParamNameTemplateSelector" Default="{StaticResource ParamNameAndDisambiguationCellTemplate}">
<u:TemplateSelectorKeyValue Key="{x:Static usp:AlgoParamTemplateKeys.Price}">
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="price_size_col0"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="price_size_col1"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="disambig_text" Margin="0,0,2,0" FontWeight="Light" Text="{Binding path_disambiguation, StringFormat='[{0}]', Mode=OneWay}"/>
<TextBlock Text="{Binding Path='param.(sp:IAlgoParamVM.display_name)', Mode=OneWay}"/>
<TextBlock Text="*" Visibility="{Binding param.is_dirty, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
<TextBlock x:Name="bid_text" Grid.Column="1" HorizontalAlignment="Center">
<TextBlock.ToolTip>
<TextBlock>
Best Bid Price (click to assign)
<LineBreak/>
<Italic>
Hold a number key to cross the market by 'n' ticks.
</Italic>
</TextBlock>
</TextBlock.ToolTip>
<Hyperlink
Focusable="False"
Foreground="{u:BTSColor MarketData.bid}"
Command="{Binding DataContext.payup_bid_cmd, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
PreviewMouseLeftButtonDown="{x:Static usp:AlgoEditorGridView.PayupTicksMouseButtonHandler}"
>
<Run>
<Run.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="DataContext.bbo.bid" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
<Binding Path="DataContext.spec" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
</MultiBinding>
</Run.Text>
</Run>
</Hyperlink>
</TextBlock>
<TextBlock x:Name="ask_text" Grid.Column="2" HorizontalAlignment="Center" Margin="4,0">
<TextBlock.ToolTip>
<TextBlock>
Best Ask Price (click to assign)
<LineBreak/>
<Italic>
Hold a number key to cross the market by 'n' ticks.
</Italic>
</TextBlock>
</TextBlock.ToolTip>
<Hyperlink
Focusable="False"
Foreground="{u:BTSColor MarketData.ask}"
Command="{Binding DataContext.payup_ask_cmd, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
PreviewMouseLeftButtonDown="{x:Static usp:AlgoEditorGridView.PayupTicksMouseButtonHandler}"
>
<Run>
<Run.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="DataContext.bbo.ask" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
<Binding Path="DataContext.spec" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
</MultiBinding>
</Run.Text>
</Run>
</Hyperlink>
</TextBlock>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding path_disambiguation}" Value="{x:Static sys:String.Empty}">
<Setter TargetName="disambig_text" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding DataContext.security, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}" Value="{x:Null}">
<Setter TargetName="bid_text" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="ask_text" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding param.permissions, Converter={StaticResource ParamPermissionsToIsReadOnlyConverter}}" Value="{u:True}">
<Setter TargetName="bid_text" Property="ToolTip" Value="Best Bid Price"/>
<Setter TargetName="ask_text" Property="ToolTip" Value="Best Ask Price"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="{x:Static usp:AlgoParamTemplateKeys.Size}">
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="price_size_col0"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="price_size_col1"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="disambig_text" Margin="0,0,2,0" FontWeight="Light" Text="{Binding path_disambiguation, StringFormat='[{0}]', Mode=OneWay}"/>
<TextBlock Text="{Binding Path='param.(sp:IAlgoParamVM.display_name)', Mode=OneWay}"/>
<TextBlock Text="*" Visibility="{Binding param.is_dirty, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
<TextBlock
x:Name="bid_text"
Grid.Column="1"
HorizontalAlignment="Center"
Foreground="{u:BTSColor MarketData.bid}"
Text="{Binding DataContext.bbo.bid_size, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
ToolTip="Best Bid Size"
/>
<TextBlock
x:Name="ask_text"
Grid.Column="2"
HorizontalAlignment="Center"
Margin="4,0"
Foreground="{u:BTSColor MarketData.ask}"
Text="{Binding DataContext.bbo.ask_size, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
ToolTip="Best Ask Size"
/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding path_disambiguation}" Value="{x:Static sys:String.Empty}">
<Setter TargetName="disambig_text" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding DataContext.security, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}" Value="{x:Null}">
<Setter TargetName="bid_text" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="ask_text" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</u:KeyedTemplateSelector>
<!--Read Templates-->
<usp:AlgoParamTemplateSelector
x:Key="ParamTemplateSelector"
IntTemplate="{StaticResource SimpleValueTextTemplate}"
NullableIntTemplate="{StaticResource SimpleValueTextTemplate}"
UIntTemplate="{StaticResource SimpleValueTextTemplate}"
NullableUIntTemplate="{StaticResource SimpleValueTextTemplate}"
ExecutionStrategyTemplate="{StaticResource ExecutionStrategyTemplate}"
>
<usp:AlgoParamTemplateSelector.KeyedTemplates>
<u:TemplateSelectorKeyValue Key="{x:Static usp:AlgoParamTemplateKeys.Price}">
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}" Mode="OneWay">
<Binding Path="param.value"/>
<Binding Path="DataContext.spec" RelativeSource="{RelativeSource AncestorType=usp:AlgoEditorGridView}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</usp:AlgoParamTemplateSelector.KeyedTemplates>
<!--Bool-->
<usp:AlgoParamTemplateSelector.BoolTemplate>
<DataTemplate>
<CheckBox Focusable="False" VerticalAlignment="Center" IsChecked="{Binding param.value, Mode=TwoWay}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.BoolTemplate>
<!--Double-->
<usp:AlgoParamTemplateSelector.DoubleTemplate>
<DataTemplate>
<TextBlock Text="{Binding param.value, Converter={StaticResource DoubleTextConverter}}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.DoubleTemplate>
<!--Enum-->
<usp:AlgoParamTemplateSelector.EnumTemplate>
<DataTemplate>
<TextBlock Text="{Binding param.value, Converter={StaticResource EnumDescriptionConverter}}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.EnumTemplate>
<!--Nullable Enum-->
<usp:AlgoParamTemplateSelector.NullableEnumTemplate>
<DataTemplate>
<TextBlock Text="{Binding param.value, Converter={StaticResource NullableEnumDescriptionConverter}}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.NullableEnumTemplate>
<!--Order Def-->
<usp:AlgoParamTemplateSelector.OrderDefTemplate>
<DataTemplate>
<usp:OrderTypeItem BadgeFill="{Binding Path='(sp:AlgoParamRowVM.param).(sp:IAlgoSelector.selected_algo).meta.Color', Converter={StaticResource ColorToBrushConverter}}" DisplayName="{Binding Path='(sp:AlgoParamRowVM.param).(sp:IAlgoSelector.selected_algo).(sp:IAlgoVM.display_name)'}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.OrderDefTemplate>
<!--Quantity Measure-->
<usp:AlgoParamTemplateSelector.QuantityMeasureTemplate>
<DataTemplate>
<TextBlock Text="{Binding param.value, Converter={StaticResource QuantityMeasureTextConverter}}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.QuantityMeasureTemplate>
<!--String-->
<usp:AlgoParamTemplateSelector.StringTemplate>
<DataTemplate>
<TextBlock Text="{Binding param.value, Converter={StaticResource StringTextConverter}}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.StringTemplate>
<!--Timestamp-->
<usp:AlgoParamTemplateSelector.TimestampTemplate>
<DataTemplate>
<TextBlock Text="{Binding param.value, Converter={StaticResource TimeConverter}}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.TimestampTemplate>
</usp:AlgoParamTemplateSelector>
<!--Write Templates-->
<usp:AlgoParamTemplateSelector x:Key="ParamEditingTemplateSelector" ExecutionStrategyTemplate="{StaticResource ExecutionStrategyTemplate}">
<usp:AlgoParamTemplateSelector.KeyedTemplates>
<u:TemplateSelectorKeyValue Key="{x:Static usp:AlgoParamTemplateKeys.Price}">
<DataTemplate>
<usp:PriceEntryControl
Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}"
ContractSpec="{Binding DataContext.spec, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
Focusable="False"
IsTabStop="False"
ArrowKeyTickModifiers="Ctrl"
>
<usp:PriceEntryControl.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</usp:PriceEntryControl.CommandBindings>
<usp:PriceEntryControl.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</usp:PriceEntryControl.InputBindings>
</usp:PriceEntryControl>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</usp:AlgoParamTemplateSelector.KeyedTemplates>
<!--Bool-->
<usp:AlgoParamTemplateSelector.BoolTemplate>
<DataTemplate>
<CheckBox Focusable="False" VerticalAlignment="Center" IsChecked="{Binding param.value, Mode=TwoWay}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.BoolTemplate>
<!--Double-->
<usp:AlgoParamTemplateSelector.DoubleTemplate>
<DataTemplate>
<c:TickUpDownDouble x:Name="tick_up_down" Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
<c:TickUpDownDouble.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</c:TickUpDownDouble.CommandBindings>
<c:TickUpDownDouble.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</c:TickUpDownDouble.InputBindings>
</c:TickUpDownDouble>
</DataTemplate>
</usp:AlgoParamTemplateSelector.DoubleTemplate>
<!--Enum-->
<usp:AlgoParamTemplateSelector.EnumTemplate>
<DataTemplate>
<ComboBox
HorizontalAlignment="Stretch"
ItemsSource="{Binding param.value, Converter={StaticResource EnumItemsSourceConverter}, Mode=OneTime}"
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
SelectedItem="{Binding param.value, UpdateSourceTrigger=PropertyChanged}"
/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.EnumTemplate>
<!--Int-->
<usp:AlgoParamTemplateSelector.IntTemplate>
<DataTemplate>
<c:TickUpDownInt Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
<c:TickUpDownInt.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</c:TickUpDownInt.CommandBindings>
<c:TickUpDownInt.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</c:TickUpDownInt.InputBindings>
</c:TickUpDownInt>
</DataTemplate>
</usp:AlgoParamTemplateSelector.IntTemplate>
<!--Nullable Bool-->
<usp:AlgoParamTemplateSelector.NullableBoolTemplate>
<DataTemplate>
<CheckBox Focusable="False" VerticalAlignment="Center" IsThreeState="True" IsChecked="{Binding param.value, Mode=TwoWay}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.NullableBoolTemplate>
<!--Nullable Enum-->
<usp:AlgoParamTemplateSelector.NullableEnumTemplate>
<DataTemplate>
<ComboBox
HorizontalAlignment="Stretch"
ItemsSource="{Binding Converter={x:Static usp:AlgoEditorGridView.NullableEnumParamRowItemsSourceConverter}}"
ItemTemplate="{StaticResource NullableEnumDescriptionTemplate}"
SelectedItem="{Binding param.value, UpdateSourceTrigger=PropertyChanged}"
/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.NullableEnumTemplate>
<!--Nullable Int-->
<usp:AlgoParamTemplateSelector.NullableIntTemplate>
<DataTemplate>
<c:TickUpDownNullableInt Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
<c:TickUpDownNullableInt.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</c:TickUpDownNullableInt.CommandBindings>
<c:TickUpDownNullableInt.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</c:TickUpDownNullableInt.InputBindings>
</c:TickUpDownNullableInt>
</DataTemplate>
</usp:AlgoParamTemplateSelector.NullableIntTemplate>
<!--Nullable UInt-->
<usp:AlgoParamTemplateSelector.NullableUIntTemplate>
<DataTemplate>
<c:TickUpDownNullableUInt Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
<c:TickUpDownNullableUInt.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</c:TickUpDownNullableUInt.CommandBindings>
<c:TickUpDownNullableUInt.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</c:TickUpDownNullableUInt.InputBindings>
</c:TickUpDownNullableUInt>
</DataTemplate>
</usp:AlgoParamTemplateSelector.NullableUIntTemplate>
<!--Order Def-->
<usp:AlgoParamTemplateSelector.OrderDefTemplate>
<DataTemplate>
<ComboBox HorizontalAlignment="Stretch" DataContext="{Binding param}" ItemsSource="{Binding Path='(sp:IAlgoSelector.algos)'}" ItemTemplate="{StaticResource AlgoVMItemTemplate}" SelectedItem="{Binding Path='(sp:IAlgoSelector.selected_algo)', UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</usp:AlgoParamTemplateSelector.OrderDefTemplate>
<!--QuantityMeasure-->
<usp:AlgoParamTemplateSelector.QuantityMeasureTemplate>
<DataTemplate>
<uag:QuantityMeasureControl Focusable="False" HorizontalAlignment="Stretch" QuantityMeasure="{Binding param.value, UpdateSourceTrigger=PropertyChanged}" ArrowKeyTickModifiers="Ctrl">
<uag:QuantityMeasureControl.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</uag:QuantityMeasureControl.CommandBindings>
<uag:QuantityMeasureControl.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</uag:QuantityMeasureControl.InputBindings>
</uag:QuantityMeasureControl>
</DataTemplate>
</usp:AlgoParamTemplateSelector.QuantityMeasureTemplate>
<!--Timestamp-->
<usp:AlgoParamTemplateSelector.TimestampTemplate>
<DataTemplate>
<Grid DataContext="{Binding algo}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ComboBox
x:Name="PART_StartMode_ComboBox"
HorizontalAlignment="Stretch"
ItemsSource="{Binding start_modes}"
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
SelectedItem="{Binding start_mode}"
/>
<c:DateTimePicker
x:Name="PART_AbsoluteStart_DateTimePicker"
Grid.Column="2"
TimeTuple="{Binding absolute_start}"
Visibility="{Binding show_absolute_start, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
<c:TimeSpanControl
x:Name="PART_RelativeStart_TimeSpanControl"
Grid.Column="2"
TimeSpan="{Binding relative_start, Mode=TwoWay}"
Visibility="{Binding show_relative_start, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
</Grid>
</DataTemplate>
</usp:AlgoParamTemplateSelector.TimestampTemplate>
<!--UInt-->
<usp:AlgoParamTemplateSelector.UIntTemplate>
<DataTemplate>
<c:TickUpDownUInt Focusable="False" HorizontalAlignment="Stretch" Value="{Binding param.value, UpdateSourceTrigger=PropertyChanged}">
<c:TickUpDownUInt.CommandBindings>
<CommandBinding Command="{x:Static u:NavigationHelper.MoveFocusCommand}" Executed="{x:Static u:NavigationHelper.KeyboardMoveFocusExecuted}"/>
</c:TickUpDownUInt.CommandBindings>
<c:TickUpDownUInt.InputBindings>
<KeyBinding Key="Up" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Up}"/>
<KeyBinding Key="Down" Command="{x:Static u:NavigationHelper.MoveFocusCommand}" CommandParameter="{x:Static FocusNavigationDirection.Down}"/>
</c:TickUpDownUInt.InputBindings>
</c:TickUpDownUInt>
</DataTemplate>
</usp:AlgoParamTemplateSelector.UIntTemplate>
</usp:AlgoParamTemplateSelector>
<usp:AlgoEditorGridRowStyleSelector x:Key="RowStyleSelector">
<usp:AlgoEditorGridRowStyleSelector.DefaultStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding param.is_dirty}" Value="{u:True}"/>
<Condition Binding="{Binding AnimateDirty, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}" Value="{u:True}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#8098FB98"/>
<Setter Property="ToolTip" Value="Value has been modified."/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</usp:AlgoEditorGridRowStyleSelector.DefaultStyle>
<usp:AlgoEditorGridRowStyleSelector.SpreaderStyle>
<Style TargetType="DataGridRow">
<Setter Property="DetailsVisibility" Value="Collapsed"/>
<Setter Property="DetailsTemplate">
<Setter.Value>
<DataTemplate>
<AdornerDecorator>
<usp:ExecutionStrategyEditor
DataContext="{Binding algo.strategy_editor}"
HorizontalAlignment="Stretch"
Margin="6"
SyntheticName="{Binding name}"
TickFactors="{Binding tick_factors}"
IsNew="{Binding is_new}"
CanUserAddNewLeg="{Binding can_add_leg}"
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
PriceCalc="{Binding price_calc}"
ParameterRows="{Binding rows}"
LegTuples="{Binding leg_tuples}"
RemoveLegCommand="{Binding remove_leg}"
ContractPickerModel="{Binding contract_picker_model}"
/>
</AdornerDecorator>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</usp:AlgoEditorGridRowStyleSelector.SpreaderStyle>
</usp:AlgoEditorGridRowStyleSelector>
</ResourceDictionary>
</UserControl.Resources>
<u:StateManager.Groups>
<u:StateGroupsManager>
<u:StateDefinitionCollection>
<u:StateDefinition State="{Binding IsReadOnly, Source={u:DirectReference me}, Converter={x:Static usp:AlgoEditorGridView.GeneralVisualStateConverter}}"/>
<u:StateDefinition State="{Binding CanUserChangeAlgoRoot, Source={u:DirectReference me}, Converter={x:Static usp:AlgoEditorGridView.AlgoRootVisualStateConverter}}"/>
</u:StateDefinitionCollection>
</u:StateGroupsManager>
</u:StateManager.Groups>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.General}}">
<VisualState Name="{x:Static Normal, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.General}}"/>
<VisualState Name="{x:Static ReadOnly, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.General}}">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="_param_grid" Storyboard.TargetProperty="(DataGrid.IsReadOnly)">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
<ColorAnimation Storyboard.TargetName="_param_grid" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="Gray"/>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="root_algo_combo_box" Storyboard.TargetProperty="(Control.IsEnabled)">
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.AlgoRoot}}">
<VisualState Name="{x:Static Inactive, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.AlgoRoot}}"/>
<VisualState Name="{x:Static Active, MemberType={u:Nested usp:AlgoEditorGridView.VisualStateNames.AlgoRoot}}">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="root_algo_label" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="root_algo_combo_box" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ThicknessAnimation Storyboard.TargetName="_param_grid" Storyboard.TargetProperty="(Control.Margin)" BeginTime="0" Duration="0" To="0,4,0,0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="root_algo_label" VerticalAlignment="Center" Margin="{StaticResource Margin.LP2}" Visibility="Collapsed">Algo</TextBlock>
<ComboBox
x:Name="root_algo_combo_box"
Margin="40,0,0,0"
ItemsSource="{Binding AlgoRootsViewSource.View, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
SelectedItem="{Binding SelectedAlgoRoot, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
Visibility="Collapsed"
>
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ComboBox.DelineatedGroupContainerStyle}"/>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<usp:OrderTypeItem BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}" DisplayName="{Binding display_name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<DataGrid
x:Name="_param_grid"
Grid.Row="1"
Grid.IsSharedSizeScope="True"
VirtualizingPanel.ScrollUnit="Pixel"
AlternatingRowBackground="{x:Null}"
IsReadOnly="False"
Margin="0,2,0,0"
MinRowHeight="18"
ColumnHeaderHeight="0"
ItemsSource="{Binding AlgoParamRowsViewSource.View, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorGridView}}"
SelectionMode="Single"
RowStyleSelector="{StaticResource RowStyleSelector}"
>
<DataGrid.Columns>
<DataGridTemplateColumn IsReadOnly="True" Header="Param" CellTemplateSelector="{StaticResource ParamNameTemplateSelector}" MinWidth="120" Width="*">
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
<Setter Property="IsTabStop" Value="False"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Value" CellTemplateSelector="{StaticResource ParamTemplateSelector}" CellEditingTemplateSelector="{StaticResource ParamEditingTemplateSelector}" MinWidth="120" Width="*">
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsReadOnly, RelativeSource={RelativeSource AncestorType=DataGrid}}" Value="{u:False}">
<Setter Property="ToolTip" Value="Click to edit"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
@@ -0,0 +1,207 @@
using bts.collections;
using bts.utils;
using mbts.spark;
using mbts.ui.controls;
using mbts.ui.converters;
using mbts.ui.utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
namespace mbts.ui.spark {
partial class AlgoEditorGridView : UserControl {
public static readonly IValueConverter
GeneralVisualStateConverter = ValueConverter.create<bool, string>(is_read_only => is_read_only ? VisualStateNames.General.ReadOnly : VisualStateNames.General.Normal),
AlgoRootVisualStateConverter = ValueConverter.create<bool, string>(show_bbo => show_bbo ? VisualStateNames.AlgoRoot.Active : VisualStateNames.AlgoRoot.Inactive),
AlgoRootComboBoxMarginConverter = ValueConverter.create<double, Thickness>(name_col_width => new Thickness(name_col_width + 8, 0, 0, 0)),
NullableEnumParamRowItemsSourceConverter = ValueConverter.create<AlgoParamRowVM, IEnumerable>(param_row => {
if (param_row?.param != null) {
var tenum = param_row.param.GetType().GetInterfaceGenericArgument(AlgoParamTemplateSelector.generic_algo_param_itype);
if (tenum.IsNullable()) {
// .First() should be safe, since we know this is a Nullable<T>.
tenum = tenum.GetGenericArguments().First();
}
if (tenum.IsEnum)
return Enum.GetValues(tenum);
}
return null;
});
public static readonly MouseButtonEventHandler PayupTicksMouseButtonHandler = (s, e) => {
if ((s as Hyperlink)?.Command is ICommand cmd) {
cmd.ExecuteIfCan(KeyboardExtensions.GetPressedNumber() ?? 0);
e.Handled = true;
}
};
readonly ObservableAggregateCollection<AlgoParamRowVM> _param_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
public AlgoEditorGridView() {
InitializeComponent();
_set_value_column_width();
// AlgoRoots
AlgoRootsViewSource = new CollectionViewSource<_AlgoRootsCollectionView>();
BindingOperations.SetBinding(AlgoRootsViewSource, CollectionViewSource.SourceProperty, new Binding(nameof(AlgoRoots)) { Source = this });
AlgoRootsViewSource.GroupDescriptions.Add(new PropertyGroupDescription(nameof(IAlgoVM.group)));
// AlgoParamRows
AlgoParamRowsViewSource = new CollectionViewSource<ListCollectionView<AlgoParamRowVM>>();
BindingOperations.SetBinding(AlgoParamRowsViewSource, CollectionViewSource.SourceProperty, new Binding(nameof(AlgoParamRows)) { Source = this });
AlgoParamRowsViewSource.IsLiveFilteringRequested = true;
AlgoParamRowsViewSource.LiveFilteringProperties.Add(nameof(AlgoParamRowVM.permissions));
AlgoParamRowsViewSource.Filter += param_rows_filter;
DataGridEditHelper.set_hooks(this);
void param_rows_filter(object sender, FilterEventArgs e) =>
e.Accepted = e.Item is AlgoParamRowVM row && row.param.permissions != AlgoParamPermissions.None;
}
public enum Columns { ParamName, Value }
event Action CommitEditRequested, CancelEditRequested;
public CollectionViewSource AlgoRootsViewSource { get; }
public CollectionViewSource<ListCollectionView<AlgoParamRowVM>> AlgoParamRowsViewSource { get; }
bool _stretch_value_column = true;
public bool StretchValueColumn {
get => _stretch_value_column;
set {
if (_stretch_value_column != value) {
_stretch_value_column = value;
_set_value_column_width();
}
}
}
#region AnimateDirtyParams
public static readonly DependencyProperty AnimateDirtyProperty =
WPFHelper.create_dp<bool, AlgoEditorGridView>(nameof(AnimateDirty), true);
public bool AnimateDirty {
get => (bool)GetValue(AnimateDirtyProperty);
set => SetValue(AnimateDirtyProperty, value);
}
#endregion
#region IsReadOnly
public static readonly DependencyProperty IsReadOnlyProperty =
WPFHelper.create_dp<bool, AlgoEditorGridView>(nameof(IsReadOnly));
public bool IsReadOnly {
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
#endregion
#region CanUserChangeAlgoRoot
public static readonly DependencyProperty CanUserChangeAlgoRootProperty =
WPFHelper.create_dp<bool, AlgoEditorGridView>(nameof(CanUserChangeAlgoRoot));
public bool CanUserChangeAlgoRoot {
get => (bool)GetValue(CanUserChangeAlgoRootProperty);
set => SetValue(CanUserChangeAlgoRootProperty, value);
}
#endregion
#region AlgoRoots
public static readonly DependencyProperty AlgoRootsProperty =
WPFHelper.create_dp<IReadOnlyObservableCollection<IAlgoVM>, AlgoEditorGridView>(nameof(AlgoRoots));
public IReadOnlyObservableCollection<IAlgoVM> AlgoRoots {
get => GetValue(AlgoRootsProperty) as IReadOnlyObservableCollection<IAlgoVM>;
set => SetValue(AlgoRootsProperty, value);
}
#endregion
#region SelectedAlgoRoot
public static readonly DependencyProperty SelectedAlgoRootProperty =
WPFHelper.create_dp<IAlgoVM, AlgoEditorGridView>(nameof(SelectedAlgoRoot), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
public IAlgoVM SelectedAlgoRoot {
get => GetValue(SelectedAlgoRootProperty) as IAlgoVM;
set => SetValue(SelectedAlgoRootProperty, value);
}
#endregion
#region AlgoParamRows
public static readonly DependencyProperty AlgoParamRowsProperty =
WPFHelper.create_dp<IReadOnlyObservableCollection<AlgoParamRowVM>, AlgoEditorGridView>(nameof(AlgoParamRows));
public IReadOnlyObservableCollection<AlgoParamRowVM> AlgoParamRows {
get => GetValue(AlgoParamRowsProperty) as IReadOnlyObservableCollection<AlgoParamRowVM>;
set => SetValue(AlgoParamRowsProperty, value);
}
#endregion
public static AlgoEditorGridView create_attach_view() =>
new AlgoEditorGridView {
Margin = new Thickness(6),
CanUserChangeAlgoRoot = true,
};
public void commit_edit() => CommitEditRequested?.Invoke();
public void cancel_edit() => CancelEditRequested?.Invoke();
void _set_value_column_width() {
foreach (var c in _param_grid?.Columns.empty_if_null())
c.Width = _stretch_value_column ? new DataGridLength(1, DataGridLengthUnitType.Star) : DataGridLength.Auto;
}
void _on_execution_strategy_toggle_click(object sender, RoutedEventArgs e) {
if (sender is Hyperlink hl && hl.try_find_ancestor_of_type(out DataGridRow row_view)) {
Visibility vis; string text;
if (row_view.DetailsVisibility == Visibility.Visible) {
vis = Visibility.Collapsed;
text = "Show Strategy Params";
} else {
vis = Visibility.Visible;
text = "Hide Strategy Params";
}
hl.Inlines.Clear();
hl.Inlines.Add(text);
row_view.SetCurrentValue(DataGridRow.DetailsVisibilityProperty, vis);
}
}
public static class VisualStateNames {
public static class General {
public const string GroupName = nameof(General);
public static readonly string
Normal = $"{GroupName}{nameof(Normal)}",
ReadOnly = $"{GroupName}{nameof(ReadOnly)}";
}
public static class AlgoRoot {
public const string GroupName = nameof(AlgoRoot);
public static readonly string
Active = $"{GroupName}{VisualStates.Active}",
Inactive = $"{GroupName}{VisualStates.Inactive}";
}
}
class _AlgoRootsCollectionView : ListCollectionView<IAlgoVM> {
public _AlgoRootsCollectionView(IList<IAlgoVM> list) : base(list) =>
CustomSort = AlgoCollectionHelper.algo_vm_comparer.to_non_generic();
}
}
class AlgoEditorGridRowStyleSelector : StyleSelector {
public Style DefaultStyle { get; set; }
public Style SpreaderStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container) {
if (item is AlgoParamRowVM row) {
if (row.param.id == nameof(bts.api.orderman.SpreaderOrderDefinition.ExecutionStrategy))
return SpreaderStyle;
}
return DefaultStyle;
}
}
}
@@ -0,0 +1,88 @@
using bts.collections;
using mbts.ui.controls;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using System.Reactive.Disposables;
namespace mbts.spark {
static class AlgoParamRowsHelper {
public static IDisposable connect(AlgoDefinitionVM algo, ObservableAggregateCollection<AlgoParamRowVM> rows, bool include_algo_type_rows) =>
algo == null ? null : new CompositeDisposable(algo.observe_algo_root().SubscribeEach(a => {
rows.clear_collections();
if (a == null)
return Disposable.empty;
var child_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
/// Note: the order of operations here matters
/// <see cref="_wrap_rows(IAlgoVM, AlgoParamRowVM, ObservableAggregateCollection{AlgoParamRowVM}, bool)" /> has the potential to trigger a lot of CollectionChanged events (depending on how deep the algo-tree is).
/// There is no need to burden the aggregate 'rows' with echoing all of these events, if the child-row collection is finished changing when it is added to the aggregate.
var hook = _wrap_rows(a, null, child_rows, include_algo_type_rows);
rows.add_collection(child_rows.AsReadOnly());
return hook;
})) {
rows.clear_collections
};
public static IDisposable negate_is_dirty(IReadOnlyObservableCollection<AlgoParamRowVM> rows) {
return ObservableAutoWrapper.connect_item_hooks(rows, hook);
IDisposable hook(AlgoParamRowVM r) {
r.param.is_dirty_changed += negate;
return Disposable.Create(unhook);
void negate(bool is_dirty) {
if (is_dirty)
r.param.mark_committed();
}
void unhook() =>
r.param.is_dirty_changed -= negate;
}
}
static IDisposable _wrap_rows(AlgoDefinitionVM algo_def, ObservableAggregateCollection<AlgoParamRowVM> rows, bool include_algo_type_rows) {
AlgoParamRowVM root = null;
ObservableAggregateCollection<AlgoParamRowVM> algo_params;
if (include_algo_type_rows) {
// while normal params live and die with the selected algo-type (when the algo-type changes, they are removed, and replaced with params for the new algo type),
// the algo-type row is constant (it is the row with the algo-type dropdown, which the user may use to select different algos).
algo_params = new ObservableAggregateCollection<AlgoParamRowVM>();
rows.add_collection(new ObservableAppendCollection<AlgoParamRowVM>(algo_params, root = new AlgoSelectorParamRowVM(new AlgoVMWrapper(algo_def), algo_def, null)).AsReadOnly());
} else {
algo_params = rows;
}
return algo_def.observe_algo_root().SubscribeEach(a => {
algo_params.clear_collections();
if (a == null)
return Disposable.empty;
var child_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
/// Note: the order of operations here matters
/// <see cref="_wrap_rows(IAlgoVM, AlgoParamRowVM, ObservableAggregateCollection{AlgoParamRowVM}, bool)" /> has the potential to trigger a lot of CollectionChanged events (depending on how deep the algo-tree is).
/// There is no need to burden the aggregate 'rows' with echoing all of these events, if the child-row collection is finished changing when it is added to the aggregate.
var hook = _wrap_rows(a, root, child_rows, include_algo_type_rows);
algo_params.add_collection(child_rows.AsReadOnly());
return hook;
});
}
static IDisposable _wrap_rows(IAlgoVM algo_root, AlgoParamRowVM parent, ObservableAggregateCollection<AlgoParamRowVM> all_rows, bool include_algo_type_rows) {
var wrapped_params = new ObservableAutoWrapper<AlgoParamRowVM, IAlgoParamVM>(algo_root.algo_params, pvm => new AlgoParamRowVM(algo_root, pvm, parent));
var selector_params = new ObservableCollection<AlgoParamRowVM>();
all_rows.add_collection(wrapped_params);
all_rows.add_collection(selector_params);
return ObservableAggregateCollection.connect(selector_params, algo_root.child_algo_selectors, new Func<IAlgoSelector, (ObservableCollection<AlgoParamRowVM>, IDisposable)>(hook_children));
(ObservableCollection<AlgoParamRowVM>, IDisposable) hook_children(IAlgoSelector s) {
var child_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
var selector_wrapper = new AlgoSelectorParamRowVM(algo_root, s, parent);
return (
include_algo_type_rows ?
(ObservableCollection<AlgoParamRowVM>)new ObservableAppendCollection<AlgoParamRowVM>(child_rows, selector_wrapper) :
child_rows,
s.subscribe_selected_algo().SubscribeEach(a => {
s.value = a;
child_rows.clear_collections();
return _wrap_rows(a, selector_wrapper, child_rows, include_algo_type_rows);
}));
}
}
}
}
@@ -0,0 +1,78 @@
using bts.collections;
using bts.spark;
using mbts.spark;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace mbts.ui.spark {
static class AlgoParamTemplateKeys {
public const string
Price = nameof(Price),
Size = nameof(Size);
}
/// TODO: remove this class and just use a <see cref="KeyedTemplateSelector"/>.
class AlgoParamTemplateSelector : DataTemplateSelector {
public static readonly Type generic_algo_param_itype = typeof(IAlgoParamVM<>);
KeyedCollection<TemplateSelectorKeyValue, string> _keyed_templates = new KeyedCollection<TemplateSelectorKeyValue, string>(tsk => tsk.Key);
public DataTemplate BoolTemplate { get; set; }
public DataTemplate NullableBoolTemplate { get; set; }
public DataTemplate IntTemplate { get; set; }
public DataTemplate NullableIntTemplate { get; set; }
public DataTemplate UIntTemplate { get; set; }
public DataTemplate NullableUIntTemplate { get; set; }
public DataTemplate DoubleTemplate { get; set; }
public DataTemplate EnumTemplate { get; set; }
public DataTemplate NullableEnumTemplate { get; set; }
public DataTemplate StringTemplate { get; set; }
public DataTemplate QuantityMeasureTemplate { get; set; }
public DataTemplate OrderDefTemplate { get; set; }
public DataTemplate ExecutionStrategyTemplate { get; set; }
public DataTemplate TimestampTemplate { get; set; }
/// <see cref="_keyed_templates"/> is needed for the 'price' param. Price was formerly edited in a separate control, however now it is just another param row.
/// Before this, the generic type argument in the <see cref="IAlgoParamVM{T}"/> implementation was enough to determine the template.
/// Since 'price' is just a double, further specification was needed to associate it with a 'PriceEntryControl' template.
public IList KeyedTemplates => _keyed_templates;
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
if (item is ITemplateSelectorKey tsk && tsk.key is string template_key && _keyed_templates.TryGetValue(template_key, out var tkv))
return tkv.Value;
IAlgoParamVM pvm = item as IAlgoParamVM ?? (item as AlgoParamRowVM)?.param;
if (pvm != null) {
if (pvm is IAlgoParamVM<double>) return DoubleTemplate;
if (pvm is IAlgoParamVM<int>) return IntTemplate;
if (pvm is IAlgoParamVM<int?>) return NullableIntTemplate;
if (pvm is IAlgoParamVM<uint>) return UIntTemplate;
if (pvm is IAlgoParamVM<uint?>) return NullableUIntTemplate;
if (pvm is IAlgoParamVM<bool>) return NullableBoolTemplate;
if (pvm is IAlgoParamVM<bool?>) return BoolTemplate;
if (pvm is IAlgoParamVM<string>) return StringTemplate;
if (pvm is IAlgoParamVM<IAlgoVM>) return OrderDefTemplate;
if (pvm is IAlgoParamVM<QuantityMeasureImmutable?>) return QuantityMeasureTemplate;
if (pvm is IAlgoParamVM<ExecutionStrategy>) return ExecutionStrategyTemplate;
if (pvm is IAlgoParamVM<Google.Protobuf.WellKnownTypes.Timestamp>) return TimestampTemplate;
var targ = ReflectionHelper.GetGenericTypeArgs(pvm.GetType(), generic_algo_param_itype).FirstOrDefault();
if (targ != null) {
if (targ.IsEnum)
return EnumTemplate;
else if (targ.IsNullable()) {
targ = targ.GetGenericArguments()[0];
if (targ.IsEnum)
return NullableEnumTemplate;
}
}
}
return null;
}
}
}
@@ -0,0 +1,181 @@
#define ENTER_BEHAVES_LIKE_TAB
using bts;
using mbts.ui.controls;
using mbts.ui.extensions;
using mbts.ui.spark.algo;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace mbts.ui.spark {
partial class AlgoEditorGridView {
static class DataGridEditHelper {
public static IDisposable set_hooks(AlgoEditorGridView algo_editor) {
var grid = algo_editor._param_grid;
grid.PreviewKeyDown += on_preview_key_down;
grid.KeyDown += on_key_down;
grid.SelectionChanged += on_selection_changed;
grid.PreparingCellForEdit += on_preparing_cell_for_edit;
grid.CellEditEnding += on_cell_edit_ending;
grid.GotFocus += on_got_focus;
algo_editor.CommitEditRequested += on_commit_edit_requested;
algo_editor.CancelEditRequested += on_cancel_edit_requested;
return Disposable.Create(() => {
grid.PreviewKeyDown += on_preview_key_down;
grid.KeyDown -= on_key_down;
grid.SelectionChanged -= on_selection_changed;
grid.PreparingCellForEdit -= on_preparing_cell_for_edit;
grid.CellEditEnding -= on_cell_edit_ending;
grid.GotFocus -= on_got_focus;
algo_editor.CommitEditRequested -= on_commit_edit_requested;
algo_editor.CancelEditRequested -= on_cancel_edit_requested;
});
/// Handlers
// hooked up to PreviewKeyDown, instead of KeyDown, so the cells can't override this behavior
void on_preview_key_down(object sender, KeyEventArgs e) {
switch (e.Key) {
case Key.Escape: e.Handled = cancel_edit(); break;
case Key.Enter:
if (grid.TryGetCurrentRowView(out var row_view)) {
if (row_view.IsEditing) {
commit_edit();
#if ENTER_BEHAVES_LIKE_TAB
NavigationHelper.keyboard_move_focus(FocusNavigationDirection.Down);
#endif
} else grid.BeginEdit();
}
e.Handled = true;
break;
}
}
// hooked up to KeyDown, instead of PreviewKeyDown, so cells can implement custom Key/CommandBindings, or other key-based behavior without the DataGrid handling it in the PreviewKeyDown event.
// for ex: TickUpDown cells can do things like TickUp/Down when Ctrl+Up/Down arrow-key is pressed.
void on_key_down(object sender, KeyEventArgs e) {
switch (e.Key) {
case Key.Up: NavigationHelper.keyboard_move_focus(FocusNavigationDirection.Up); break;
case Key.Down: NavigationHelper.keyboard_move_focus(FocusNavigationDirection.Down); break;
default: return;
}
e.Handled = true;
}
void on_selection_changed(object sender, SelectionChangedEventArgs e) {
_commit(e.RemovedItems?.Cast<object>()?.SafeSingleOrDefault());
_commit(e.AddedItems?.Cast<object>()?.SafeSingleOrDefault());
void _commit(object row_item) {
if (grid.TryGetRowView(row_item, out var row_view) && row_view.IsEditing)
grid.CommitEdit();
}
}
void on_preparing_cell_for_edit(object sender, DataGridPreparingCellForEditEventArgs e) {
if (e.EditingElement is ContentPresenter presenter) {
OneOf.Match(get<TextBox>, get<ComboBox>, get<QuantityMeasureControl>, get<ToggleButton>, text_box, combo_box, quantity_measure_control, toggle_button);
T get<T>() => presenter.descendants_of_type<T>().FirstOrDefault();
// TextBox
void text_box(TextBox tb) => Keyboard.Focus(tb);
// ComboBox
void combo_box(ComboBox cb) { }
// QuantityMeasureControl
async void quantity_measure_control(QuantityMeasureControl qmc) {
if (!focus_text()) {
await qmc.LoadedAsync();
focus_text();
}
bool focus_text() =>
qmc.try_find_descendent_of_type(out TextBox tb) && Keyboard.Focus(tb) == tb;
}
// ToggleButton
void toggle_button(ToggleButton tb) {
if (Mouse.LeftButton == MouseButtonState.Pressed && !tb.IsLoaded) {
// this is a hack to toggle IsChecked when the user clicks a checkbox cell that is not already in edit mode.
IDisposable dispose = null;
dispose = Disposable.Create(
tb.SubscribePreviewMouseUp(on_mouse_up),
tb.SubscribeMouseLeave(on_mouse_leave));
void on_mouse_up(object s, MouseButtonEventArgs mbe) {
if (tb.IsMouseOver)
tb.SetCurrentValue(ToggleButton.IsCheckedProperty, !tb.IsChecked);
Disposable.dispose(ref dispose);
}
void on_mouse_leave(object s, MouseEventArgs me) =>
Disposable.dispose(ref dispose);
}
}
}
e.EditingElement.Focus();
}
void on_cell_edit_ending(object sender, DataGridCellEditEndingEventArgs e) {
if (e.EditingElement is ContentPresenter presenter) {
switch (e.EditAction) {
case DataGridEditAction.Cancel: OneOf.Match(get<TextBox>, get<ComboBox>, get<ToggleButton>, cancel_text_box, cancel_combo_box, cancel_toggle_button); break;
case DataGridEditAction.Commit: OneOf.Match(get<TextBox>, get<ComboBox>, get<ToggleButton>, commit_text_box, commit_combo_box, commit_toggle_button); break;
}
T get<T>() => presenter.descendants_of_type<T>().FirstOrDefault();
// TextBox
void commit_text_box(TextBox tb) => tb.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
void cancel_text_box(TextBox tb) => tb.GetBindingExpression(TextBox.TextProperty)?.UpdateTarget();
// ComboBox
void commit_combo_box(ComboBox cb) { }
void cancel_combo_box(ComboBox cb) { }
// ToggleButton
void commit_toggle_button(ToggleButton tb) { }
void cancel_toggle_button(ToggleButton tb) { }
}
}
void on_got_focus(object sender, RoutedEventArgs e) {
if (e.OriginalSource is DataGridCell cell && cell.try_find_ancestor_of_type(out DataGridRow row)) {
if (row.IsEditing) {
if (!cell.IsEditing) {
grid.CancelEdit(DataGridEditingUnit.Row);
if (try_set_current())
grid.BeginEdit(e);
}
} else {
try_set_current();
if (!row.IsEditing)
grid.BeginEdit(e);
}
bool try_set_current() {
grid.SetCurrentValue(DataGrid.CurrentItemProperty, row.Item);
if (grid.TryGetCurrentRowView(out var row_view)) {
row_view.SetCurrentValue(DataGridRow.IsSelectedProperty, true);
grid.SetCurrentValue(DataGrid.CurrentCellProperty, new DataGridCellInfo(row.Item, grid.Columns[1]));
return true;
} else return false;
}
}
}
void on_commit_edit_requested() {
if (grid.TryGetCurrentRowView(out var row_view) && row_view.IsEditing)
commit_edit();
}
void commit_edit() {
using (grid.SuspendGotFocus(on_got_focus))
grid.CommitEdit(DataGridEditingUnit.Row, true);
}
void on_cancel_edit_requested() =>
cancel_edit();
bool cancel_edit() {
if (grid.CurrentCell != null && grid.TryGetCurrentRowView(out var row_view) && row_view.IsEditing) {
using (grid.SuspendGotFocus(on_got_focus))
grid.CancelEdit(DataGridEditingUnit.Row);
return true;
} else return false;
}
}
}
}
}
@@ -0,0 +1,120 @@
<UserControl
x:Class="mbts.ui.spark.AlgoEditorView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sp="clr-namespace:mbts.spark"
xmlns:u="clr-namespace:mbts.ui"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:usp="clr-namespace:mbts.ui.spark"
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Controls/Order/AlgoPickerNodeView.xaml"/>
<ResourceDictionary Source="/view/Controls/Order/AlgoContentTemplateSelector.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!--Primitive Constants-->
<sys:Double x:Key="HorizontalPush1">4</sys:Double>
<sys:Double x:Key="HorizontalPush2">8</sys:Double>
<sys:Double x:Key="HorizontalPush3">12</sys:Double>
<sys:Double x:Key="VerticalPush1">4</sys:Double>
<sys:Double x:Key="VerticalPush2">8</sys:Double>
<sys:Double x:Key="VerticalPush3">12</sys:Double>
<Thickness x:Key="Margin.LP1" Left="{StaticResource HorizontalPush1}"/>
<Thickness x:Key="Margin.LP2" Left="{StaticResource HorizontalPush2}"/>
<Thickness x:Key="Margin.LP3" Left="{StaticResource HorizontalPush3}"/>
<Thickness x:Key="Margin.TP1" Top="{StaticResource VerticalPush1}"/>
<Thickness x:Key="Margin.TP2" Top="{StaticResource VerticalPush2}"/>
<!--Converters-->
<uspcv:OrderTypeBrushConverter x:Key="OrderTypeBrushConverter" DefaultBrush="Transparent"/>
<!--Default Styles-->
<Style x:Key="ContentHostGroupBoxStyle" TargetType="GroupBox">
<Setter Property="Padding" Value="8"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="u:TextBoxBehavior.EnterKeyBehavior" Value="CommitAndRelease"/>
<Setter Property="u:TextBoxBehavior.ReleaseOnEscape" Value="True"/>
</Style>
<Style TargetType="uag:AlgoPickerNodeView" BasedOn="{StaticResource {x:Type uag:AlgoPickerNodeView}}">
<Setter Property="DropDownStyleKey" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorView}, Converter={x:Static usp:AlgoContentTemplateSelector.AlgoDropDownStyleKeyConverter}}"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<GroupBox
x:Name="content_host"
Grid.Row="2"
Background="Transparent"
BorderBrush="{Binding algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
Margin="0,6,0,0"
Padding="12,8"
>
<GroupBox.Style>
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="BorderBrush" Value="Transparent">
<Setter Property="BorderThickness" Value="0"/>
</Trigger>
<Trigger Property="usp:AlgoEditorView.IsMouseOverNodeContent" Value="True">
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
</Style.Triggers>
</Style>
</GroupBox.Style>
<GroupBox.Header>
<ComboBox
ToolTipService.ShowOnDisabled="True"
Width="140"
Margin="{StaticResource Margin.LP1}"
ItemsSource="{Binding algos_view}"
SelectedItem="{Binding algo_root}"
>
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:True}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:False}">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="ToolTip" Value="Cannot change structure while templates are present."/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
<ComboBox.IsEnabled>
<Binding Path="editing_permissions">
<Binding.Converter>
<cv:EnumHasFlagsConverter>
<sp:AlgoEditingPermissions>AllowEditStructure</sp:AlgoEditingPermissions>
</cv:EnumHasFlagsConverter>
</Binding.Converter>
</Binding>
</ComboBox.IsEnabled>
<ComboBox.ItemTemplate>
<DataTemplate>
<usp:OrderTypeItem DisplayName="{Binding display_name}" BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</GroupBox.Header>
<ContentControl
Grid.IsSharedSizeScope="True"
Content="{Binding algo_root, Converter={x:Static usp:AlgoContentTemplateSelector.DataContextConverter}}"
ContentTemplateSelector="{StaticResource AlgoTemplateSelector}"
Margin="{StaticResource Margin.TP1}"
Visibility="{Binding algo_root.can_expand, Converter={StaticResource BooleanToVisibilityConverter}}"
IsTabStop="False"
>
<ContentControl.Resources>
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}"/>
</ContentControl.Resources>
</ContentControl>
</GroupBox>
</UserControl>
@@ -0,0 +1,28 @@
using mbts.ui.extensions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace mbts.ui.spark {
partial class AlgoEditorView : UserControl {
public AlgoEditorView() {
InitializeComponent();
content_host.PreviewMouseMove += content_host_PreviewMouseMove;
content_host.MouseEnter += content_host_MouseEnter;
content_host.MouseLeave += content_host_MouseLeave;
// local methods
void content_host_PreviewMouseMove(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
void content_host_MouseEnter(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
void content_host_MouseLeave(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
void refresh_is_mouse_over_content_host(MouseEventArgs e) => content_host.set_current_if_changed(IsMouseOverNodeContentProperty, this.ChildAtPoint<GroupBox>(e.GetPosition(this)) == content_host);
}
#region IsMouseOverNodeContent
public static readonly DependencyProperty IsMouseOverNodeContentProperty =
DependencyProperty.RegisterAttached("IsMouseOverNodeContent", typeof(bool), typeof(AlgoEditorView), new PropertyMetadata(false));
public static bool GetIsMouseOverNodeContent(DependencyObject d) => (bool)d.GetValue(IsMouseOverNodeContentProperty);
public static void SetIsMouseOverNodeContent(DependencyObject d, bool value) => d.SetValue(IsMouseOverNodeContentProperty, value);
#endregion
}
}
@@ -1,6 +1,8 @@
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.collections;
using bts.shell.fills;
using bts.spark;
using bts.utils;
using mbts.collections;
@@ -16,8 +18,9 @@ using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Windows.Data;
@@ -31,22 +34,19 @@ using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
namespace mbts.spark {
class FillsVM : DisposableViewModelBase, IHandleGridSorting {
readonly OrdersModel _model;
readonly RXOperableCollection<Row, FillTuple, string> _rows;
readonly DIYGridSortManager<Columns> _sort_manager;
readonly IDIYGridFiltersManager<FillTuple, Columns, FilterStates> _filters_manager;
readonly DIYGridRowsManager<FillTuple, Columns, FillsFilterStates> _rows_manager;
readonly ObservableKeyedCollection<ColumnBase, Columns> _columns = new ObservableKeyedCollection<ColumnBase, Columns>(_col_key);
List<(ColumnBase col, Columns key, ListSortDirection direction)> _active_comparers = new List<(ColumnBase col, Columns key, ListSortDirection direction)>();
IWindowSettingsManager<GridSettings> _settings_manager;
int _total_row_count;
public FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IWindowSettingsManager<GridSettings> settings_manager)
public FillsVM(IBus bus, OrdersModel model, IObservable<FillsSettings> state_obs, IWindowSettingsManager<GridSettings> settings_manager)
: this(bus, model, state_obs, _get_observable(bus, model, state_obs), settings_manager) { }
FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IConnectableObservable<TableUpdate<FillTuple, string>> fills_source, IWindowSettingsManager<GridSettings> settings_manager) {
public FillsVM(IBus bus, OrdersModel model, IObservable<FillsSettings> state_obs, IObservable<TableUpdate<FillTuple, string>> fills_source, IWindowSettingsManager<GridSettings> settings_manager) {
_model = model;
_settings_manager = settings_manager;
/// Columns
var col_tups = new ObservableKeyedCollection<DIYGridColumnData<FillTuple, Columns, FilterStates>, Columns>(c => c.key);
var col_tups = new ObservableKeyedCollection<DIYGridColumnData<FillTuple, Columns, FillsFilterStates>, Columns>(c => c.key);
// security
add_col(Columns.Security, DefaultColumnHeaders.Security, r => r.security.ToString(),
DiscreteValuesFilterVM.Create<FillTuple, SecurityImmutable, SecurityCriteria>(
@@ -83,45 +83,46 @@ namespace mbts.spark {
add_col(Columns.FillKey, DefaultColumnHeaders.FillKey, r => r.fill_key.ToString(), new RegexFilterVM<FillTuple>(r => r.fill.FillKey), DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.FillKey);
add_col(Columns.OrderKey, DefaultColumnHeaders.OrderKey, r => r.order_key.ToString(), new RegexFilterVM<FillTuple>(r => r.fill.OrderKey.ToString()), DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.OrderKey);
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), new RegexFilterVM<FillTuple>(r => r.fill.SecurityKey.ToString()), DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
/// Rows
_rows = new RXOperableCollection<Row, FillTuple, string>(
(ft, fk) => new Row(ft, model),
ft => ft.fill.FillKey,
new ComparerBuilder<FillTuple>(new[] {
_get_column_comparer(Columns.FillTime),
_get_column_comparer(Columns.FillKey)
}).to_comparer());
// row sort/filter managers
_sort_manager = new DIYGridSortManager<Columns>(col_tups.WrapSelector(t => (t.col, t.key)), Columns.FillKey);
_filters_manager = DIYGridFiltersManager.create(col_tups);
var rows_manager = new DIYGridRowsManager<FillTuple, Columns, FilterStates>(_filters_manager, _sort_manager, _get_column_comparer, f => _rows.filter = f, c => _rows.comparer = c, _rows.reset, FillsWindow.window_type_name);
// rows
_rows_manager = DIYGridRowsManager.create(FillsWindow.window_type_name, Columns.FillKey, col_tups, col_tups.WrapSelector(t => (t.col, t.key)), _get_column_comparer, fs => fs.build_predicate());
// grid settings
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, rows);
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
grid_settings_manager.load_from(settings_manager.current_settings());
//
_dispose = Disposable.Create(
if (_settings_manager != null)
grid_settings_manager.load_from(_settings_manager.current_settings());
var filter_src = Observable.FromEvent<Action<Func<FillTuple, bool>>, Func<FillTuple, bool>>(cb => _rows_manager.filter_changed += cb, cb => _rows_manager.filter_changed -= cb);
var comp_src = Observable.FromEvent<Action<IComparer<FillTuple>>, IComparer<FillTuple>>(cb => _rows_manager.comparer_changed += cb, cb => _rows_manager.comparer_changed -= cb);
TableUpdate<FillTuple, string> last_unfiltered_fills = TableUpdate<FillTuple, string>.mk_empty(FillTuple.key_fn);
var fills_buffer = Observable.Create<TableUpdate<FillTuple, string>>(o => {
// this is what .Cache() does, but is a little cheaper, since we know subscriptions and callbacks occur on the same thread (i.e., on this Dispatcher).
o.OnNext(last_unfiltered_fills.as_snapshot());
_unfiltered_fills_changed += o.OnNext;
return Disposable.Create(() => _unfiltered_fills_changed -= o.OnNext);
});
_dispose = new CompositeDisposable(
ObservableAutoWrapper.connect(col_tups, columns, ct => ct.col),
RXCollection.connect(rows, fills_buffer.Filter(filter_src, FillTuple.key_fn), comp_src, ft => new Row(ft, model), r => r.fill_tuple),
fills_source.OnDispatcher().Subscribe(t => {
_rows.update(t);
last_unfiltered_fills = t;
Set(ref _total_row_count, t.state.Count, nameof(total_row_count));
_unfiltered_fills_changed?.Invoke(t);
}),
state_obs.OnDispatcher().Subscribe(s => {
if (s.grid != null) {
if (s.grid != null && _settings_manager != null) {
// this means we are not using defaults,
// Note: settings_manager in turn notifies grid_settings_manager of this update
_settings_manager.instance_settings = s.grid;
}
var fs = s.filters ?? new FilterStates {
var fs = s.filters ?? new FillsFilterStates {
fill_time = new TimeFilterTuple(
TimeFilterMode.CMEOpen,
TimeTextTuple.FromDateTime(DateTime.Today),
TimeTextTuple.FromDateTime(DateTime.Today.AddDays(1)))
};
rows_manager.on_settings(fs, s.sorting_criterion);
_rows_manager.on_settings(fs, s.sorting_criterion);
}),
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
fills_source.Connect()
settings_manager == null ? Disposable.empty : settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
(fills_source as IConnectableObservable<TableUpdate<FillTuple, string>>)?.Connect() ?? Disposable.empty
);
// local methods
void add_col<TFilter>(Columns col_key, string header, Func<Row, string> get_text, IDataGridColumnPersistentFilter<FillTuple, TFilter> filter, string filter_template, double width) =>
@@ -131,14 +132,17 @@ namespace mbts.spark {
Width = width
}, filter);
void add_col_expl<TFilter>(Columns col_key, ColumnBase col, IDataGridColumnPersistentFilter<FillTuple, TFilter> filter) =>
col_tups.Add(col_key, DIYGridColumnData.create<FillTuple, Columns, FilterStates, TFilter>(col, filter, _get_column_comparer(col_key)));
col_tups.Add(col_key, DIYGridColumnData.create<FillTuple, Columns, FillsFilterStates, TFilter>(col, filter, _get_column_comparer(col_key)));
// discrete-values-filter factories
IDataGridColumnPersistentFilter<FillTuple, T[]> dv_filter<T>(IReadOnlyCollection<T> items, Func<FillTuple, T> get_row_val) => DiscreteValuesFilterVM.Create<FillTuple, T>(items, get_row_val);
IDataGridColumnPersistentFilter<FillTuple, T[]> dv_filter_rx<T>(IObservable<IEnumerable<T>> items, Func<FillTuple, T> get_row_val) => DiscreteValuesFilterVM.Create<FillTuple, T>(items, get_row_val);
}
// this event is used to forward updates from the main data to the filtered data that drives the rows, after resetting the total row count.
event Action<TableUpdate<FillTuple, string>> _unfiltered_fills_changed;
public ObservableCollection<ColumnBase> columns { get; } = new ObservableCollection<ColumnBase>();
public ObservableCollection<Row> rows => _rows;
public ObservableCollection<Row> rows { get; } = new ObservableCollection<Row>();
public int total_row_count => _total_row_count;
public SimpleGridSettingsManager<Row> grid_settings_manager { get; }
@@ -150,26 +154,26 @@ namespace mbts.spark {
return grid_settings_manager.create();
}
public FillsFilterStates get_filters() =>
_rows_manager.get_filters();
public IEnumerable<SortingCriteria> get_sorting_criterion() =>
_sort_manager.create();
_rows_manager.get_sorting_criterion();
public void on_sorting(object sender, DIYGridSortingEventArgs e) =>
_rows_manager.on_sorting(e);
static Columns _col_key(ColumnBase column) =>
((IKeyedColumn<Columns>)column).key;
void IHandleGridSorting.on_sorting(object sender, DIYGridSortingEventArgs e) =>
_sort_manager.on_sorting(e);
public FilterStates get_filters() =>
_filters_manager.create();
static IObservable<TableUpdate<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, StartupData state) {
static IObservable<TableUpdate<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, FillsSettings state) {
if (!state.orders.is_null_or_empty()) {
var orders_hash = new HashSet<uint>(state.orders);
return model.on_fill_tuples.Filter(f => orders_hash.Contains(f.fill.OrderKey));
} else return model.on_fill_tuples;
}
static IConnectableObservable<TableUpdate<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, IObservable<StartupData> state) =>
static IConnectableObservable<TableUpdate<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, IObservable<FillsSettings> state) =>
state.SelectManyInSeries(s => _get_observable(bus, model, s));
static IComparer<FillTuple> _get_column_comparer(Columns col) {
@@ -208,8 +212,9 @@ namespace mbts.spark {
}
public class Row : SecurityRow, IEquatable<Row>, IRowVM {
public readonly FillTuple fill_tuple;
readonly OrdersModel _model;
readonly FillTuple _fill_tuple;
readonly FillImmutable _fill;
readonly OrderRecordImmutable _order;
Brush _badge_fill;
@@ -217,7 +222,7 @@ namespace mbts.spark {
public Row(FillTuple fill_tuple, OrdersModel model) :
base(fill_tuple.security ?? SecurityDataHelper.Defaults.SecurityImmutable) {
_fill_tuple = fill_tuple;
this.fill_tuple = fill_tuple;
_fill = fill_tuple.fill;
_order = fill_tuple.order ?? OrderDataHelper.Defaults.OrderRecordImmutable;
_model = model;
@@ -227,9 +232,9 @@ namespace mbts.spark {
public DateTime fill_time => _fill.Timestamp.to_local_time();
public TimeInForce time_in_force => _order.time_in_force();
public OrderType order_type => _order.order_type();
public string display_name => OrderUtils.display_name(_order, _fill_tuple.algo);
public string display_name => OrderUtils.display_name(_order, fill_tuple.algo);
public OrderMetadata order_metadata => _order_metadata ?? (_order_metadata = _model.factory.get_metadata(_order.Def.CustomDef.CustomCase));
public Brush badge_fill => _badge_fill ?? (_badge_fill = OrderUtils.badge_fill(_order, _fill_tuple.algo_meta, order_metadata));
public Brush badge_fill => _badge_fill ?? (_badge_fill = OrderUtils.badge_fill(fill_tuple.algo_meta, order_metadata));
public string account { get; private set; }
public string user => _order.user();
public Venue exchange => security.Exchange;
@@ -0,0 +1,55 @@
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.utility;
using System;
using System.Collections.ObjectModel;
namespace mbts.spark {
/// <summary>
/// This class provides a stable DataContext for a fills view, with an internal <see cref="FillsVM"/> that may be disposed and recreated as needed.
/// </summary>
class FillsVMContainer : IHandleGridSorting, IDisposable, IWillLetYouKnowWhenIDispose {
bool _disposed;
FillsVM _value;
IDisposable _columns_duplex;
public event Action<bool> OnDisposed;
public FillsVM value {
get => _value;
set {
if (value != _value) {
if (_disposed)
throw new ObjectDisposedException(nameof(FillsVMContainer));
Disposable.dispose(ref _columns_duplex);
if (_value != null) {
columns.Clear();
rows.clear_collections();
}
Disposable.dispose(ref _value);
_value = value;
if (_value != null) {
_columns_duplex = ObservableAutoWrapper.connect_two_way(_value.columns, columns);
rows.add_collection(_value.rows);
}
}
}
}
public ObservableAggregateCollection<FillsVM.Row> rows { get; } = new ObservableAggregateCollection<FillsVM.Row>();
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
public void Dispose() {
if (_disposed)
throw new ObjectDisposedException(nameof(FillsVMContainer));
value = null;
OnDisposed?.Invoke(true);
_disposed = true;
}
public void on_sorting(object sender, DIYGridSortingEventArgs e) {
if (!_disposed)
_value?.on_sorting(sender, e);
}
}
}
@@ -1,6 +1,7 @@
using bts;
using bts.api;
using bts.shell;
using bts.shell.fills;
using bts.spark;
using mbts.persist;
using mbts.ui;
@@ -13,47 +14,46 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using StartupData = bts.shell.FillsWindowFactory.StartupData;
namespace mbts.spark {
class FillsWindow : SparkWindow<StartupData>, ISharedWindow {
class FillsWindow : SparkWindow<FillsSettings>, ISharedWindow {
public const string window_type_name = "Fills";
const string _thread_id = "Fills";
readonly AllowOnce _init_guard = new AllowOnce("Fills");
readonly OrdersModel _orders_model;
readonly IWindowSettingsManager<GridSettings> _settings_manager;
FillsVM _vm;
FillsView _view;
StartupData _state;
FillsSettings _state;
IReadOnlyList<SparkCommand> _commands;
IDisposable _subscriptions;
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, StartupData state, WindowSettingsManager<GridSettings> settings_manager)
: base("Fills", bus, factory, SparkCommands.ShowFills.icon) {
public FillsWindow(FillsWindowFactory factory, IBus bus, OrdersModel orders_model, FillsSettings state, WindowSettingsManager<GridSettings> settings_manager)
: base("Fills", bus, factory, SparkCommands.ShowFills().icon) {
_orders_model = orders_model;
_state = state;
_settings_manager = settings_manager;
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().fills ? "Fills" : null;
}
event Action<FillsWindowFactory.StartupData> _state_changed;
event Action<FillsSettings> _state_changed;
public override string window_type => window_type_name;
public override UIElement Content => _view;
public override IEnumerable<SparkCommand> Commands => _commands;
public override StartupData SaveState => _state = new StartupData(_state.orders, _vm.get_sorting_criterion().ToList(), _vm.get_filters(), _vm.get_grid_save_state());
public override FillsSettings SaveState => _state = new FillsSettings(_state.orders, _vm.get_sorting_criterion().ToList(), _vm.get_filters(), _vm.get_grid_save_state());
public override double MinWidth =>
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
FillsSettings.DefaultGridSettings.columns.Sum(gcs => gcs.width)
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
public string thread_id => _thread_id;
public string thread_id { get; }
public void thread_init() {
_init_guard.check_access();
Log.notnull(_state);
var state_src = Observable.FromEvent<Action<FillsWindowFactory.StartupData>, FillsWindowFactory.StartupData>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
var state_src = Observable.FromEvent<Action<FillsSettings>, FillsSettings>(cb => _state_changed += cb, cb => _state_changed -= cb).StartWith(_state);
_vm = new FillsVM(_bus, _orders_model, state_src, _settings_manager);
_view = new FillsView { DataContext = _vm };
@@ -79,7 +79,7 @@ namespace mbts.spark {
};
_subscriptions = Disposable.Create(() =>
_state_changed?.Invoke(
_state = new StartupData(_state.orders,
_state = new FillsSettings(_state.orders,
_vm.get_sorting_criterion().ToList(),
_vm.get_filters(),
_vm.grid_settings_manager.create()
@@ -1,5 +1,6 @@
using bts.api;
using bts.api.orderman;
using bts.shell.fills;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
@@ -16,10 +17,31 @@ using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.FillsView;
namespace bts.shell {
class FillsWindowFactory : ShellWindowFactory<FillsWindowFactory.StartupData> {
class FillsWindowFactory : ShellWindowFactory<FillsSettings> {
readonly IBus _bus;
readonly OrdersModel _orders_model;
readonly UserSettingsHub _settings_hub;
/// Fills 'sorting-criterion' settings were compromised a while back (mid-late 2018).
/// The issue was that, when a column was sorted, its 'sort member path' was assigned to the SortingCriteria.col_name,
/// rather than the <see cref="Columns"/> enum/id string.
/// This dictionary serves as a reverse-lookup so the sort member path can be converted to the enum/id string.
static readonly IReadOnlyDictionary<string, Columns> _sort_member_to_id =
new Dictionary<string, Columns> {
{ nameof(FillsVM.Row.account), Columns.Account },
{ nameof(FillsVM.Row.exchange), Columns.Exchange },
{ nameof(FillsVM.Row.fill_key), Columns.FillKey },
{ nameof(FillsVM.Row.fill_kind), Columns.FillKind },
{ nameof(FillsVM.Row.fill_time), Columns.FillTime },
{ nameof(FillsVM.Row.order_key), Columns.OrderKey },
{ nameof(FillsVM.Row.order_type), Columns.OrderType },
{ nameof(FillsVM.Row.price), Columns.Price },
{ nameof(FillsVM.Row.security), Columns.Security },
{ nameof(FillsVM.Row.security_key), Columns.SecurityKey },
{ nameof(FillsVM.Row.side), Columns.Side },
{ nameof(FillsVM.Row.size), Columns.Size },
{ nameof(FillsVM.Row.time_in_force), Columns.TimeInForce },
{ nameof(FillsVM.Row.user), Columns.User }
};
public FillsWindowFactory(TradingShellModule module, IBus bus, UserSettingsHub settings_hub, OrdersModel orders_model)
: base(module, "Fills") {
@@ -31,148 +53,19 @@ namespace bts.shell {
public static string col_id(Columns col) => col.ToString();
public override Task<IShellWindow> load(StartupData state) =>
public override Task<IShellWindow> load(FillsSettings state) =>
Task.FromResult(_create(state));
public async void show(StartupData state) =>
public async void show(FillsSettings state) =>
_bus.post(SparkTags.ShowWindow, await load(state));
IShellWindow _create(StartupData state) {
IShellWindow _create(FillsSettings state) {
GridSettingsHelper.normalize_sorting_criterion(state.sorting_criterion, _sort_member_to_id);
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
state.grid,
get: us => us.fills_grid,
set: (us, gs) => us.with(provided_fills_gridsettings: gs));
return new FillsWindow(this, _bus, _orders_model, state, settings_manager);
}
[ProtoContract]
public class StartupData {
public const int Version = 1;
[ProtoMember(1)]
public uint[] orders;
[ProtoMember(2)]
List<SortingCriteria> _sorting_criteria;
[ProtoMember(3)]
public FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
public StartupData() { }
public StartupData(uint[] orders) : this(orders, null, null) { }
public StartupData(FilterStates filters) : this(null, null, filters) { }
public StartupData(uint[] orders, List<SortingCriteria> sorting_criteria, FilterStates filters, GridSettings grid = default) {
this.orders = orders;
this._sorting_criteria = sorting_criteria;
this.filters = filters;
this.grid = grid;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList());
public static StartupData Default() =>
new StartupData(null, null, new FilterStates(), null);
static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings fill_time => new GridColumnSettings(col_id(Columns.FillTime), DefaultColumnHeaders.FillTime, width: DefaultColumnWidths.Time);
public static GridColumnSettings user => new GridColumnSettings(col_id(Columns.User), DefaultColumnHeaders.User, width: DefaultColumnWidths.User);
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange);
public static GridColumnSettings time_in_force => new GridColumnSettings(col_id(Columns.TimeInForce), DefaultColumnHeaders.TimeInForce, width: DefaultColumnWidths.TimeInForce);
public static GridColumnSettings order_type => new GridColumnSettings(col_id(Columns.OrderType), DefaultColumnHeaders.OrderType, width: DefaultColumnWidths.OrderType);
public static GridColumnSettings side => new GridColumnSettings(col_id(Columns.Side), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Side);
public static GridColumnSettings quantity => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind);
public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey);
public static GridColumnSettings order_key => new GridColumnSettings(col_id(Columns.OrderKey), DefaultColumnHeaders.OrderKey, width: DefaultColumnWidths.OrderKey);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(fill_time.id, false);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return fill_time;
yield return user;
yield return account;
yield return exchange;
yield return time_in_force;
yield return order_type;
yield return side;
yield return quantity;
yield return price;
yield return fill_kind;
yield return fill_key;
yield return order_key;
yield return security_key;
}
}
}
}
[ProtoContract]
public class FilterStates {
[ProtoMember(1)]
[DIYGridColumnID(nameof(Columns.FillTime))]
public TimeFilterTuple fill_time { get; set; }
[ProtoMember(2)]
[DIYGridColumnID(nameof(Columns.TimeInForce))]
public TimeInForce[] time_in_force { get; set; }
[ProtoMember(3)]
[DIYGridColumnID(nameof(Columns.OrderType))]
public CustomOrderDefinition.CustomOneofCase[] order_type { get; set; }
[ProtoMember(4)]
[DIYGridColumnID(nameof(Columns.Account))]
public string[] account { get; set; }
[ProtoMember(5)]
[DIYGridColumnID(nameof(Columns.User))]
public string[] user { get; set; }
[ProtoMember(6)]
[DIYGridColumnID(nameof(Columns.Exchange))]
public Venue[] exchange { get; set; }
[ProtoMember(7)]
[DIYGridColumnID(nameof(Columns.Security))]
public SecurityCriteria security { get; set; }
[ProtoMember(8)]
[DIYGridColumnID(nameof(Columns.Side))]
public Side[] side { get; set; }
[ProtoMember(9)]
[DIYGridColumnID(nameof(Columns.Price))]
public DoubleMinMaxState price { get; set; }
[ProtoMember(10)]
[DIYGridColumnID(nameof(Columns.Size))]
public DoubleMinMaxState size { get; set; }
[ProtoMember(11)]
[DIYGridColumnID(nameof(Columns.FillKey))]
public string fill_key { get; set; }
[ProtoMember(12)]
[DIYGridColumnID(nameof(Columns.OrderKey))]
public string order_key { get; set; }
[ProtoMember(13)]
[DIYGridColumnID(nameof(Columns.SecurityKey))]
public string security_key { get; set; }
[ProtoMember(14)]
[DIYGridColumnID(nameof(Columns.FillKind))]
public Fill.Types.FillKind[] fill_kind { get; set; }
}
}
}
@@ -0,0 +1,95 @@
using bts.api;
using bts.api.orderman;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.controls;
using ProtoBuf;
using System;
using Columns = mbts.ui.spark.FillsView.Columns;
namespace bts.shell.fills {
[ProtoContract]
class FillsFilterStates {
[ProtoMember(1)]
[DIYGridColumnID(nameof(Columns.FillTime))]
public TimeFilterTuple fill_time { get; set; }
[ProtoMember(2)]
[DIYGridColumnID(nameof(Columns.TimeInForce))]
public TimeInForce[] time_in_force { get; set; }
[ProtoMember(3)]
[DIYGridColumnID(nameof(Columns.OrderType))]
public CustomOrderDefinition.CustomOneofCase[] order_type { get; set; }
[ProtoMember(4)]
[DIYGridColumnID(nameof(Columns.Account))]
public string[] account { get; set; }
[ProtoMember(5)]
[DIYGridColumnID(nameof(Columns.User))]
public string[] user { get; set; }
[ProtoMember(6)]
[DIYGridColumnID(nameof(Columns.Exchange))]
public Venue[] exchange { get; set; }
[ProtoMember(7)]
[DIYGridColumnID(nameof(Columns.Security))]
public SecurityCriteria security { get; set; }
[ProtoMember(8)]
[DIYGridColumnID(nameof(Columns.Side))]
public Side[] side { get; set; }
[ProtoMember(9)]
[DIYGridColumnID(nameof(Columns.Price))]
public DoubleMinMaxState price { get; set; }
[ProtoMember(10)]
[DIYGridColumnID(nameof(Columns.Size))]
public DoubleMinMaxState size { get; set; }
[ProtoMember(11)]
[DIYGridColumnID(nameof(Columns.FillKey))]
public string fill_key { get; set; }
[ProtoMember(12)]
[DIYGridColumnID(nameof(Columns.OrderKey))]
public string order_key { get; set; }
[ProtoMember(13)]
[DIYGridColumnID(nameof(Columns.SecurityKey))]
public string security_key { get; set; }
[ProtoMember(14)]
[DIYGridColumnID(nameof(Columns.FillKind))]
public Fill.Types.FillKind[] fill_kind { get; set; }
}
static class FillsFilterStatesExtensions {
public static Func<FillTuple, bool> build_predicate(this FillsFilterStates fs) {
var pb = new ColumnPredicateBuilder<FillTuple>();
// fill time
pb.add_time_filter(fs.fill_time, ft => ft.fill_time());
// security
pb.add_security_criteria(fs.security, ft => ft.security);
// regex
pb.add_regex(fs.fill_key, ft => ft.fill.FillKey);
pb.add_regex(fs.order_key, ft => ft.order_key().ToString());
// discrete values
pb.add_discrete_values(fs.user, ft => ft.user());
pb.add_discrete_values(fs.account, ft => ft.account);
pb.add_discrete_values(fs.exchange, ft => ft.exchange());
pb.add_discrete_values(fs.time_in_force, ft => ft.time_in_force());
pb.add_discrete_values(fs.order_type, ft => ft.order_type());
pb.add_discrete_values(fs.side, ft => ft.fill.Side);
pb.add_discrete_values(fs.fill_kind, ft => ft.fill.FillKind);
// numeric
pb.add_numeric(fs.size, ft => ft.fill.LastShares);
pb.add_numeric(fs.price, ft => ft.fill.LastPrice);
return pb.to_predicate();
}
}
}
@@ -0,0 +1,109 @@
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using static bts.shell.FillsWindowFactory;
using static mbts.ui.controls.DIYGrid;
using Columns = mbts.ui.spark.FillsView.Columns;
namespace bts.shell.fills {
[ProtoContract]
class FillsSettings {
public const int Version = 1;
[ProtoMember(1)]
public uint[] orders;
[ProtoMember(2)]
List<SortingCriteria> _sorting_criteria;
[ProtoMember(3)]
public FillsFilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
public FillsSettings() { }
public FillsSettings(uint[] orders) : this(orders, null, null) { }
public FillsSettings(FillsFilterStates filters) : this(null, null, filters) { }
public FillsSettings(uint[] orders, List<SortingCriteria> sorting_criteria, FillsFilterStates filters, GridSettings grid = default) {
this.orders = orders;
this._sorting_criteria = sorting_criteria;
this.filters = filters;
this.grid = grid;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public static GridSettings MiniGridSettings => GridSettings.Default.with(DefaultColumns.Mini.all, DefaultColumns.Mini.sorting_criterion);
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList());
public static FillsSettings Default() =>
new FillsSettings(null, null, new FillsFilterStates(), null);
static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings fill_time => new GridColumnSettings(col_id(Columns.FillTime), DefaultColumnHeaders.FillTime, width: DefaultColumnWidths.Time);
public static GridColumnSettings user => new GridColumnSettings(col_id(Columns.User), DefaultColumnHeaders.User, width: DefaultColumnWidths.User);
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, width: DefaultColumnWidths.Exchange);
public static GridColumnSettings time_in_force => new GridColumnSettings(col_id(Columns.TimeInForce), DefaultColumnHeaders.TimeInForce, width: DefaultColumnWidths.TimeInForce);
public static GridColumnSettings order_type => new GridColumnSettings(col_id(Columns.OrderType), DefaultColumnHeaders.OrderType, width: DefaultColumnWidths.OrderType);
public static GridColumnSettings side => new GridColumnSettings(col_id(Columns.Side), DefaultColumnHeaders.Side, width: DefaultColumnWidths.Side);
public static GridColumnSettings quantity => new GridColumnSettings(col_id(Columns.Size), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings price => new GridColumnSettings(col_id(Columns.Price), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings fill_kind => new GridColumnSettings(col_id(Columns.FillKind), DefaultColumnHeaders.FillKind, width: DefaultColumnWidths.FillKind);
public static GridColumnSettings fill_key => new GridColumnSettings(col_id(Columns.FillKey), DefaultColumnHeaders.FillKey, width: DefaultColumnWidths.FillKey);
public static GridColumnSettings order_key => new GridColumnSettings(col_id(Columns.OrderKey), DefaultColumnHeaders.OrderKey, width: DefaultColumnWidths.OrderKey);
public static GridColumnSettings security_key => new GridColumnSettings(col_id(Columns.SecurityKey), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(fill_time.id, false);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return fill_time;
yield return user;
yield return account;
yield return exchange;
yield return time_in_force;
yield return order_type;
yield return side;
yield return quantity;
yield return price;
yield return fill_kind;
yield return fill_key;
yield return order_key;
yield return security_key;
}
}
public static class Mini {
static readonly IReadOnlyDictionary<Columns, (bool is_visible, double? width_override)> _mini_cols_data =
new Dictionary<Columns, (bool is_visible, double? width_override)> {
{ Columns.Security, (true, null) },
{ Columns.FillTime, (true, ShortColumnWidths.Time) },
{ Columns.OrderType, (true, ShortColumnWidths.OrderType) },
{ Columns.Side, (true, ShortColumnWidths.Side) },
{ Columns.Size, (true, ShortColumnWidths.Size) },
{ Columns.Price, (true, ShortColumnWidths.Price) },
};
public static IEnumerable<GridColumnSettings> all => DefaultColumns.all.Select(_convert);
public static IEnumerable<SortingCriteria> sorting_criterion => default_sorting_criterion.Where(c => _try_get_data(c.col_name, out (bool is_visible, double? _) d) && d.is_visible);
static bool _try_get_data(string id_str, out (bool is_visible, double? width_override) data) {
if (Enum.TryParse(id_str, out Columns k) && _mini_cols_data.TryGetValue(k, out data))
return true;
data = default;
return false;
}
static GridColumnSettings _convert(GridColumnSettings default_col) =>
_try_get_data(default_col.id, out (bool is_visible, double? width_override) d) ?
default_col.with(is_visible: d.is_visible, width: d.width_override) :
default_col.with(is_visible: false);
}
}
}
}
@@ -1,17 +1,24 @@
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.md;
using mbts.co2;
using System;
using System.Threading.Tasks;
namespace mbts.spark {
class LadderModel {
readonly OrdersModel _order_gateway;
readonly MarketDataAuxiliaryModel _md_auxiliary_model;
public LadderModel(SecurityMD source, OrdersModel ordergw, MarketDataAuxiliaryModel md_auxiliary_model) {
public LadderModel(SecurityMD source, OrdersModel ordergw, MBOService mbo, Func<OrderSecurity, bool> order_filter = null) {
security_md = source;
_order_gateway = ordergw;
_md_auxiliary_model = md_auxiliary_model;
orders = ordergw.on_security_working_orders_with_piq(source.security.Id);
this.mbo_service = mbo;
if (order_filter != null) {
orders = orders.Filter(order_filter, o => o.order.Key);
}
}
public enum CancelType { All, Bids, Asks }
@@ -20,7 +27,9 @@ namespace mbts.spark {
public SecurityImmutable security => security_md.security;
public MDConsumerHandle md => security_md.md;
public OrdersModel order_gateway => _order_gateway;
public MarketDataAuxiliaryModel md_auxiliary_model => _md_auxiliary_model;
public IObservable<TableUpdate<OrderSecurity, uint>> orders { get; }
public MBOService mbo_service { get; }
public Task cancel_orders(CancelType ct) {
switch (ct) {
@@ -38,6 +38,14 @@ namespace mbts.spark {
font_weight = FontWeights.SemiBold.ToOpenTypeWeight()
};
public static LadderColumnSettings DefaultPiqColumnSettings => new LadderColumnSettings {
show = false,
width = 50,
font_family = "Segoe UI",
font_size = 10,
font_weight = FontWeights.SemiBold.ToOpenTypeWeight()
};
[ProtoMember(1)]
public bool show { get; set; }
[ProtoMember(2)]
@@ -111,7 +119,8 @@ namespace mbts.spark {
buy = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.MarketData.bid, false) },
sell = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.MarketData.ask, false) },
passive = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.LadderSpecial.passive_volume_bar, false) },
total = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.LadderSpecial.total_volume_bar), show_numbers = true }
total = new HistogramBarAnimation { bars = new ColorShowProto((uint)BTSColors.LadderSpecial.total_volume_bar), show_numbers = true },
show_ltq = false
};
}
}
@@ -126,6 +135,8 @@ namespace mbts.spark {
public HistogramBarAnimation passive { get; set; }
[ProtoMember(5)]
public HistogramBarAnimation total{ get; set; }
[ProtoMember(6)]
public bool show_ltq { get; set; }
public bool Equals(VolumeColumnSettings other) =>
other != null &&
@@ -133,7 +144,8 @@ namespace mbts.spark {
buy.safe_equals(other.buy) &&
sell.safe_equals(other.sell) &&
passive.safe_equals(other.passive) &&
total.safe_equals(other.total);
total.safe_equals(other.total) &&
show_ltq.Equals(other.show_ltq);
public override bool Equals(object obj) => Equals(obj as VolumeColumnSettings);
@@ -143,7 +155,8 @@ namespace mbts.spark {
buy.GetHashCode(),
sell.GetHashCode(),
passive.GetHashCode(),
total.GetHashCode());
total.GetHashCode(),
show_ltq.GetHashCode());
}
[ProtoContract]
@@ -219,6 +232,7 @@ namespace mbts.spark {
// Default proto constructor
// Added this so that when clients upgrade to a version with new settings,
// the default values are applied correctly; thx to John for this tip
/// TODO: all default values should be consts, because they must be referenced in two places (in the default constructor, and the <see cref="DefaultValueAttribute"/>)
public LadderSettings() {
day_visible = true;
high_visible = true;
@@ -230,6 +244,7 @@ namespace mbts.spark {
tif_visible = true;
order_type_visible = true;
account_position_visible = true;
day_position_visible = true;
size_presets_visible = true;
cancel_buys_visible = true;
cancel_sells_visible = true;
@@ -250,7 +265,7 @@ namespace mbts.spark {
ask_column = LadderColumnSettings.Default,
price_column = LadderColumnSettings.Default,
last_column = LadderColumnSettings.Default,
piq_column = LadderColumnSettings.Default;
piq_column = LadderColumnSettings.DefaultPiqColumnSettings;
orders_column.background = BTSColors.LadderSpecial.default_column_background;
bid_column.foreground = 0xFFFFFF;
bid_column.background = BTSColors.MarketData.bid;
@@ -259,8 +274,8 @@ namespace mbts.spark {
price_column.background = BTSColors.LadderSpecial.default_column_background;
last_column.background = 0xFFFFFF;
piq_column.background = BTSColors.LadderSpecial.default_column_background;
return new LadderSettings {
row_height = 20,
var rv = new LadderSettings {
row_height = 22,
grid_line_color = 0x535353,
orders_column = orders_column,
bid_column = bid_column,
@@ -293,6 +308,7 @@ namespace mbts.spark {
tif_visible = true,
order_type_visible = true,
account_position_visible = true,
day_position_visible = true,
size_presets_visible = true,
cancel_buys_visible = true,
cancel_sells_visible = true,
@@ -304,6 +320,8 @@ namespace mbts.spark {
zoom_factor = _default_zoom_factor,
net_change = NetChangeTerms.None,
};
rv.row_height = LadderRender.measure_row_height(rv);
return rv;
}
}
@@ -387,6 +405,8 @@ namespace mbts.spark {
public bool order_type_visible { get; set; }
[ProtoMember(36), DefaultValue(true)]
public bool account_position_visible { get; set; }
[ProtoMember(50), DefaultValue(true)]
public bool day_position_visible { get; set; }
[ProtoMember(37), DefaultValue(true)]
public bool size_presets_visible { get; set; }
[ProtoMember(38), DefaultValue(true)]
@@ -463,6 +483,7 @@ namespace mbts.spark {
tif_visible == other.tif_visible &&
order_type_visible == other.order_type_visible &&
account_position_visible == other.account_position_visible &&
day_position_visible == other.day_position_visible &&
size_presets_visible == other.size_presets_visible &&
cancel_buys_visible == other.cancel_buys_visible &&
cancel_sells_visible == other.cancel_sells_visible &&
@@ -478,6 +499,7 @@ namespace mbts.spark {
public override bool Equals(object obj) => Equals(obj as LadderSettings);
// TODO: remove mutable props from hash logic (or conversely, make them immutable).
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(
row_height.GetHashCode(),
@@ -1,4 +1,5 @@
using bts.api;
using bts.md;
using mbts;
using mbts.config;
using mbts.persist;
@@ -21,19 +22,26 @@ namespace bts.shell {
readonly LadderConfig _ladder_conf;
readonly IBus _bus;
readonly IWindowManager _wm;
readonly MBOService _mbo;
readonly UserSettingsHub _settings_hub;
readonly OrdersModel _orders_model;
readonly MarketDataAuxiliaryModel _md_auxiliary_model;
IReadOnlyDictionary<uint, SecurityMD> _securities = new Dictionary<uint, SecurityMD>();
public LadderWindowFactory(IShellModule owner, IBus bus, IWindowManager wm, UserSettingsHub settings_hub, OrdersModel orders_model, MarketDataAuxiliaryModel md_auxiliary_model)
public LadderWindowFactory(
IShellModule owner,
IBus bus,
IWindowManager wm,
UserSettingsHub settings_hub,
OrdersModel orders_model,
MBOService mbo)
: base(owner, "SparkLadder") {
_bus = bus;
_wm = wm;
_settings_hub = settings_hub;
_orders_model = orders_model;
_md_auxiliary_model = md_auxiliary_model;
_ladder_conf = new LadderConfig(Configuration.get<bts.config.LadderKnobs>(), Configuration.get<bts.config.toggles.LaddersOnThreads>().enabled);
_ladder_conf = new LadderConfig(Configuration.get<bts.config.LadderKnobs>(), Configuration.get<bts.config.MultiThreadedWindows>().ladder);
_mbo = mbo;
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
bus.subscribe(SparkTags.ShowLadderTag).OnDispatcher().Subscribe(s => show_windows(new[] { s })),
@@ -56,15 +64,16 @@ namespace bts.shell {
IShellWindow create_ladder_window(SecurityMD md, LadderSettings state_settings) {
var id = Interlocked.Increment(ref _ladder_count) % Environment.ProcessorCount;
var view = new Lazy<UIElement>(() => {
var model = new LadderModel(md, _orders_model, _md_auxiliary_model);
var model = new LadderModel(md, _orders_model, _mbo);
var settings_manager = new WindowSettingsManager<LadderSettings>(
_settings_hub, state_settings == null || state_settings.use_defaults ? null : state_settings,
us => sanitize_ladder_settings(us.ladder),
(us, v) => us.with(sanitize_ladder_settings(v)));
var init = state_settings ?? settings_manager.default_settings;
init.use_defaults |= state_settings == null;
var vm = new LadderVM(_bus, model, settings_manager, init);
var vm = LadderVM.create(_bus, model, settings_manager, init);
vm.order_properties_vm.open_algo_popup_on_select = _ladder_conf.open_algo_popup_on_select;
vm.order_properties_vm.use_algo_param_grid = _ladder_conf.use_algo_param_grid;
var lv = new LadderView(vm);
foreach (var kv in AlgoStudioModelUtils.get_dynamic_resources(vm.model.order_gateway.algo_model.ui_configuration))
lv.Resources[kv.Key] = kv.Value;
@@ -139,12 +148,13 @@ namespace bts.shell {
readonly struct LadderConfig {
public readonly uint max_open_ladders;
public readonly bool ladders_on_threads, open_algo_popup_on_select;
public readonly bool ladders_on_threads, open_algo_popup_on_select, use_algo_param_grid;
public readonly TimeSpan security_timeout;
public LadderConfig(bts.config.LadderKnobs knobs, bool ladders_on_threads) {
max_open_ladders = knobs.max_open_ladders;
open_algo_popup_on_select = knobs.open_algo_popup_on_select;
use_algo_param_grid = knobs.use_algo_param_grid;
security_timeout = TimeSpan.FromSeconds(Math.Max(15, knobs.security_timeout_secs));
this.ladders_on_threads = ladders_on_threads;
}
@@ -29,45 +29,46 @@ namespace mbts.spark {
}
#endregion
readonly LadderVM _parent;
readonly LadderVM _owner;
readonly IBus _bus;
readonly Property<FontFamily> _font_family;
readonly Property<FontWeight> _font_weight;
readonly Property<double> _font_size;
readonly Property<Brush> _foreground, _background, _hoverbackground, _border, _parent_algo_type_brush;
readonly Property<bool> _is_exchange_order, _has_multiple_orders_at_same_level, _is_hedge_order;
readonly Property<CustomOrderDefinition.CustomOneofCase> _parent_algo_type;
readonly Property<string> _summary;
readonly Property<Brush> _foreground, _background, _hoverbackground, _border;
readonly Property<bool> _has_multiple_orders_at_same_level, _is_hedge_order;
readonly Property<CustomOrderDefinition.CustomOneofCase> _algo_type;
readonly Property<string> _algo_studio_abbreviation;
IObservable<LadderSettings.BadgeStyle> _bs;
LadderSettings.BadgeStyle _current_badge_style;
int _n_active_price_change_ops = 0;
double _real_price = double.NaN, _phantom_price = double.NaN;
uint filled, quantity;
uint filled, quantity, num_root_orders;
int working_quantity;
uint? _min_piq = UInt32.MaxValue;
LadderModel model => _parent.model;
Dictionary<uint, (OrderRecordImmutable order, uint? p)> _orders = new Dictionary<uint, (OrderRecordImmutable order, uint? p)>();
public event Action piq_changed;
public LadderOrderVM((OrderRecordImmutable order, uint? p) op, double real_price, LadderVM parent, IBus bus, IObservable<LadderSettings.BadgeStyle> bs) {
_parent = parent;
LadderModel model => _owner.model;
Dictionary<uint, (OrderRecordImmutable order, uint? p, AlgoData? algo, AlgoDefinitionMetadata? meta)> _orders = new Dictionary<uint, (OrderRecordImmutable order, uint? p, AlgoData? algo, AlgoDefinitionMetadata? meta)>();
public LadderOrderVM((OrderRecordImmutable, uint?, AlgoData?, AlgoDefinitionMetadata?) op, double real_price, LadderVM owner, IBus bus, IObservable<LadderSettings.BadgeStyle> bs) {
_owner = owner;
_bus = bus;
_bs = bs;
_real_price = real_price;
// settings
var fs = UserSettings.factory_defaults.ladder;
_font_family = backing(nameof(font_family), parent.settings, get_font_family, get_font_family(fs));
_font_weight = backing(nameof(font_weight), parent.settings, get_font_weight, get_font_weight(fs));
_font_size = backing(nameof(font_size), parent.settings, get_font_size, get_font_size(fs));
_foreground = backing(nameof(foreground), parent.settings, get_foreground, get_foreground(fs));
_font_family = backing(nameof(font_family), owner.settings, get_font_family, get_font_family(fs));
_font_weight = backing(nameof(font_weight), owner.settings, get_font_weight, get_font_weight(fs));
_font_size = backing(nameof(font_size), owner.settings, get_font_size, get_font_size(fs));
_foreground = backing(nameof(foreground), owner.settings, get_foreground, get_foreground(fs));
_background = backing(nameof(background), _color_bg_brush);
_hoverbackground = backing(nameof(hoverbackground), _color_bg_brush);
_border = backing(nameof(border), _color_bg_brush);
_summary = backing(nameof(summary), string.Empty);
_is_exchange_order = backing(nameof(is_exchange_order), false);
_has_multiple_orders_at_same_level = backing(nameof(has_multiple_orders_at_same_level), false);
_is_hedge_order = backing(nameof(is_hedge_order), false);
_parent_algo_type = backing(nameof(parent_algo_type), CustomOrderDefinition.CustomOneofCase.None);
_parent_algo_type_brush = backing(nameof(parent_algo_type_brush), _color_bg_brush);
_algo_type = backing(nameof(algo_type), CustomOrderDefinition.CustomOneofCase.None);
_algo_studio_abbreviation = backing(nameof(algo_studio_abbreviation), string.Empty);
FontFamily get_font_family(LadderSettings s) => new FontFamily(s.orders_column.font_family);
FontWeight get_font_weight(LadderSettings s) => ui.utility.text.TextUtils.from_open_type_weight_safe(s.orders_column.font_weight);
double get_font_size(LadderSettings s) => s.orders_column.font_size;
@@ -95,36 +96,72 @@ namespace mbts.spark {
}
var darker = BTSColor.darken2(color.R, color.G, color.B, 0.75);
Color dark = new Color() { A = 255, R = darker.r, G = darker.g, B = darker.b };
//var completion = _parent_and_child_not_sharing_slot().Sum(order => OrderUtils.filled(order)) / _parent_and_child_not_sharing_slot().Sum(order => OrderUtils.quantity(order));
bts.spark.OrderMetadata parent_algo_metadata = _parent.model.order_gateway.factory.get_metadata(parent_algo_type);
parent_algo_type_brush = parent_algo_metadata.Color.rgb_brush();
var buy_sell_background_fill_color = new SolidColorBrush(color);
buy_sell_background_fill_color.Freeze();
background = buy_sell_background_fill_color;
hoverbackground = mkbrush(dark, _darkbg, 0);
filled = _parent_and_child_not_sharing_slot().Select(order => OrderUtils.filled(order)).Sum();
working_quantity = _parent_and_child_not_sharing_slot().Select(order => order.working_quantity()).Sum();
quantity = _parent_and_child_not_sharing_slot().Select(order => order.State.Quantity).Sum();
filled = quantity = num_root_orders = 0;
working_quantity = 0;
foreach(var n in _root_nodes()) {
num_root_orders++;
filled += n.filled();
working_quantity += n.working_quantity();
quantity += n.quantity();
}
// if this badge only represents a single order, then is_exchange_order and the border (= algo color)
// should be set appropriately
if (_orders.Count == 1) {
var single_order = _orders.Values.First().order;
var single_order_metadata = _parent.model.order_gateway.factory.get_metadata(single_order);
if (num_root_orders == 1) {
var single_order = _root_nodes().First();
var single_order_metadata = _owner.model.order_gateway.factory.get_metadata(single_order);
border = single_order_metadata.Color.rgb_brush();
is_exchange_order = OrderUtils.is_exchange_order(single_order);
if (_orders.TryGetValue(single_order.Key, out var opm)) {
algo_studio_abbreviation = _sanitize_abbreviation(string.IsNullOrEmpty(opm.meta?.abbreviation) ? _generate_abbreviation_for_algo_studio_algo(opm.algo) : opm.meta?.abbreviation);
border = opm.meta?.badge_color?.rgb_color().rgb_brush() ?? border;
}
if (single_order.order_type().is_bts_algo_type()) {
algo_type = single_order.order_type();
} else {
algo_type = CustomOrderDefinition.CustomOneofCase.None;
}
} else {
border = new SolidColorBrush(color);
is_exchange_order = false;
}
has_multiple_orders_at_same_level = _orders.Count > 1;
is_hedge_order = _orders.Any(op => op.Value.order.sub_type() == OrderSubType.HedgingLeg);
has_multiple_orders_at_same_level = num_root_orders > 1;
is_hedge_order = _root_nodes().Any(o => o.sub_type() == OrderSubType.HedgingLeg);
if (_orders.Any()) {
summary = _orders.Values.Select(op => op.order.short_description(_parent.security.ContractSpec)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
}
_set_badge_text();
string _generate_abbreviation_for_algo_studio_algo(AlgoData? a) {
char[] delim = { ' ' };
string candidate = string.Empty;
if (a.HasValue) {
var algo_name = a.Value.name;
var words = algo_name.Split(delim);
if (words.Length == 1) {
var single_word = words[0];
if (single_word.Length > 1) {
candidate = words[0];
} else {
candidate = single_word;
}
} else {
candidate = words.Take(2).Select(w => w.Substring(0, 1)).list_string(string.Empty);
}
}
return candidate.ToUpper();
}
string _sanitize_abbreviation(string ab) {
string candidate = ab;
if (ab.Length > 2) {
candidate = candidate.Substring(0, 2);
}
return candidate.ToUpper();
}
}
private void _set_badge_text() {
@@ -132,12 +169,32 @@ namespace mbts.spark {
RaisePropertyChanged(nameof(text_lines));
}
public void add_order((OrderRecordImmutable order, uint? p) new_op) {
/// <summary>
/// Add Order performs an add the first time it's called via the constructor. When it's called, directly
/// it really serves as an update/replace. Also note that since the min_piq is a separate piece of state representing
/// the lowest piq of the orders the badge represents, we must reevaluate if that has changed on said update.
/// </summary>
public void add_order((OrderRecordImmutable order, uint? p, AlgoData? algo, AlgoDefinitionMetadata? meta) new_op) {
_orders[new_op.order.Key] = new_op;
// reevaluate min piq if this order's piq is less
if (new_op.p.HasValue) {
// if this lovm only represents 1 order and the piq is now different, regardless of what it is that's our piq
if (_orders.Count == 1 && new_op.p.Value != min_piq) {
min_piq = new_op.p.Value;
} else if (new_op.p.Value < min_piq) {
min_piq = new_op.p.Value;
}
}
}
public void remove_order(uint order_key) {
_orders.Remove(order_key);
// on a remove, potentially the order w/ the lowest piq has been removed, so
// if the min() is now > than the current min_piq, update the min_piq
var prospective_piq = _orders.Select(op => op.Value.p).Where(piq => piq.HasValue).Min();
if (prospective_piq > min_piq) {
min_piq = prospective_piq;
}
}
//public void update(OrderRecordImmutable neworder) {
@@ -147,13 +204,22 @@ namespace mbts.spark {
// }
//}
public uint security_key => _parent.security.Id;
public uint security_key => _owner.security.Id;
public double price { get => double.IsNaN(_phantom_price) ? _real_price : _phantom_price; }
public uint? min_piq => _orders.Select(op => op.Value.p).Where(piq => piq.HasValue).Min();
public uint? min_piq {
get {
return _min_piq;
}
set {
if (Set(ref _min_piq, value)) {
piq_changed?.Invoke();
}
}
}
public double real_price => _real_price;
public Side side {
get {
var distinct_sides = _orders.Values.Select(op => op.order.side()).Distinct();
var distinct_sides = _root_nodes().Select(o => o.side()).Distinct();
if (distinct_sides.Count() > 1) {
return Side.Undefined;
}
@@ -161,16 +227,14 @@ namespace mbts.spark {
}
}
IEnumerable<OrderRecordImmutable> _parent_and_child_not_sharing_slot() => _orders.Values.Select(op => op.order).Where(o => !_orders.ContainsKey(o.ParentKey));
IEnumerable<OrderRecordImmutable> _root_nodes() => _orders.Values.Select(_owner.get_root_at_price).DistinctOn(OrderUtils.RecordKeySelector);
public IReadOnlyList<string> text_lines { get; private set; }
public bool has_parent => _orders.Values.Any( op => op.order.has_parent());
public bool has_phantom_price => !double.IsNaN(_phantom_price);
public ICommand detach_command => new DelegateCommand(() => _execute_command(_crack_order), () => !has_parent);
public ICommand cancel_command => new DelegateCommand(() => _execute_command(_cancel_order));
//public ICommand modify_command => new DelegateCommand(() => _execute_command(() => _bus.post(SparkTags.ShowModifyOrderTag, _order.Key)), () => !has_parent);
public event Action command_executed;
void _execute_command(Action action) {
action();
@@ -184,27 +248,21 @@ namespace mbts.spark {
public Brush background { get => _background.Value; private set => _background.Value = value; }
public Brush hoverbackground { get => _hoverbackground.Value; private set => _hoverbackground.Value = value; }
public Brush border { get => _border.Value; private set => _border.Value = value; }
public string summary { get => _summary.Value; private set => _summary.Value = value; }
public bool is_exchange_order { get => _is_exchange_order.Value; private set => _is_exchange_order.Value = value; }
public string summary => _root_nodes().Any() ? $"Badge containing root orders: {string.Join(",", _root_nodes().Select(o => o.Key))}" : string.Empty;
public bool has_multiple_orders_at_same_level { get => _has_multiple_orders_at_same_level.Value; private set => _has_multiple_orders_at_same_level.Value = value; }
public bool is_hedge_order { get => _is_hedge_order.Value; private set => _is_hedge_order.Value = value; }
public Brush parent_algo_type_brush { get => _parent_algo_type_brush.Value; private set => _parent_algo_type_brush.Value = value; }
// the setter is public so that the LadderVM can set this field
public CustomOrderDefinition.CustomOneofCase parent_algo_type { get => _parent_algo_type.Value; set => _parent_algo_type.Value = value; }
public CustomOrderDefinition.CustomOneofCase algo_type { get => _algo_type.Value; private set => _algo_type.Value = value; }
public string algo_studio_abbreviation { get => _algo_studio_abbreviation.Value; private set => _algo_studio_abbreviation.Value = value; }
public IEnumerable<OrderRecordImmutable> orders => _orders.Values.Select(o => o.order);
public IReadOnlyList<uint> order_keys => _orders.Keys.ToReadOnlyList();
public IEnumerable<OrderRecordImmutable> root_orders => _root_nodes();
void _cancel_order() {
foreach (var op in _orders.Values) {
model.order_gateway.cancel_order(op.order).ignore();
foreach (var order in _root_nodes()) {
model.order_gateway.cancel_order(order).ignore();
}
}
void _crack_order() {
var distinct_parent_keys = _orders.Values.Where(op => op.order.has_parent()).Select(x => x.order.ParentKey).Distinct();
model.order_gateway.crack_orders(distinct_parent_keys).ignore();
}
public async void try_set_price(OrderRecordImmutable order, double new_price) {
// don't change stop or stop_limits => server crash
if (order.order_type() == bts.api.orderman.CustomOrderDefinition.CustomOneofCase.Stop || order.order_type() == bts.api.orderman.CustomOrderDefinition.CustomOneofCase.StopLimit) return;
@@ -212,7 +270,7 @@ namespace mbts.spark {
_phantom_price = new_price;
RaisePropertyChanged(nameof(price));
++_n_active_price_change_ops;
if (await _parent.model.order_gateway.update_order(order, new_price, order.State.Quantity)) {
if (await _owner.model.order_gateway.update_order(order, new_price, order.State.Quantity)) {
// wait 3 secs, then give up if price change still hasn't taken effect
await Task.Delay(3000);
}
@@ -234,14 +292,14 @@ namespace mbts.spark {
price_filter.minimum.value = real_price;
price_filter.maximum.value = real_price;
var obvm = new OrderBookVM(null, _parent.bus, null, _parent.model.order_gateway, null, true);
var state = new bts.shell.OrderBookWindowFactory.OrderBookStartupData(
new bts.shell.OrderBookWindowFactory.FilterStates {
security_criteria = new SecurityCriteria(_parent.security),
var obvm = new OrderBookVM(null, _owner.bus, null, _owner.model.order_gateway, null, true);
var state = new bts.shell.orderbook.OrderBookStartupData(
new bts.shell.orderbook.FilterStates {
security_criteria = new SecurityCriteria(_owner.security),
price = price_filter,
statuses = new[] { OrderStatus.Working }
},
bts.shell.OrderBookWindowFactory.OrderBookStartupData.default_mini_grid,
bts.shell.orderbook.OrderBookStartupData.default_mini_grid,
use_defaults: false
);
// NOTE:
@@ -249,30 +307,33 @@ namespace mbts.spark {
// if these EditXXX ever support settings.
// The end result after this group of lines will be
// ------------------------
// | Cancel | Qty | Price | ...
// ------------------------
// ----------------------------------------------
// | Cancel | Update | Qty | Price | Commit | Attach | Detach | ...
// ----------------------------------------------
//
var price_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Price.ToString());
obvm.columns.Insert(0, new OrderBookVM.AttachDetachColumn(typeof(MiniOrderBookView)));
obvm.columns.Insert(0, new OrderBookVM.CommitColumn());
var price_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookGridView.ColumnKeys.Price.ToString());
var price_index = obvm.columns.indexOf(price_col);
if (price_index > -1) {
var existing_editprice_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditablePrice.ToString());
var existing_editprice_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookGridView.ColumnKeys.EditablePrice.ToString());
var existing_editprice_index = obvm.columns.indexOf(existing_editprice_col);
if (existing_editprice_index == -1) {
// insert the edit price column at the beginning
obvm.columns.Insert(0, new OrderBookVM.EditablePriceColumn());
}
}
var qty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Size.ToString());
var qty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookGridView.ColumnKeys.Size.ToString());
var qty_index = obvm.columns.indexOf(qty_col);
if (qty_index > -1) {
var existing_editqty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditableQty.ToString());
var existing_editqty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookGridView.ColumnKeys.EditableQty.ToString());
var existing_editqty_index = obvm.columns.indexOf(existing_editqty_col);
if (existing_editqty_index == -1) {
// insert the edit quantity column at the beginning
obvm.columns.Insert(0, new OrderBookVM.EditableQtyColumn());
}
}
obvm.columns.Insert(0, new OrderBookVM.UpdateColumn(typeof(MiniOrderBookView)));
obvm.columns.Insert(0, new OrderBookVM.CancelColumn(nameof(OrderBookRowVM.cancel_command)));
obvm.load_from(state);
@@ -280,7 +341,7 @@ namespace mbts.spark {
}
public override string ToString() {
return $"Price: {this.price} Order Count: {this.orders.Count()} PIQ: {(this.min_piq.HasValue ? this.min_piq.Value.ToString() : "")}";
return $"Price: {price} Order Count: {_orders.Values.Count()} Root Order Count: {_root_nodes().Count()} PIQ: {(this.min_piq.HasValue ? this.min_piq.Value.ToString() : "")}";
}
}
}
@@ -1,4 +1,5 @@
using bts.api;
using bts.collections;
using bts.utils;
using mbts.collections;
using mbts.ui;
@@ -51,6 +52,7 @@ namespace mbts.spark {
_tif_visibility = new OrderEntryPanelItemVisibilityVM("Time In Force"),
_order_type_visibility = new OrderEntryPanelItemVisibilityVM("Order Type"),
_account_position_visibility = new OrderEntryPanelItemVisibilityVM("Account Position"),
_day_position_visibility = new OrderEntryPanelItemVisibilityVM("Day Position"),
_size_preset_visibility = new OrderEntryPanelItemVisibilityVM("Size Presets"),
_auto_clear_visibility = new OrderEntryPanelItemVisibilityVM("Auto Clear"),
_clear_visibility = new OrderEntryPanelItemVisibilityVM("Clear"),
@@ -163,6 +165,7 @@ namespace mbts.spark {
yield return _tif_visibility;
yield return _order_type_visibility;
yield return _account_position_visibility;
yield return _day_position_visibility;
yield return _size_preset_visibility;
yield return _clear_visibility;
yield return _auto_clear_visibility;
@@ -198,7 +201,7 @@ namespace mbts.spark {
}
public string key => GridOrLadderSettingsSelectorKeys.Ladder;
public string key => GridSettingsTemplateKeys.Ladder;
public void load(LadderSettings settings) {
_current = settings;
@@ -231,6 +234,7 @@ namespace mbts.spark {
_tif_visibility.show = settings.tif_visible;
_order_type_visibility.show = settings.order_type_visible;
_account_position_visibility.show = settings.account_position_visible;
_day_position_visibility.show = settings.day_position_visible;
_size_preset_visibility.show = settings.size_presets_visible;
_clear_visibility.show = settings.clear_visible;
_auto_clear_visibility.show = settings.auto_clear_visible;
@@ -277,6 +281,7 @@ namespace mbts.spark {
clone.tif_visible = _tif_visibility.show;
clone.order_type_visible = _order_type_visibility.show;
clone.account_position_visible = _account_position_visibility.show;
clone.day_position_visible = _day_position_visibility.show;
clone.size_presets_visible = _size_preset_visibility.show;
clone.auto_clear_visible = _auto_clear_visibility.show;
clone.clear_visible = _clear_visibility.show;
@@ -448,6 +453,7 @@ namespace mbts.spark {
passive = new VolumeAtPriceAnimationVM("Passive", new VolumeAtPriceSampleData(this, 72, 127, 198));
total = new VolumeAtPriceAnimationVM("Total", new VolumeAtPriceSampleData(this, 81, 100, 144));
_any_visible = backing(nameof(any_visible), () => volume_components.Any(vc => vc.show_numbers || vc.bars.enabled));
_show_last_trade = backing(nameof(show_last_trade), false);
foreach (var vc in volume_components) {
vc.show_numbers_changed += _any_visible.refresh;
vc.bars.enabled_changed += _any_visible.refresh;
@@ -462,6 +468,9 @@ namespace mbts.spark {
public VolumeAtPriceAnimationVM sell { get; }
public VolumeAtPriceAnimationVM passive { get; }
public VolumeAtPriceAnimationVM total { get; }
readonly Property<bool> _show_last_trade;
public bool show_last_trade { get => _show_last_trade.Value; set => _show_last_trade.Value = value; }
public bool any_visible => volume_components.Any(vc => vc.show_numbers || vc.bars.enabled);
public IEnumerable<VolumeAtPriceAnimationVM> volume_components {
@@ -480,6 +489,7 @@ namespace mbts.spark {
sell.assign_from(settings.sell);
passive.assign_from(settings.passive);
total.assign_from(settings.total);
show_last_trade = settings.show_ltq;
}
public VolumeColumnSettings get_settings() =>
@@ -489,6 +499,7 @@ namespace mbts.spark {
sell = sell.get_settings(),
passive = passive.get_settings(),
total = total.get_settings(),
show_ltq = show_last_trade
};
}
@@ -2,6 +2,7 @@
using bts.api.orderman;
using bts.shell;
using bts.spark;
using bts.utils;
using mbts.co2;
using mbts.persist;
using mbts.ui;
@@ -24,28 +25,44 @@ namespace mbts.spark {
readonly IBus _bus;
readonly LadderModel _model;
readonly Property<double> _high, _low, _change, _open, _settlement, _position, _left_panel_width, _zoom_factor;
readonly Property<double> _high, _low, _change, _open, _settlement, _left_panel_width, _zoom_factor;
//readonly Property<double> _window_width, _window_height;
readonly Property<uint> _orders_column_width, _bid_column_width, _price_column_width, _ask_column_width, _last_column_width, _volume_column_width, _piq_column_width;
readonly Property<ulong> _volume;
readonly Property<bool> _auto_clear, _show_day, _show_high, _show_low, _show_open, _show_settle, _show_volume, _show_account,
_show_tif, _show_order_type, _show_account_position, _show_size_preset, _show_auto_clear, _show_clear, _show_cancel_buys,
readonly Property<bool> _auto_clear;
readonly PropertyAutoWrapper<LadderSettings, bool> _show_day, _show_high, _show_low, _show_open, _show_settle, _show_volume, _show_account,
_show_tif, _show_order_type, _show_size_preset, _show_auto_clear, _show_clear, _show_cancel_buys,
_show_cancel_sells, _show_cancel_all, _show_zoom;
readonly Property<Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
readonly PropertyAutoWrapper<LadderSettings, Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
double _position, _day_position;
bool _show_account_position, _show_day_position;
IWindowSettingsManager<LadderSettings> _settings_manager;
ulong _misc_time = 0;
ulong _last_trade_time = 0;
IEqualityComparer<double> _equality;
Dictionary<double, LadderOrderVM> _slot_price_vs_orders;
IReadOnlyDictionary<uint, OrderSecurity> _cache = DictionaryHelper.empty<uint, OrderSecurity>();
public LadderVM(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager, LadderSettings init) {
public static LadderVM create(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager, LadderSettings init) {
var non_shared_wsm = settings_manager.as_non_shared();
return new LadderVM(bus, model, settings_manager, non_shared_wsm, init, new OrderPropertiesEditorVM(model.order_gateway, Observable.Return(model.security_md), settings_manager.subscribe_current().Select(s => new InlineOrderProperties(model.order_gateway, s))));
}
public static LadderVM create_readonly(IBus bus, LadderModel model) {
var default_settings = LadderSettings.Default;
return create_readonly(bus, model, new WindowSettingsManager<LadderSettings>.DisconnectedImpl(default_settings), default_settings);
}
public static LadderVM create_readonly(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager, LadderSettings init) =>
new LadderVM(bus, model, settings_manager, settings_manager.as_non_shared(), init);
LadderVM(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager, IWindowSettingsManagerNonShared<LadderSettings> non_shared_wsm, LadderSettings init, OrderPropertiesEditorVM order_properties_vm = null) {
_bus = bus;
_model = model;
_equality = TickUtils.tick_equality_comparer(_model.security.ContractSpec);
_slot_price_vs_orders = new Dictionary<double, LadderOrderVM>(_equality);
var non_shared_wsm = settings_manager.as_non_shared();
_settings_manager = non_shared_wsm;
order_properties_vm = new OrderPropertiesEditorVM(_model.order_gateway, Observable.Return(_model.security_md), _settings_manager.subscribe_current().Select(s => new InlineOrderProperties(_model.order_gateway, s)));
this.order_properties_vm = order_properties_vm;
_high = backing(nameof(high), double.NaN);
_low = backing(nameof(low), double.NaN);
_change = backing(nameof(change), double.NaN);
@@ -63,46 +80,51 @@ namespace mbts.spark {
_left_panel_width = backing(nameof(left_panel_width), ws.order_entry_panel_width);
_zoom_factor = backing(nameof(zoom_factor), ws.zoom_factor);
// remember: account_positions needs to subscribe to selected_account; both the sub and account_positions need to be disposed
var account_positions = new KeyDrivenObservableSeries<string, double>(a => _model.order_gateway.security_total_position(security.Id, a));
_position = backing(nameof(position), account_positions.OnDispatcher());
_volume = backing(nameof(volume), 0ul);
_auto_clear = backing(nameof(auto_clear), false);
_status = backing(nameof(status_message), "");
_status_brush = backing(nameof(status_background), _transparent_status_brush);
// brush props
_bid_background = brush_prop(nameof(bid_background), s => s.bid_column.background.rgb_brush());
_bid_foreground = brush_prop(nameof(bid_foreground), s => s.bid_column.foreground.rgb_brush());
_ask_background = brush_prop(nameof(ask_background), s => s.ask_column.background.rgb_brush());
_ask_foreground = brush_prop(nameof(ask_foreground), s => s.ask_column.foreground.rgb_brush());
Property<Brush> brush_prop(string name, Func<LadderSettings, Brush> get_brush) =>
backing(name, settings_manager.subscribe_current().OnDispatcher(), get_brush);
// bool visibility props
_show_day = bool_prop(nameof(show_day), s => s.day_visible);
_show_high = bool_prop(nameof(show_high), s => s.high_visible);
_show_low = bool_prop(nameof(show_low), s => s.low_visible);
_show_open = bool_prop(nameof(show_open), s => s.open_visible);
_show_settle = bool_prop(nameof(show_settle), s => s.settle_visible);
_show_volume = bool_prop(nameof(show_volume), s => s.volume_visible);
_show_account = bool_prop(nameof(show_account), s => s.account_visible);
_show_tif = bool_prop(nameof(show_tif), s => s.tif_visible);
_show_order_type = bool_prop(nameof(show_order_type), s => s.order_type_visible);
_show_account_position = bool_prop(nameof(show_account_position), s => s.account_position_visible);
_show_size_preset = bool_prop(nameof(show_size_preset), s => s.size_presets_visible);
_show_auto_clear = bool_prop(nameof(show_auto_clear), s => s.auto_clear_visible);
_show_clear = bool_prop(nameof(show_clear), s => s.clear_visible);
_show_cancel_buys = bool_prop(nameof(show_cancel_buys), s => s.cancel_buys_visible);
_show_cancel_sells = bool_prop(nameof(show_cancel_sells), s => s.cancel_sells_visible);
_show_cancel_all = bool_prop(nameof(show_cancel_all), s => s.cancel_all_visible);
_show_zoom = bool_prop(nameof(show_zoom), s => s.zoom_slider_visible);
Property<bool> bool_prop(string name, Func<LadderSettings, bool> get_bool) =>
backing(name, settings_manager.subscribe_current().OnDispatcher(), get_bool);
// All settings observers should be aggregated together, so they can all share the same subscription to the settings
// (these subscriptions are not horribly expensive, but they're not free either - even more so with the .OnDispatcher() extension).
var settings_observer = new CompositeObserver<LadderSettings> {
// brush props
(_bid_background = brush_prop(nameof(bid_background), s => s.bid_column.background)).set,
(_bid_foreground = brush_prop(nameof(bid_foreground), s => s.bid_column.foreground)).set,
(_ask_background = brush_prop(nameof(ask_background), s => s.ask_column.background)).set,
(_ask_foreground = brush_prop(nameof(ask_foreground), s => s.ask_column.foreground)).set,
// bool visibility props
(_show_day = bool_prop(nameof(show_day), s => s.day_visible)).set,
(_show_high = bool_prop(nameof(show_high), s => s.high_visible)).set,
(_show_low = bool_prop(nameof(show_low), s => s.low_visible)).set,
(_show_open = bool_prop(nameof(show_open), s => s.open_visible)).set,
(_show_settle = bool_prop(nameof(show_settle), s => s.settle_visible)).set,
(_show_volume = bool_prop(nameof(show_volume), s => s.volume_visible)).set,
(_show_account = bool_prop(nameof(show_account), s => s.account_visible)).set,
(_show_tif = bool_prop(nameof(show_tif), s => s.tif_visible)).set,
(_show_order_type = bool_prop(nameof(show_order_type), s => s.order_type_visible)).set,
(_show_size_preset = bool_prop(nameof(show_size_preset), s => s.size_presets_visible)).set,
(_show_auto_clear = bool_prop(nameof(show_auto_clear), s => s.auto_clear_visible)).set,
(_show_clear = bool_prop(nameof(show_clear), s => s.clear_visible)).set,
(_show_cancel_buys = bool_prop(nameof(show_cancel_buys), s => s.cancel_buys_visible)).set,
(_show_cancel_sells = bool_prop(nameof(show_cancel_sells), s => s.cancel_sells_visible)).set,
(_show_cancel_all = bool_prop(nameof(show_cancel_all), s => s.cancel_all_visible)).set,
(_show_zoom = bool_prop(nameof(show_zoom), s => s.zoom_slider_visible)).set,
s => {
if (Set(ref _show_account_position, s.account_position_visible, nameof(show_account_position)) | Set(ref _show_day_position, s.day_position_visible, nameof(show_day_position))) {
RaisePropertyChanged(nameof(position_display_text));
}
}
};
PropertyAutoWrapper<LadderSettings, bool> bool_prop(string name, Func<LadderSettings, bool> get_bool) =>
backing(name, init, get_bool, EqualityComparer.Create(get_bool));
PropertyAutoWrapper<LadderSettings, Brush> brush_prop(string name, Func<LadderSettings, uint> get_color_uint) =>
backing(name, init, s => get_color_uint(s).rgb_brush(), EqualityComparer.Create(get_color_uint));
//
//_window_height = backing(nameof(window_height), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 600);
//_window_width = backing(nameof(window_width), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 420);
position_click_command = new DelegateCommand(() => size_calculator.value = DoubleHelper.safe_convert_to_uint(Math.Abs(position)));
send_order_command = new DelegateCommand<(double price, Side? side, bool size_from_alternate)>(send_order);
position_click_command = new DelegateCommand(() => size_calculator.value = DoubleHelper.safe_convert_to_uint(Math.Abs(_show_day_position ? day_position : position)));
send_order_command = new DelegateCommand<(double price, Side? side, MouseButton button)>(send_order);
cancel_command = new AsyncCommand<LadderModel.CancelType>(_model.cancel_orders, true);
show_book_command = new DelegateCommand(_show_book);
show_ticket_command = new DelegateCommand(_show_ticket);
@@ -125,26 +147,20 @@ namespace mbts.spark {
orders = new ObservableCollection<LadderOrderVM>();
_dispose = Disposable.Create(
settings_manager.subscribe_current().OnDispatcher().Subscribe(settings_observer),
settings_command,
non_shared_wsm,
settings.OnDispatcher().Subscribe(s => order_properties_vm.on_favorite_algos(s.favorite_algos)),
order_properties_vm,
order_properties_vm.observe_account(true).Select(a => a?.name ?? string.Empty).Subscribe(account_positions),
account_positions,
get_order_properties_editor_hooks(),
size_calculator = new CalculatorLogic(0, _settings_manager.subscribe_current().Select(s => s.calculator_buttons)),
bus.subscribe(SparkTags.LogActivity).OnDispatcher().Subscribe(status_update),
model.order_gateway.working_orders_with_piq.OnDispatcher().Subscribe(working => {
// we need to hook this up to model.orders which is filtered for just the security
model.orders.OnDispatcher().Subscribe(working => {
// Here we manage N orders to 1 price slot in the ladder
Dictionary<double, HashSet<uint>> orders_used_in_this_update = new Dictionary<double, HashSet<uint>>(_equality);
var ladders_orders =
working.Values
.Where(o =>
o.order.Def.SecurityKey == security.Id &&
(o.order.HasState && o.order.State.DisplayStatus == OrderState.Types.DisplayStatus.Visible));
_cache = working.state;
foreach (var osec in ladders_orders) {
var wo = osec.order;
foreach (var os in _cache.Values) {
var wo = os.order;
var slot_price_target = wo.price();
var order_type = wo.order_type();
var filled = wo.filled();
@@ -158,9 +174,9 @@ namespace mbts.spark {
}
if (_slot_price_vs_orders.TryGetValue(slot_price_target, out LadderOrderVM slot_vm)) {
slot_vm.add_order((wo, osec.piq));
slot_vm.add_order((wo, os.piq, os.algo, os.meta));
} else {
slot_vm = new LadderOrderVM((wo, osec.piq), slot_price_target, this, _bus, _settings_manager.subscribe_current().Select(s => s.badge_style));
slot_vm = new LadderOrderVM((wo, os.piq, os.algo, os.meta), slot_price_target, this, _bus, _settings_manager.subscribe_current().Select(s => s.badge_style));
_slot_price_vs_orders[slot_price_target] = slot_vm;
}
@@ -177,7 +193,7 @@ namespace mbts.spark {
// remove any orders from the lovm that aren't in here
foreach(var kvp in orders_used_in_this_update) {
var slot_vm = _slot_price_vs_orders[kvp.Key];
foreach(var existing_order_key in slot_vm.orders.Select(o => o.Key).ToList()) {
foreach(var existing_order_key in slot_vm.order_keys) {
if (!kvp.Value.Contains(existing_order_key)) {
slot_vm.remove_order(existing_order_key);
}
@@ -187,25 +203,6 @@ namespace mbts.spark {
// update dirty brushes and text
foreach (var lovm in _slot_price_vs_orders.Values) {
// the parent algo is only valid when there is only a single order at a level
if (lovm.orders.Count() == 1) {
var only_order = lovm.orders.First();
if (only_order.has_parent()) {
var traversal_candidate = only_order;
bool exit_early = false;
while (!exit_early && traversal_candidate.ParentKey != 0) {
if (working.TryGetFirst(o => o.Key == traversal_candidate.ParentKey, out var first))
traversal_candidate = first.Value.order;
else
exit_early = true;
}
if (!exit_early)
// we have successfully reached the root
lovm.parent_algo_type = traversal_candidate.Def.CustomDef.CustomCase;
}
// else this a single order, so do nothing
}
lovm.update();
}
@@ -226,6 +223,29 @@ namespace mbts.spark {
WorkLoop.Start($"Ladder '{security}'", _tick),
Disposable.Create(() => _on_disposed_impl?.Invoke(true))
);
IDisposable get_order_properties_editor_hooks() {
if (order_properties_vm == null)
return Disposable.empty;
var position_src = order_properties_vm.observe_account(true)
.Select(a => a?.name ?? string.Empty)
.SelectManyInSeries(a => _model.order_gateway.on_security_positions(security.Id)
.Select(table => table.state.Values.FirstOrDefault(pos => pos.Item2.Account == a, tup => tup.Item2, (bts.api.position.PositionUnitImmutable?)null)));
return Disposable.Create(
settings.OnDispatcher().Subscribe(s => order_properties_vm.on_favorite_algos(s.favorite_algos)),
order_properties_vm,
position_src.OnDispatcher().Subscribe(pos => {
double total, day;
if (pos.HasValue) {
total = pos.Value.total_pos();
day = pos.Value.day_pos();
} else {
total = day = double.NaN;
}
if ((Set(ref _position, total, nameof(position)) | Set(ref _day_position, day, nameof(day_position))) && (show_day_position || show_account_position))
RaisePropertyChanged(nameof(position_display_text));
}),
position_src.Connect());
}
}
public IBus bus => _bus;
@@ -236,7 +256,7 @@ namespace mbts.spark {
// we return only entries who have piqs and whose piqs are not uint.Max
// uint.Max happens when the server is restarted but the client is still up
// maybe long term we don't show this data while the exchange connection is down
public IEnumerable<(double, uint)> price_vs_piq => _slot_price_vs_orders.Values.Where(result => result.min_piq.HasValue && result.min_piq.Value != uint.MaxValue).Select(o => (o.price, o.min_piq.Value));
public IEnumerable<(double, uint)> price_vs_piq => _slot_price_vs_orders.Values.Where(result => result.min_piq.HasValue && result.min_piq.Value != uint.MaxValue).Select(o => (o.price, o.min_piq.Value));
public bool is_synthetic => _model.security.ContractCase == Security.ContractOneofCase.Synthetic;
Property<string> _status;
@@ -270,40 +290,53 @@ namespace mbts.spark {
public double change => _change.Value;
public double open => _open.Value;
public double settlement => _settlement.Value;
public double position => _position.Value;
public double position => _position;
public double day_position => _day_position;
public ulong volume => _volume.Value;
public bool auto_clear { get => _auto_clear.Value; set => _auto_clear.set(value); }
public CalculatorLogic size_calculator { get; }
/// Important: <see cref="order_properties_vm"/> can be null.
public OrderPropertiesEditorVM order_properties_vm { get; }
public ObservableCollection<LadderOrderVM> orders { get; }
public IAsyncCommand cancel_command { get; }
public ICommand send_order_command { get; }
public ICommand show_book_command { get; }
public ICommand show_ticket_command { get; }
public Brush bid_background => _bid_background.Value;
public Brush bid_foreground => _bid_foreground.Value;
public Brush ask_background => _ask_background.Value;
public Brush ask_foreground => _ask_foreground.Value;
public Brush bid_background => _bid_background.view;
public Brush bid_foreground => _bid_foreground.view;
public Brush ask_background => _ask_background.view;
public Brush ask_foreground => _ask_foreground.view;
public DialogCommand settings_command { get; }
public bool show_day { get => _show_day.Value; set => _show_day.set(value); }
public bool show_high { get => _show_high.Value; set => _show_high.set(value); }
public bool show_low { get => _show_low.Value; set => _show_low.set(value); }
public bool show_open { get => _show_open.Value; set => _show_open.set(value); }
public bool show_settle { get => _show_settle.Value; set => _show_settle.set(value); }
public bool show_volume { get => _show_volume.Value; set => _show_volume.set(value); }
public bool show_account { get => _show_account.Value; set => _show_account.set(value); }
public bool show_tif { get => _show_tif.Value; set => _show_tif.set(value); }
public bool show_order_type { get => _show_order_type.Value; set => _show_order_type.set(value); }
public bool show_account_position { get => _show_account_position.Value; set => _show_account_position.set(value); }
public bool show_size_preset { get => _show_size_preset.Value; set => _show_size_preset.set(value); }
public bool show_auto_clear { get => _show_auto_clear.Value; set => _show_auto_clear.set(value); }
public bool show_clear { get => _show_clear.Value; set => _show_clear.set(value); }
public bool show_cancel_buys { get => _show_cancel_buys.Value; set => _show_cancel_buys.set(value); }
public bool show_cancel_sells { get => _show_cancel_sells.Value; set => _show_cancel_sells.set(value); }
public bool show_cancel_all { get => _show_cancel_all.Value; set => _show_cancel_all.set(value); }
public bool show_zoom { get => _show_zoom.Value; set => _show_zoom.set(value); }
public bool show_day => _show_day.view;
public bool show_high => _show_high.view;
public bool show_low => _show_low.view;
public bool show_open => _show_open.view;
public bool show_settle => _show_settle.view;
public bool show_volume => _show_volume.view;
public bool show_account => _show_account.view;
public bool show_tif => _show_tif.view;
public bool show_order_type => _show_order_type.view;
public bool show_account_position => _show_account_position;
public bool show_day_position => _show_day_position;
public bool show_size_preset => _show_size_preset.view;
public bool show_auto_clear => _show_auto_clear.view;
public bool show_clear => _show_clear.view;
public bool show_cancel_buys => _show_cancel_buys.view;
public bool show_cancel_sells => _show_cancel_sells.view;
public bool show_cancel_all => _show_cancel_all.view;
public bool show_zoom => _show_zoom.view;
public double left_panel_width { get => _left_panel_width.Value; set => _left_panel_width.set(value); }
public double zoom_factor { get => _zoom_factor.Value; set => _zoom_factor.set(value); }
public string position_display_text {
get {
if (show_day_position) {
string day_pos_text = SparkUtils.fmt_position(day_position);
return show_account_position ? $"{day_pos_text} ({SparkUtils.fmt_position(position)})" : day_pos_text;
} else if (show_account_position) {
return SparkUtils.fmt_position(position);
} else return string.Empty;
}
}
//public double window_height { get => _window_height.Value; set => _window_height.set(value); }
//public double window_width { get => _window_width.Value; set => _window_width.set(value); }
public uint orders_column_width { get => _orders_column_width.Value; set => _orders_column_width.set(value); }
@@ -315,6 +348,22 @@ namespace mbts.spark {
public uint volume_column_width { get => _volume_column_width.Value; set => _volume_column_width.set(value); }
internal LadderModel model => _model;
internal IObservable<LadderSettings> settings => _settings_manager.subscribe_current();
internal OrderRecordImmutable get_root_at_price((OrderRecordImmutable order, uint? _, AlgoData? __, AlgoDefinitionMetadata? meta) op) {
var order = op.order;
double p = order.price();
foreach (var o in lineage(order.ParentKey)) {
if (_equality.Equals(p, o.price()))
order = o;
}
return order;
}
internal IEnumerable<OrderRecordImmutable> lineage(uint key) {
while (key != 0 && _cache.TryGetValue(key, out var op)) {
yield return op.order;
key = op.order.ParentKey;
}
}
public LadderSettings get_save_state() {
var state = _settings_manager.current_settings();
@@ -324,6 +373,8 @@ namespace mbts.spark {
}
public void key_down(KeyEventArgs e) {
if (order_properties_vm == null)
return;
// the ctrl modifier requirement helps avoid keyboard-input collisions w/ algo-templates popup
if (!KeyboardExtensions.IsCtrlDown()) return;
var new_algo = order_properties_vm.algo_definitions.FirstOrDefault(o => o.algo_root.key_match(e));
@@ -333,32 +384,41 @@ namespace mbts.spark {
}
}
public async void send_order((double price, Side? side, bool size_from_alternate) args) {
public void send_order((double price, Side? side, MouseButton buton) args) {
if (args.buton == MouseButton.Middle) return;
var size_from_alternate = args.buton == MouseButton.Right;
if (order_properties_vm == null)
return;
var s = _settings_manager.current_settings();
if (DoubleHelper.IsNaNOrInfinity(args.price) || !args.side.HasValue || HasErrors || order_properties_vm.HasErrors || (args.size_from_alternate && s.alternate_order_size == 0))
if (DoubleHelper.IsNaNOrInfinity(args.price) || !args.side.HasValue || HasErrors || order_properties_vm.HasErrors || (size_from_alternate && s.alternate_order_size == 0)) {
return;
else if (!args.size_from_alternate && (!size_calculator.value.HasValue || size_calculator.value.Value == 0)) {
size_calculator.flash_error();
} else {
var order = order_properties_vm.create(args.side.Value);
if (order.CustomDef != null) {
if (order.CustomDef.Stop != null) {
order.CustomDef.Stop.StopPrice = args.price;
} else if (order.CustomDef.StopLimit != null) {
// swap the prices here so that the level clicked
// in the ladder is the StopPrice (trigger) and the Price
// is what was specificed in the extra parameter box
double temp_price = order.CustomDef.StopLimit.StopPrice;
order.CustomDef.StopLimit.StopPrice = args.price;
args.price = temp_price;
}
}
uint size = args.size_from_alternate ? s.alternate_order_size : (uint)Math.Abs(size_calculator.value.Value);
await _model.order_gateway.send_order(order, args.price, size);
if (auto_clear)
size_calculator.value = 0;
}
if (!size_from_alternate && (!size_calculator.value.HasValue || size_calculator.value.Value == 0)) {
size_calculator.flash_error();
}
else {
uint size = size_from_alternate ? s.alternate_order_size : (uint)Math.Abs(size_calculator.value ?? 0);
send_order_async(args.side.Value, size, args.price).ignore();
}
}
public async Task send_order_async(Side side, uint size, double price) {
var order = order_properties_vm.create(side);
if (order.CustomDef != null) {
if (order.CustomDef.Stop != null) {
order.CustomDef.Stop.StopPrice = price;
} else if (order.CustomDef.StopLimit != null) {
// swap the prices here so that the level clicked
// in the ladder is the StopPrice (trigger) and the Price
// is what was specificed in the extra parameter box
double temp_price = order.CustomDef.StopLimit.StopPrice;
order.CustomDef.StopLimit.StopPrice = price;
price = temp_price;
}
}
await _model.order_gateway.send_order(order, price, size);
if (auto_clear)
size_calculator.value = 0;
}
void _update_header() {
@@ -382,15 +442,18 @@ namespace mbts.spark {
}
void _show_book() =>
_bus.post(SparkTags.ShowOrderBookTag, new bts.shell.OrderBookWindowFactory.OrderBookStartupData {
filters = new bts.shell.OrderBookWindowFactory.FilterStates {
security_criteria = new SecurityCriteria(security)
}
});
void _show_book() {
var ob_settings = bts.shell.orderbook.OrderBookStartupData.Default;
ob_settings.filters = new bts.shell.orderbook.FilterStates {
security_criteria = new SecurityCriteria(security)
};
_bus.post(SparkTags.ShowOrderBookTag, ob_settings);
}
void _show_ticket() =>
_bus.post(SparkTags.ShowOrderTicketTag, (new OrderDefinitionImmutable(order_properties_vm.create(Side.Buy)), 0d, 0u));
void _show_ticket() {
if (order_properties_vm != null)
_bus.post(SparkTags.ShowOrderTicketTag, (new OrderDefinitionImmutable(order_properties_vm.create(Side.Buy)), 0d, 0u));
}
void _tick() {
_update_header();
@@ -1,12 +1,14 @@
using bts;
using bts.api;
using bts.api.md.auxiliary;
using bts.collections;
using bts.utils;
using mbts.co2;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.extensions;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.ui.spark.algo;
using mbts.ui.utility.text;
using mbts.utils;
using System;
@@ -16,8 +18,10 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
@@ -54,14 +58,19 @@ namespace mbts.spark {
LadderVM _vm;
LadderTactileAdorner _tactile_adorner;
ulong _prices_updated;
NativePriceSize[] _bids, _asks;
bool _price_dirty = false;
NativePriceSize[] _bids = new NativePriceSize[NativePriceHelpers.COCONUT_MAX_DEPTH], _asks = new NativePriceSize[NativePriceHelpers.COCONUT_MAX_DEPTH];
bool _price_dirty = false, _piq_dirty = false;
double[] _price_list = null;
IComparer<double> _price_comparer;
double _last_origin_price;
MarketBreakLines _mkt_break_lines;
NativePriceSize _last_trade;
Func<NativePriceSize, string> _fmt_size = MDDisplayHelper.get_formatter_nps(MarketSizeDisplay.get_default());
int _old_tooltip_row = int.MaxValue;
double _old_last_trade_price = 0.0;
bool _tooltips_are_hooked_up = false;
ToolTip tooltip_view = new ToolTip();
IDisposable _parameterized_order_popup;
public LadderGridHost() {
AllowDrop = true;
@@ -82,6 +91,7 @@ namespace mbts.spark {
};
RecenterCommand = new DelegateCommand(_scroll_to_center);
DataContextChanged += (s, e) => _connect_vm();
IsVisibleChanged += (s, e) => _connect_vm();
Unloaded += (s, e) => {
Disposable.dispose(ref _settings_sub);
Disposable.dispose(ref _tick_sub);
@@ -97,7 +107,7 @@ namespace mbts.spark {
public event Action visible_columns_changed;
public IEnumerable<ColumnType> visible_columns =>
public IEnumerable<ColumnType> visible_column_types =>
columns.Where(c => c.settings.show)
.Select(c => c.column_type);
@@ -205,6 +215,26 @@ namespace mbts.spark {
set => SetValue(PIQColumnWidthProperty, value);
}
#region CanUserSendOrders
public static readonly DependencyProperty CanUserSendOrdersProperty =
DependencyProperty.Register(
nameof(CanUserSendOrders),
typeof(bool),
typeof(LadderGridHost),
new PropertyMetadata(true, (d, e) => ((LadderGridHost)d).OnCanUserSendOrdersChanged(e)));
public bool CanUserSendOrders {
get => (bool)GetValue(CanUserSendOrdersProperty);
set => SetValue(CanUserSendOrdersProperty, value);
}
event Action<bool> CanUserSendOrdersChanged;
void OnCanUserSendOrdersChanged(DependencyPropertyChangedEventArgs e) =>
CanUserSendOrdersChanged?.Invoke((bool)e.NewValue);
#endregion
#region Command
public ICommand Command {
get => (ICommand)GetValue(CommandProperty);
@@ -235,11 +265,15 @@ namespace mbts.spark {
case NotifyCollectionChangedAction.Add:
foreach (LadderOrderVM item in e.NewItems) {
_ordershost.add_order(item);
item.piq_changed += _set_piq_dirty;
_set_piq_dirty();
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (LadderOrderVM item in e.OldItems) {
_ordershost.remove_order(item);
item.piq_changed -= _set_piq_dirty;
_set_piq_dirty();
}
break;
case NotifyCollectionChangedAction.Reset:
@@ -250,13 +284,37 @@ namespace mbts.spark {
throw new NotImplementedException();
}
}
void _set_piq_dirty() { _piq_dirty = true; }
#endregion
Column[] columns { get; set; } = new Column[0];
IEnumerable<Column> _visible_columns => columns.Where(c => c.settings.show);
public void drag_out_of_bounds() =>
_tactile_adorner?.drag_out_of_bounds();
public void scroll_to_price(double price) {
if (_scrollViewer == null || _price_list.is_null_or_empty())
return;
if (security.ContractSpec.tickdiff(_price_list[0], price) is int target_row) {
int visible_row_count = (int)(_scrollViewer.ActualHeight / _settings.row_height);
int curr_row = visible_row_count + (int)(_scrollViewer.VerticalOffset / _settings.row_height);
if ((target_row > curr_row && target_row < _row_count) || (target_row < (curr_row - visible_row_count) && target_row >= 0))
_scroll_to_row(target_row);
}
}
public IObservable<ColumnType> subscribe_hover_column() =>
Observable.Create<ColumnType>(o => {
notify(Mouse.GetPosition(this));
return this.SubscribePreviewMouseMove((s, e) => notify(e.GetPosition(this)));
void notify(Point pt) =>
o.OnNext(_column_for_point(pt)?.column_type ?? ColumnType.None);
});
public override void OnApplyTemplate() {
base.OnApplyTemplate();
_scrollViewer = this.descendants_of_type<ScrollViewer>().First();
@@ -266,27 +324,141 @@ namespace mbts.spark {
}
protected override async void OnMouseDown(MouseButtonEventArgs e) {
if (_settings == null || e.Handled)
return;
Keyboard.ClearFocus();
Focus();
var point = e.GetPosition(this);
var column = _column_for_point(point);
if (column.column_type == ColumnType.Price && e.ChangedButton == MouseButton.Middle)
_scroll_to_center();
else if (_settings != null) {
var price = price_for_point(point);
Side? bs = null;
if (column.column_type == ColumnType.Bid) bs = Side.Buy;
if (column.column_type == ColumnType.Ask) bs = Side.Sell;
var pbs = (price, bs, e.ChangedButton == MouseButton.Right);
if (Command != null && Command.CanExecute(pbs)) {
if (bs.HasValue) {
using (_tactile_adorner.show_busy(_row_for_point(point), bs.Value == Side.Buy ? ColumnType.Bid : ColumnType.Ask))
await Command.ExecuteAsync(pbs);
} else
Command.Execute(pbs);
var price = price_for_point(point);
Side? bs = null;
if (column.column_type == ColumnType.Bid) bs = Side.Buy;
if (column.column_type == ColumnType.Ask) bs = Side.Sell;
if (e.ChangedButton == MouseButton.Middle) {
if (bs.HasValue) {
_send_parameterized_order(point, bs.Value);
} else if (column.column_type == ColumnType.Price) {
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown (Key.RightShift) ||
Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) {
show_mbo_popup(point).ignore();
} else {
_scroll_to_center();
}
}
}
base.OnMouseDown(e);
var pbs = (price, bs, e.ChangedButton);
if (Command is ICommand cmd && cmd.CanExecute(pbs)) {
if (bs.HasValue) {
using (_tactile_adorner.show_busy(_row_for_point(point), bs.Value == Side.Buy ? ColumnType.Bid : ColumnType.Ask))
await cmd.ExecuteAsync(pbs);
} else {
cmd.Execute(pbs);
}
}
}
void _send_parameterized_order(Point point, Side side) {
Disposable.dispose(ref _parameterized_order_popup);
if (side == Side.Undefined)
throw new ArgumentException("Side is undefined.", nameof(side));
if (!(_vm.size_calculator.value is uint size) || size == 0 || !(_vm.order_properties_vm?.selected_algo_definition?.algo_root?.can_expand ?? false))
return;
double price = _price_for_row(_row_for_point(point));
var grid = new Grid { DataContext = _vm };
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
var close_cmd = new DelegateCommand(() => Disposable.dispose(ref _parameterized_order_popup));
var send_order_cmd = new DelegateCommand(send_order);
LadderColumnSettings lcs = side == Side.Buy ? _settings.bid_column : _settings.ask_column;
grid.Children.Add(new AlgoParamsView { DataContext = _vm });
// dialog skirt
var dialog_skirt = new DialogSkirt {
RightItems = new[] {
new Button {
Background = lcs.background.rgb_brush(),
Command = send_order_cmd,
Content = $"{side} @ {security.spec_or_default().format(price)}",
Foreground = lcs.foreground.rgb_brush(),
Margin = new Thickness(4, 0, 4, 0),
Height = 22
},
new Button {
Content = "Cancel",
Command = close_cmd,
Margin = new Thickness(4, 0, 4, 0),
Height = 22
}
}
};
Grid.SetRow(dialog_skirt, 1);
grid.Children.Add(dialog_skirt);
// root
var root = new Border {
Background = BTSColors.common_background_brush,
BorderBrush = Brushes.DarkGray,
BorderThickness = new Thickness(1),
Child = grid
};
// popup
var popup = new Popup {
Child = root,
PlacementTarget = this,
PlacementRectangle = new Rect(point, point),
StaysOpen = false
};
popup.Closed += on_closed;
root.Loaded += (s, e) => {
if (_parameterized_order_popup == null)
_parameterized_order_popup = Disposable.Create(() => popup.IsOpen = false);
};
popup.PreviewMouseDown += (s, e) => {
if (e.ChangedButton == MouseButton.Middle) {
e.Handled = true;
Disposable.dispose(ref _parameterized_order_popup);
}
};
popup.IsOpen = true;
// local methods
void send_order() {
// Double check that size hasn't changed (if it has, do NOT send anything).
// While this should be impossible, sending an order with an unexpected size is dangerous enough to warrant redunant checks.
if (_vm.size_calculator.value == size) {
_vm.send_order_async(side, size, price).ignore();
} else {
Log.Ui.warn($"Parameterized algo popup '{side}' order was deliberately ignored by the ui: the 'quantity' box value changed during the lifetime of the popup (was {size} when the popup was first opened; currently is {_vm.size_calculator.value}).");
}
Disposable.dispose(ref _parameterized_order_popup);
}
void on_closed(object sender, EventArgs e) =>
_vm.order_properties_vm.algo_param_rows.revert_all();
}
private async Task show_mbo_popup(Point point) {
var query = MBOPopupModel.default_query.with(
security_id: _vm.model.security.Id,
price_level: price_for_point(point)
);
var model = new MBOPopupModel(_vm.model.mbo_service, _vm.model.security, query);
// Load the first set of orders so we know if there are any before showing the popup
var result = await model.load_orders();
if (result.success && result.value.orders.Count > 0) {
var view = new MBOPopupView(model, result.value);
var popup = new AutoClosePopup {
PlacementTarget = this,
PlacementRectangle = new Rect(point, point),
IsOpen = true,
Child = new Border {
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(0.5),
Child = new ScrollViewer {
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled,
Content = view,
}
}
};
view.RequestClose += (o, _) => popup.IsOpen = false;
}
}
protected override void OnMouseDoubleClick(MouseButtonEventArgs e) {
@@ -325,12 +497,24 @@ namespace mbts.spark {
uint? _ask_size_from_row(int row) =>
_size_from_row(row, _price_list, _asks, _vm.security.ContractSpec);
uint _volume_from_row(int row) {
if (_price_list != null) {
var price = _price_list[row];
var vapls = _volume_container.read_new_value();
if (vapls != null) {
return vapls.SelectFirstOrDefault(v => MathUtils.epsilon_equals(v.Price, price), v => v.Volume);
}
}
return 0u;
}
int _row_for_price(double price) => LadderRender.index_of(_price_list, price, _price_comparer);
#endregion
#region Column Utility Methods
Column _column_for_point(Point point) =>
columns.FirstOrDefault(c => c.x_right > point.X, columns.Length > 0 ? columns[columns.Length - 1] : Column.Empty);
_visible_columns.FirstOrDefault(c => c.x_right > point.X, columns.Length > 0 ? columns[columns.Length - 1] : Column.Empty);
double _column_start(ColumnType col_type) =>
columns.TakeWhile(c => c.column_type != col_type).Sum(c => c.settings.width);
@@ -433,18 +617,87 @@ namespace mbts.spark {
void _connect_vm() {
_dispose_resources();
_vm = DataContext as LadderVM;
if (_vm != null) {
if (!IsVisible)
return;
else if (_vm == null) {
_clear_visuals();
} else {
_vm.OnDisposed += _on_vm_disposed;
_prices_updated = 0;
_price_comparer = Comparer<double>.Create((x, y) => security.ContractSpec.tick_equals(x, y) ? 0 : y.CompareTo(x));
_price_dirty = true;
_piq_dirty = true;
_vm.on_tick += _tick;
_settings_sub = _vm.settings.OnDispatcher().Subscribe(_settings_updated);
_volume_sub = _vm.model.md_auxiliary_model.security_volume_at_price(security.Id).Subscribe(t => _volume_container.write_value(t.state.Values));
_volume_sub = _vm.model.order_gateway.md_auxiliary_model.security_volume_at_price(security.Id).Subscribe(t => _volume_container.write_value(t.state.Values));
_tick_sub = Disposable.Create(() => _vm.on_tick -= _tick);
}
}
#region tooltip handling
void _toggle_tooltips() {
if (_tooltips_are_hooked_up && !_settings.volume_column.show_ltq) {
this.MouseMove -= _mouse_move_handler;
_tooltips_are_hooked_up = false;
_hide_tool_tip();
// somehow even after hiding an unhooking the event handler, after toggling the setting off and
// then hovering over the Volume column, the tooltip will still display, so we explicitly set it
// to null here.
this.ToolTip = null;
}
if (_settings.volume_column.show_ltq && !_tooltips_are_hooked_up) {
this.MouseMove += _mouse_move_handler;
_tooltips_are_hooked_up = true;
}
}
void _mouse_move_handler(object sender, MouseEventArgs e) {
var pt = e.GetPosition(this);
if (_column_for_point(pt)?.column_type == ColumnType.Volume) {
var row = _row_for_point(pt);
// mark dirty so that we'll always get a value on our read
_volume_container.mark_dirty();
var volume = _volume_from_row(row);
if (volume > uint.MinValue && row != _old_tooltip_row) {
if (tooltip_view.IsOpen) {
_old_tooltip_row = row;
// we've changed rows, so hide the old tooltip
//System.Diagnostics.Debug.WriteLine(" Tooltip - Previous tooltip is visible, but we've changed rows");
_hide_tool_tip();
}
tooltip_view.Content = $"{volume}";
tooltip_view.IsOpen = true;
this.ToolTip = tooltip_view;
} else {
// got 0 volume or we're over the same row
if (volume == uint.MinValue) {
// The latest volume query resulted in a 0 volume
// Perhaps we read it too quickly, or perhaps we are on a row where there is no volume
// At any rate, we need this part hiding the tooltip so that we don't leave random tooltips around
// and we have to reset to old row so that when we go back to what could be our preivous row, the tooltip gets drawn again
//System.Diagnostics.Debug.WriteLine(" Tooltip - Volume is 0");
_hide_tool_tip();
//_old_tooltip_row = Int32.MaxValue;
}
}
} else {
//System.Diagnostics.Debug.WriteLine(" Tooltip - not over Volume column");
_hide_tool_tip();
}
}
void _hide_tool_tip() {
// I am leaving this here for posterity
// Apparently even when setting IsOpen to false first and then setting Content to null afterward,
// the tooltip will *still* display as empty for a brief moment, which looks odd. So, we just hide it now.
tooltip_view.IsOpen = false;
//tooltip_view.Content = null;
}
#endregion
void _on_vm_disposed(bool obj) =>
_dispose_resources();
@@ -473,11 +726,21 @@ namespace mbts.spark {
Storyboard.SetTargetProperty(anim, new PropertyPath(Behavior.AnimatedScrollViewerVerticalOffsetProperty));
sb.Begin();
} else {
_scrollViewer.ScrollToVerticalOffset(_row_count * (_settings.row_height / 2) - (ActualHeight / 2));
_scroll_to_row(_row_count / 2);
}
}
}
void _scroll_to_row(int row) =>
_scrollViewer.ScrollToVerticalOffset((row * _settings.row_height) - (ActualHeight / 2));
IObservable<bool> _subscribe_can_send_orders() =>
Observable.Create<bool>(o => {
o.OnNext(CanUserSendOrders);
CanUserSendOrdersChanged += o.OnNext;
return Disposable.Create(() => CanUserSendOrdersChanged -= o.OnNext);
});
#region Timer.Tick, Refresh Methods
void _tick() {
if (_settings != null) {
@@ -523,8 +786,10 @@ namespace mbts.spark {
if (depth_changed)
_refresh_sizes();
}
// TODO - detect changes in PIQ to selectively redraw
_refresh_piq();
if (_piq_dirty) {
_refresh_piq();
_piq_dirty = false;
}
_refresh_volume_at_price();
_last_origin_price = _mkt_break_lines.origin;
_price_dirty = false;
@@ -565,8 +830,7 @@ namespace mbts.spark {
if (!_settings.piq_column.show)
return;
int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer);
var piq_list = _vm.price_vs_piq;
LadderRender.render_piqs(_piq_vis_tup.visual, _last_vis_tup.glyph_context, origin_index, _price_list, piq_list.ToArray(), _settings.row_height, security.ContractSpec, _settings.piq_column);
LadderRender.render_piqs(_piq_vis_tup.visual, _last_vis_tup.glyph_context, origin_index, _price_list, _vm.price_vs_piq, _settings.row_height, security.ContractSpec, _settings.piq_column);
}
void _refresh_sizes() {
@@ -585,7 +849,18 @@ namespace mbts.spark {
var vapls = _volume_container.read_new_value();
if (vapls != null) {
Log.assert_d(_price_list != null);
LadderRender.render_volumes(_volume_vis_tup.visual, _volume_vis_tup.glyph_context, _price_list, vapls, _settings.row_height, security.ContractSpec, _settings.volume_column);
var ltp = _last_trade.price;
var last_index = LadderRender.index_of(_price_list, ltp, _price_comparer);
string direction_arrow = string.Empty;
if (MathUtils.epsilon_equals(ltp, _old_last_trade_price)) {
// do nothing if in here b/c direction_arrow already string.empty
} else if (ltp > _old_last_trade_price) {
direction_arrow = "▲";
} else {
direction_arrow = "▼";
}
LadderRender.render_volumes(_volume_vis_tup.visual, _volume_vis_tup.glyph_context, _price_list, vapls, _settings.row_height, security.ContractSpec, _settings.volume_column, last_index, _last_trade, direction_arrow);
_old_last_trade_price = ltp;
}
}
@@ -660,6 +935,26 @@ namespace mbts.spark {
_ordershost.Height = height;
_render_grid_background();
}
void _clear_visuals() {
if (_settings == null)
return;
_price_list = new double[_row_count];
for (int i = 0; i < _price_list.Length; i++)
_price_list[i] = double.NaN;
Array.Clear(_bids, 0, _bids.Length);
Array.Clear(_asks, 0, _asks.Length);
_price_dirty = true;
LadderRender.clear_prices(_price_vis_tup.visual, _row_count, _settings.row_height, _settings.price_column, _price_vis_tup.glyph_context);
_refresh_bid_mkt_break();
_refresh_ask_mkt_break();
_refresh_mid_mkt_break();
_refresh_session_low_line();
_refresh_session_high_line();
_refresh_session_range();
_refresh_last_trade();
_refresh_sizes();
}
#endregion
#region Market Break, Session Low/High Utils
@@ -748,6 +1043,7 @@ namespace mbts.spark {
case ColumnType.PIQ: SetCurrentValue(PIQColumnWidthProperty, col.settings.width); break;
case ColumnType.Volume:
SetCurrentValue(VolumeColumnWidthProperty, col.settings.width);
_toggle_tooltips();
_volume_container.mark_dirty();
break;
}
@@ -913,4 +1209,4 @@ namespace mbts.spark {
}
}
}
}
}
@@ -72,8 +72,7 @@ namespace mbts.spark {
panel.Children.Remove(item);
}
}
} //else
//Log.Ui.warn($"Failed to remove order ({order.id}): order not in {nameof(LadderOrdersHost)}'s {nameof(_orders)}.");
}
}
public void clear_orders() {
@@ -157,6 +156,11 @@ namespace mbts.spark {
nameof(OrderBookVM.is_row_collection_populated)
);
popup.Closed += (sender, event_args) => (popup.DataContext as IDisposable)?.Dispose();
popup.MouseUp += (sender, event_args) => {
if (event_args.ChangedButton == MouseButton.Middle) {
popup.IsOpen = false;
}
};
// This is useful for troubleshooting focus issues
//popup.LostKeyboardFocus += (ss, ee) => {
// System.Diagnostics.Debug.WriteLine($"Lost Focus Event - Current Focus: {mbts.utils.MiscUtils.who_has_keyboard_focus()}");
@@ -187,11 +191,8 @@ namespace mbts.spark {
Margin = Badge.DefaultMargin,
Orientation = Orientation.Horizontal,
MaxWidth = _col_width
//MaxHeight = _row_height,
//ClipToBounds = true
};
//var view_box = new Viewbox { Child = panel, Width = _col_width, Height = _row_height };
_position_panel(panel, price);
_stacks[price] = new PriceCellComponents(panel);
Children.Add(panel);
@@ -214,7 +215,7 @@ namespace mbts.spark {
Log.Ui.info("Drop - " + vm.summary);
double price = _parent.price_for_point(e.GetPosition(_parent));
if (price != vm.price) {
foreach (var order in vm.orders) {
foreach (var order in vm.root_orders) {
vm.try_set_price(order, price);
}
}
@@ -1,10 +1,10 @@
using bts;
using bts.api;
using bts.collections;
using bts.api.md.auxiliary;
using mbts.co2;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.spark;
using mbts.ui.utility.text;
using System;
using System.Collections.Generic;
@@ -86,6 +86,16 @@ namespace mbts.spark {
return result;
}
public static void clear_prices(DrawingVisual price, int row_count, double row_height, LadderColumnSettings settings, GlyphContext glyph_context) {
var foreground = settings.foreground.rgb_brush();
var fprices = _format_empty_prices(row_count, row_height, settings, glyph_context);
using (var dc = price.RenderOpen())
using (_draw_with_clip(dc, settings, row_height, row_count)) {
for (int x = 0; x < row_count; ++x)
dc.DrawGlyphRun(foreground, fprices[x]);
}
}
public static double[] render_prices(DrawingVisual price, GlyphContext glyph_context, double mid, double wrt_price, bool format_in_tick_terms, int row_count, double row_height,
LadderColumnSettings settings, ContractSpecificationImmutable spec) {
var dprices = gen_prices(mid, row_count, spec);
@@ -184,7 +194,10 @@ namespace mbts.spark {
IEnumerable<VolumeAtPriceLevelImmutable> data,
double row_height,
ContractSpecificationImmutable spec,
VolumeColumnSettings settings
VolumeColumnSettings settings,
int last_index,
NativePriceSize last_trade,
string direction_arrow
) {
if (!data.Any()) {
_clear_visual(dv);
@@ -207,28 +220,47 @@ namespace mbts.spark {
_clear_visual(dv);
return;
}
double max = data.Max(v => v.Volume);
// inflate the max so that the graph never fills the available space
double max = data.Max(v => v.Volume) * 1.1;
Brush text_brush = settings.general.foreground.rgb_brush();
_render_cells(dv, row_height, data.Select(d => (d.Price, d)), settings.general, prices, spec, render_cell);
/// local methods
void render_cell(DrawingContext dc, int r, VolumeAtPriceLevelImmutable vap) {
double y = row_height * r;
var last_trade_here = r == last_index;
// render the total first; each component (buy/sell/passive) will be rendered on top of the total
if (total_brush != null) {
// if we have to change the brush to indicate this is the last, save brush, draw, then restore brush
var temp = total_brush;
if (settings.show_ltq && last_trade_here) {
total_brush = BTSColors.yellow_brush;
}
render_bar(dc, 0, y, vap.Volume, total_brush, out double _);
total_brush = temp;
}
List<string> numbers_text = new List<string>();
double x_bar = 0;
foreach ((var get_volume, Brush bars, bool show_numbers) in components) {
uint v = get_volume(vap);
render_bar(dc, x_bar, y, v, bars, out double width);
double width;
if (settings.show_ltq && last_trade_here) {
render_bar(dc, x_bar, y, v, BTSColors.yellow_brush, out width);
} else {
render_bar(dc, x_bar, y, v, bars, out width);
}
x_bar += width;
if (show_numbers)
numbers_text.Add(v.ToString());
}
if (settings.total.show_numbers) {
numbers_text.Add(vap.Volume.ToString());
}
if (settings.show_ltq && last_trade_here) {
numbers_text.Clear();
numbers_text.Add($"{direction_arrow} {last_trade.size} ");
}
if (numbers_text.Count > 0) {
string str = string.Join("/", numbers_text);
GlyphRun gr = glyph_context.get_glyph_run(str, settings.general.font_size, settings.general.width, row_height, HorizontalAlignment.Center, 0, VerticalAlignment.Center, y, true);
@@ -243,7 +275,9 @@ namespace mbts.spark {
}
double pct = v / max;
width = pct * settings.general.width;
dc.DrawRectangle(brush, null, new Rect(x, y, width, row_height - 1));
// 4 seems like a good number to move the y pos down by and similarly
// to shorten the total y coponent by (-4 for the shift of +4, and another -4 to give it some space);
dc.DrawRectangle(brush, null, new Rect(x, y + 4, width, row_height - 2 * 4));
}
}
@@ -278,22 +312,22 @@ namespace mbts.spark {
}
// this is basically a copy of redner_sizes up above
internal static void render_piqs(DrawingVisual dv, GlyphContext glyph_context, int origin_index, double[] prices, (double price, uint piq)[] piqs, double row_height, ContractSpecificationImmutable spec, LadderColumnSettings settings) {
internal static void render_piqs(DrawingVisual dv, GlyphContext glyph_context, int origin_index, double[] prices, IEnumerable<(double price, uint piq)> pvp, double row_height, ContractSpecificationImmutable spec, LadderColumnSettings settings) {
using (var dc = dv.RenderOpen()) {
using (_draw_with_clip(dc, settings, row_height, prices.Length)) {
if (!prices.within_range(origin_index) || piqs.is_null_or_empty())
if (!prices.within_range(origin_index) || pvp.is_null_or_empty())
return;
var foreground = settings.foreground.rgb_brush();
int curr_index = origin_index;
double curr_price = prices[curr_index];
for (int i = 0; i < piqs.Length; i++) {
double price = piqs[i].price;
foreach (var entry in pvp) {
double price = entry.price;
// prices are in descending order, so get diff from curr_price - price
int tick_diff = spec.tickdiff_unchecked(curr_price, price);
curr_index += tick_diff;
curr_price = price;
if (curr_index >= 0 && curr_index < prices.Length) {
var gr = format_text(piqs[i].piq.ToString(), curr_index, row_height, settings, glyph_context);
var gr = format_text(entry.piq.ToString(), curr_index, row_height, settings, glyph_context);
dc.DrawGlyphRun(foreground, gr);
} else break;
}
@@ -317,6 +351,12 @@ namespace mbts.spark {
dc.DrawRectangle(new SolidColorBrush(color.rgb_color()) { Opacity = 0.2 }, null, new Rect(0, high_index * row_height, settings.width, (low_index - high_index) * row_height));
}
static GlyphRun[] _format_empty_prices(int row_count, double row_height, LadderColumnSettings settings, GlyphContext glyph_context) {
var result = new GlyphRun[row_count];
Parallel.For(0, row_count, i => result[i] = format_text("-", i, row_height, settings, glyph_context));
return result;
}
static IDisposable _draw_with_clip(DrawingContext dc, LadderColumnSettings settings, double row_height, int row_count) {
dc.PushClip(new RectangleGeometry(new Rect(0, 0, settings.width, row_height * row_count)));
return Disposable.Create(dc.Pop);
@@ -13,11 +13,12 @@
TextElement.FontSize="{x:Static u:FontSizes.Small}"
>
<UserControl.Resources>
<cv:BooleanToVisibilityConverter x:Key="NetChangeGroupBoxIsVisibleVisibilityConverter" VisibilityForTrue="Hidden" VisibilityForFalse="Visible"/>
<sys:Double x:Key="HorizontalPush">4</sys:Double>
<sys:Double x:Key="HorizontalSplit">8</sys:Double>
<sys:Double x:Key="IndentChild">24</sys:Double>
<sys:Double x:Key="GroupBoxOffsetLeft">8</sys:Double>
<sys:Double x:Key="GroupBoxOffsetRight">24</sys:Double>
<sys:Double x:Key="GroupBoxOffsetRight">2</sys:Double>
<sys:Double x:Key="VerticalPush">4</sys:Double>
<sys:Double x:Key="VerticalSectionBreak">6</sys:Double>
<sys:Double x:Key="VerticalSplit">8</sys:Double>
@@ -132,7 +133,7 @@
<ComboBox Grid.Column="3" ItemsSource="{u:EnumBindingSource {u:Nested sp:LadderSettings.BadgeStyle}}" SelectedItem="{Binding badge_style}"/>
<TextBlock Grid.Row="2" Grid.Column="1" VerticalAlignment="Center">Alternate Order Qty</TextBlock>
<TextBox Grid.Row="2" Grid.Column="3" Text="{Binding alternate_order_size}" u:TextBoxBehavior.InputFilter="{u:RegexInputFilter Regex={x:Static Member=Integer, MemberType={u:Nested txt:TextUtils.RegexFilters}}}"/>
<TextBox Grid.Row="2" Grid.Column="3" Text="{Binding alternate_order_size}" u:TextBoxBehavior.InputFilter="{u:RegexInputFilter Regex={x:Static Member=UnsignedInteger, MemberType={u:Nested txt:TextUtils.RegexFilters}}}"/>
<TabControl Grid.Row="4" Grid.ColumnSpan="5" Width="543" Height="275">
<TabControl.ItemContainerStyle>
@@ -141,8 +142,8 @@
<Setter Property="VerticalContentAlignment" Value="Top"/>
</Style>
</TabControl.ItemContainerStyle>
<!-- Fonts and Colors Tab -->
<TabItem Header="Fonts and Colors" VerticalAlignment="Stretch">
<!-- Column Settings Tab -->
<TabItem Header="Column Settings" VerticalAlignment="Stretch">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="27"/>
@@ -172,7 +173,7 @@
<ColumnDefinition SharedSizeGroup="c1"/>
<ColumnDefinition SharedSizeGroup="c2"/>
<ColumnDefinition Width="160" SharedSizeGroup="c3"/>
<ColumnDefinition Width="140" SharedSizeGroup="c4"/>
<ColumnDefinition Width="153" SharedSizeGroup="c4"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="{u:BTSColor header}" Padding="6,3">
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ColumnNameTemplateSelector}"/>
@@ -382,7 +383,8 @@
FontStyle="Italic"
Foreground="{Binding can_choose_alternate_implied_format, Converter={StaticResource IsEnabledForegroundConverter}}"
/>
<GroupBox Grid.Row="3" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,10,0,0" Grid.ColumnSpan="3">
<GroupBox Grid.Row="3" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,10,0,0" Grid.ColumnSpan="3"
Visibility="{Binding DataContext.is_synthetic, RelativeSource={RelativeSource AncestorType=usp:LadderSettingsView}, Converter={StaticResource NetChangeGroupBoxIsVisibleVisibilityConverter}}">
<GroupBox.Header>
<CheckBox
x:Name="show_name_change_chk"
@@ -478,6 +480,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="{Binding Source={StaticResource VerticalPush}}"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="volume_headers" TextElement.Foreground="{u:BTSColor header_text}" Background="{u:BTSColor header}" Margin="8,0">
<Grid.ColumnDefinitions>
@@ -586,6 +589,22 @@
</TextBlock.Style>
</TextBlock>
</WrapPanel>
<StackPanel Grid.Row="4" Margin="8,0" VerticalAlignment="Top">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border Margin="0,5,5,0">
<CheckBox Content="Show Last on Volume Column" FlowDirection="RightToLeft" DataContext="{Binding volume}" IsChecked="{Binding show_last_trade}">
<CheckBox.Resources>
<Style TargetType="{x:Type Path}">
<Setter Property="FlowDirection" Value="LeftToRight" />
</Style>
</CheckBox.Resources>
</CheckBox>
</Border>
</Grid>
</StackPanel>
</Grid>
</TabItem>
<!-- Size Presets Tab-->
@@ -1,9 +1,9 @@
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.extensions;
using mbts.ui.utility.text;
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
@@ -30,7 +30,7 @@ namespace mbts.spark {
readonly LadderGridHost _host;
readonly IList<IDisposable> _hooks;
bool _is_mouse_over, _is_mouse_down, _is_mouse_dragging;
bool _is_mouse_over, _is_mouse_down, _is_mouse_dragging, _can_send_orders;
int _curr_row;
Column _curr_column = Column.Empty;
@@ -41,6 +41,10 @@ namespace mbts.spark {
_bid_mouse_down_pen = new Pen(new SolidColorBrush(Color.FromRgb(102, 178, 255)), 1).frozen();
_ask_mouse_down_pen = new Pen(new SolidColorBrush(Color.FromRgb(255, 178, 102)), 1).frozen();
_hooks = new List<IDisposable> {
_host._subscribe_can_send_orders().Subscribe(v => {
_can_send_orders = v;
_host.Cursor = _evaluate_position();
}),
_host.SubscribeMouseEnter(_host_MouseEnter),
_host.SubscribeMouseMove(_host_MouseMove),
_host.SubscribeMouseLeave(_host_MouseLeave),
@@ -88,7 +92,7 @@ namespace mbts.spark {
_draw_cell(drawingContext, _navy_pen, x, y, _curr_column.settings.width);
}
}
else if (_is_mouse_over && (_curr_column.column_type == ColumnType.Bid || _curr_column.column_type == ColumnType.Ask)) {
else if (_is_mouse_over && _can_send_orders && (_curr_column.column_type == ColumnType.Bid || _curr_column.column_type == ColumnType.Ask)) {
double y = _curr_row * _host._settings.row_height;
double x = _curr_column.x_left;
Pen style = default;
@@ -112,12 +116,17 @@ namespace mbts.spark {
}
}
Cursor _evaluate_position() {
var pos = Mouse.GetPosition(_host);
return _evaluate_position(_host._column_for_point(pos), pos, _host.columns);
}
Cursor _evaluate_position(Column column, Point point, Column[] columns) {
if (columns.Length == 0)
return Cursors.Arrow;
switch (column.column_type) {
case ColumnType.Bid:
case ColumnType.Ask: return Cursors.Hand;
case ColumnType.Ask: return _can_send_orders ? Cursors.Hand : Cursors.No;
default: return Cursors.Arrow;
}
}
@@ -140,11 +149,33 @@ namespace mbts.spark {
}
void _host_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
// When middle click over bid or ask columns:
// Non-parameterizalbe order: change cursor to No indicating sending an order is not possible
// Parameterizable order: if qty is null/0, flash and No cursor
var pos = e.GetPosition(_host);
var column_type = _host._column_for_point(pos).column_type;
var selected_algo_can_expand = _host._vm.order_properties_vm?.selected_algo_definition?.algo_root?.can_expand ?? true;
if (e.MiddleButton == MouseButtonState.Pressed &&
(column_type == LadderGridHost.ColumnType.Bid || column_type == LadderGridHost.ColumnType.Ask)) {
if (!selected_algo_can_expand) {
_host.Cursor = Cursors.No;
}
if (selected_algo_can_expand && (_host._vm.size_calculator.value ?? 0) == 0) {
_host._vm.size_calculator.flash_error();
_host.Cursor = Cursors.No;
}
}
_is_mouse_down = true;
InvalidateVisual();
}
void _host_MouseUp(object sender, MouseButtonEventArgs e) {
// This will reset the cursor appropriately if the middle mouse button
// has been pressed while over the bid or ask columns
var point = e.GetPosition(_host);
_set_current_row_and_column(point);
_is_mouse_down = false;
_is_mouse_dragging = false;
InvalidateVisual();
@@ -19,6 +19,7 @@
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Controls/Order/OrderDetailsPopupControl.xaml"/>
<ResourceDictionary Source="/view/Controls/Order/AlgoParamsView.xaml"/>
<ResourceDictionary Source="/view/Controls/Order/AlgoTemplateFavoriteButtonsView.xaml"/>
</ResourceDictionary.MergedDictionaries>
<converters:BooleanToVisibilityConverter x:Key="BoolToInVisibility" VisibilityForTrue="Collapsed" VisibilityForFalse="Visible" />
@@ -31,7 +32,6 @@
</converters:BooleanToObjectConverter.ValueForFalse>
</converters:BooleanToObjectConverter>
<converters:EnumCharacterCasingConverter x:Key="casing_converter"/>
<converters:DoubleTextConverter x:Key="PositionTextConverter" Format="0" NaNText="-"/>
<converters:GridLengthConverter x:Key="GridLengthPixelConverter" UnitType="Pixel" />
<converters:ResponsiveTextConverter x:Key="DayTextConverter" LongText="Day:" ShortText="D:" Threshold="99" />
<converters:ResponsiveTextConverter x:Key="HighTextConverter" LongText="High:" ShortText="H:" Threshold="99" />
@@ -455,18 +455,38 @@
<Button
Margin="2"
Command="{Binding position_click_command}"
Content="{Binding position, Converter={StaticResource PositionTextConverter}}"
Content="{Binding position_display_text}"
FontWeight="Bold"
Foreground="{Binding position, Converter={StaticResource NumericSignBrushConverter}}"
Visibility="{Binding show_account_position, Converter={StaticResource BooleanToVisibilityConverter}}"
>
<Button.Visibility>
<MultiBinding>
<MultiBinding.Converter>
<converters:MultiValueConverterPipe>
<converters:BooleanOrConverter/>
<converters:BooleanToVisibilityConverter/>
</converters:MultiValueConverterPipe>
</MultiBinding.Converter>
<Binding Path="show_day_position"/>
<Binding Path="show_account_position"/>
</MultiBinding>
</Button.Visibility>
<Button.ToolTip>
<TextBlock Text="{Binding order_properties_vm.selected_account, StringFormat='{}{0} total position (click to copy to size)'}"/>
<!--<TextBlock Text="{Binding order_properties_vm.selected_account, StringFormat='{}{0} total position (click to copy to size)'}"/>-->
<TextBlock TextAlignment="Center">
<TextBlock.Text>
<MultiBinding Converter="{x:Static spark:LadderView.PositionToolTipConverter}">
<Binding Path="order_properties_vm.selected_account"/>
<Binding Path="show_day_position"/>
<Binding Path="show_account_position"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Button.ToolTip>
</Button>
<!-- Qty textbox -->
<TextBox
mbts:TextBoxBehavior.InputFilter="{mbts:RegexInputFilter Regex={x:Static Member=Integer, MemberType={mbts:Nested txt:TextUtils.RegexFilters}}}"
mbts:TextBoxBehavior.InputFilter="{mbts:RegexInputFilter Regex={x:Static Member=UnsignedInteger, MemberType={mbts:Nested txt:TextUtils.RegexFilters}}}"
mbts:TextBoxBehavior.IncrementDecrementOnMouseClick="True"
HorizontalContentAlignment="Right"
Margin="2"
@@ -489,6 +509,15 @@
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Clear button -->
<Button Margin="2" HorizontalAlignment="Stretch" Command="{Binding size_calculator.clear}"
Visibility="{Binding show_clear, Converter={StaticResource BooleanToVisibilityConverter}}">Clear</Button>
<!-- Auto Clear button - reset to defaults after order -->
<CheckBox Margin="2" IsChecked="{Binding auto_clear}"
Visibility="{Binding show_auto_clear, Converter={StaticResource BooleanToVisibilityConverter}}">
Auto Clear
</CheckBox>
<!--Algo Favorite Buttons-->
<uag:AlgoTemplateFavoriteButtonsView
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
@@ -501,14 +530,6 @@
</ItemsPanelTemplate>
</uag:AlgoTemplateFavoriteButtonsView.ItemsPanel>
</uag:AlgoTemplateFavoriteButtonsView>
<!-- Clear button -->
<Button Margin="2" HorizontalAlignment="Stretch" Command="{Binding size_calculator.clear}"
Visibility="{Binding show_clear, Converter={StaticResource BooleanToVisibilityConverter}}">Clear</Button>
<!-- Auto Clear button - reset to defaults after order -->
<CheckBox Margin="2" IsChecked="{Binding auto_clear}"
Visibility="{Binding show_auto_clear, Converter={StaticResource BooleanToVisibilityConverter}}">
Auto Clear
</CheckBox>
<StackPanel Margin="0,17,0,0" VerticalAlignment="Top">
<!-- Cancel Buttons -->
<Button Command="{Binding cancel_command}" CommandParameter="Bids" Style="{StaticResource CancelBuys}"
@@ -553,11 +574,10 @@
<Setter Property="Foreground" Value="{Binding foreground}"/>
<Setter Property="TextLines" Value="{Binding text_lines}" />
<Setter Property="Width" Value="{x:Static controls:Badge.DefaultWidth}"/>
<Setter Property="IsExchangeOrder" Value="{Binding is_exchange_order}" />
<Setter Property="HasMultipleOrdersAtLevel" Value="{Binding has_multiple_orders_at_same_level}" />
<Setter Property="IsHedgeOrder" Value="{Binding is_hedge_order}" />
<Setter Property="ParentAlgoType" Value="{Binding parent_algo_type}" />
<Setter Property="ParentAlgoTypeBrush" Value="{Binding parent_algo_type_brush}" />
<Setter Property="AlgoType" Value="{Binding algo_type}" />
<Setter Property="Abbreviation" Value="{Binding algo_studio_abbreviation}" />
<Setter Property="Cursor" Value="Hand"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
@@ -1,21 +1,52 @@
using mbts.utils;
using System.Collections.Generic;
using bts;
using bts.collections;
using bts.utils;
using mbts.ui.converters;
using mbts.utils;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
namespace mbts.spark {
partial class LadderView : UserControl {
public static IMultiValueConverter PositionToolTipConverter =
ValueConverter.create<AccountTuple?, bool, bool, string>((account, show_day_pos, show_total_pos) => {
const string day_pos_text = "day", total_pos_text = "total";
if (!account.HasValue)
return string.Empty;
var sb = new StringBuilderEx(account.Value.name);
string primary_pos;
if (show_day_pos) {
primary_pos = day_pos_text;
if (show_total_pos) {
sb.Add(" '", primary_pos, " ");
sb.Add("(", total_pos_text, ")' ");
} else {
sb.Add(" ", primary_pos, " ");
}
} else if (show_total_pos) {
primary_pos = total_pos_text;
sb.Add(" ", primary_pos, " ");
} else {
// unexpected code path (the button for which this tooltip is destined should be invisible in this case), but no need to throw an exception.
return string.Empty;
}
sb.Add("position\n(click to copy ");
if (show_day_pos && show_total_pos)
sb.Add(primary_pos, " position ");
sb.Add("to size)");
return sb.ToString();
});
readonly double _max_zoom, _min_zoom;
internal LadderView(LadderVM vm) {
InitializeComponent();
_column_headers_presenter.Columns = new GridViewColumnCollection();
_ladder.visible_columns_changed += () => _column_headers_presenter.Columns.SetItems(
_ladder.visible_columns.Select(t => ColumnFactory.create(_ladder, t)));
_ladder.visible_column_types.Select(t => ColumnFactory.create(_ladder, t)));
this.vm = vm;
DataContext = vm;
DataContextChanged += datacontext_changed;
@@ -0,0 +1,14 @@
using mbts.ui;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace mbts.ui.spark {
static class LadderViewHelper {
public static readonly DragDeltaEventHandler ColumnHeaderThumb_DragDeltaHandler =
(s, e) =>
e.Handled =
s is Thumb thumb &&
thumb.try_find_ancestor_of_type(out Canvas canvas) &&
canvas.ActualWidth + e.HorizontalChange < canvas.MinWidth;
}
}
@@ -0,0 +1,117 @@
<UserControl
x:Class="mbts.ui.spark.MiniLadderView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sp="clr-namespace:mbts.spark"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:usp="clr-namespace:mbts.ui.spark"
Background="{x:Static u:BTSColors.common_background_brush}"
TextElement.FontSize="{x:Static u:FontSizes.Small}"
>
<UserControl.Resources>
<ResourceDictionary>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Background" Value="{u:BTSColor header}"/>
<Setter Property="Foreground" Value="{u:BTSColor header_text}"/>
<Setter Property="BorderBrush" Value="{u:BTSColor header_border}"/>
<Setter Property="BorderThickness" Value="0,0,1,0"/>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Height" Value="23" />
<Setter Property="MinWidth" Value="30" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<Grid SnapsToDevicePixels="True">
<Border x:Name="HeaderBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="7"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Padding="{TemplateBinding Padding}" Grid.RowSpan="2">
<ContentPresenter x:Name="HeaderContent"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="0,0,0,1"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
</Border>
<Canvas MinWidth="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MinWidth, Mode=OneTime}">
<Thumb x:Name="PART_HeaderGripper" DragDelta="{x:Static usp:LadderViewHelper.ColumnHeaderThumb_DragDeltaHandler}">
<Thumb.Style>
<Style TargetType="{x:Type Thumb}">
<Setter Property="Canvas.Right" Value="-9"/>
<Setter Property="Width" Value="18"/>
<Setter Property="Height" Value="{Binding ActualHeight, RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Background="Transparent" Padding="{TemplateBinding Padding}">
<Rectangle Fill="{TemplateBinding Background}" HorizontalAlignment="Center" Width="2"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{u:BTSColor header_text}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Thumb.Style>
</Thumb>
</Canvas>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<AdornerDecorator>
<sp:LadderGridHost
x:Name="ladder_grid_host"
Command="{Binding send_order_command}"
Orders="{Binding orders}"
orders_column_width="{Binding Settings.orders_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
bid_column_width="{Binding Settings.orders_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
price_column_width="{Binding Settings.price_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
ask_column_width="{Binding Settings.ask_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
LastColumnWidth="{Binding Settings.last_column.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
VolumeColumnWidth="{Binding Settings.volume_column.general.width, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
CanUserSendOrders="{Binding CanUserSendOrders, RelativeSource={RelativeSource AncestorType=usp:MiniLadderView}}"
IsTabStop="False"
>
<sp:LadderGridHost.Resources>
<Style TargetType="c:Badge">
<Setter Property="Background" Value="{Binding background}"/>
<Setter Property="BorderBrush" Value="{Binding border}"/>
<Setter Property="FontFamily" Value="{Binding font_family}"/>
<Setter Property="FontSize" Value="{Binding font_size}"/>
<Setter Property="Foreground" Value="{Binding foreground}"/>
<Setter Property="TextLines" Value="{Binding text_lines}"/>
<Setter Property="Width" Value="{x:Static c:Badge.DefaultWidth}"/>
<Setter Property="HasMultipleOrdersAtLevel" Value="{Binding has_multiple_orders_at_same_level}"/>
<Setter Property="IsHedgeOrder" Value="{Binding is_hedge_order}"/>
<Setter Property="AlgoType" Value="{Binding algo_type}"/>
<Setter Property="Abbreviation" Value="{Binding algo_studio_abbreviation}" />
<Setter Property="Cursor" Value="Hand"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding hoverbackground}"/>
</Trigger>
</Style.Triggers>
</Style>
</sp:LadderGridHost.Resources>
</sp:LadderGridHost>
</AdornerDecorator>
</UserControl>
@@ -0,0 +1,29 @@
using System.Windows;
using System.Windows.Controls;
namespace mbts.ui.spark {
partial class MiniLadderView : UserControl {
public MiniLadderView() =>
InitializeComponent();
#region Settings
public static readonly DependencyProperty SettingsProperty =
WPFHelper.create_dp<mbts.spark.LadderSettings, MiniLadderView>(nameof(Settings));
public mbts.spark.LadderSettings Settings {
get => GetValue(SettingsProperty) as mbts.spark.LadderSettings;
set => SetValue(SettingsProperty, value);
}
#endregion
#region CanUserSendOrders
public static readonly DependencyProperty CanUserSendOrdersProperty =
WPFHelper.create_dp<bool, MiniLadderView>(nameof(CanUserSendOrders));
public bool CanUserSendOrders {
get => (bool)GetValue(CanUserSendOrdersProperty);
set => SetValue(CanUserSendOrdersProperty, value);
}
#endregion
}
}
@@ -0,0 +1,149 @@
using bts.api;
using bts.api.position;
using bts.utils;
using mbts;
using mbts.spark;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace bts.shell.manual_fills {
class ManualFillRow : DisposableViewModelBase, IEquatable<ManualFillRow> {
public const int _uncommitted_record_id = -1;
static readonly PropertyInfo[] _snapshot_props =
Array.ConvertAll(new[] {
nameof(security),
nameof(account),
nameof(user),
nameof(side),
nameof(size),
nameof(price)
}, typeof(ManualFillRow).GetProperty);
readonly Property<bool> _is_dirty;
readonly Property<EditableItemState> _editable_item_state;
readonly DelegateProperty<bool> _is_valid;
SecurityImmutable? _security;
AccountTuple? _account;
UserTuple? _user;
Side _side;
uint _size;
double _price;
bool _is_selected;
ManualFillData? _data;
IDisposable _committed_snapshot;
public ManualFillRow(ManualFillData data) {
_data = data;
key = data.key;
_security = data.value.security;
_account = data.value.account;
_user = data.value.user;
_side = data.value.manual_fill.Side;
_size = data.value.manual_fill.Quantity;
_price = data.value.manual_fill.Price;
if (placeholder_key.HasValue) {
_is_dirty = backing(nameof(is_dirty), false);
_editable_item_state = backing(nameof(editable_item_state), EditableItemState.Uncommitted);
_committed_snapshot = ObjectSnapshot.create(this, _snapshot_props);
} else {
_is_dirty = backing(nameof(is_dirty), ObjectSnapshot.dirty_monitor(this, out _committed_snapshot, _snapshot_props));
_editable_item_state = backing(nameof(editable_item_state), _is_dirty, ch => ch.newval ? EditableItemState.Modified : EditableItemState.None);
}
_is_valid = backing(nameof(is_valid), () => _validate(this));
}
public event Action<bool> is_selected_changed;
// key/id
public ManualFillKey key { get; }
public long? placeholder_key => key.unpersisted;
public int record_id => key.persisted ?? _uncommitted_record_id;
public string record_id_display_text => key.persisted?.ToString();
// security/spec
public SecurityImmutable? security {
get => _security;
set {
if (_set(ref _security, value))
RaisePropertyChanged(nameof(spec));
}
}
public ContractSpecificationImmutable? spec => security.spec_or_default();
// account/user
public AccountTuple? account { get => _account; set => _set(ref _account, value); }
public UserTuple? user { get => _user; set => _set(ref _user, value); }
// side/size/price
public Side side { get => _side; set => _set(ref _side, value); }
public uint size { get => _size; set => _set(ref _size, value); }
public double price { get => _price; set => _set(ref _price, value); }
// is selected
public bool is_selected {
get => _is_selected;
set {
if (_set(ref _is_selected, value))
is_selected_changed?.Invoke(value);
}
}
// dirty/valid/editable-state
public bool is_dirty => _is_dirty.Value;
public bool is_valid => _is_valid.Value;
public EditableItemState editable_item_state => _editable_item_state.Value;
public ManualFillData data => _data ??
(_data = new ManualFillData(key, new ManualFillTuple(new ManualFillImmutable(to_man_fill()), security, account, user))).Value;
public void revert() =>
_committed_snapshot.Dispose();
public IObservable<bool> subscribe_is_dirty() =>
_is_dirty.observe_current(true);
public IObservable<bool> subscribe_is_selected() =>
Observable.Create<bool>(o => {
o.OnNext(is_selected);
is_selected_changed += o.OnNext;
return Disposable.Create(() => is_selected_changed -= o.OnNext);
});
public ManualFill to_man_fill() =>
new ManualFill {
RecordId = record_id,
AccountId = account?.id ?? 0,
AccountName = account?.name ?? string.Empty,
UserId = user?.id ?? 0,
Price = price,
Side = side,
Quantity = size,
SecurityKey = security?.Id ?? 0
};
public ManualFillTuple to_tuple() =>
new ManualFillTuple(new ManualFillImmutable(to_man_fill()), security, account, user);
public bool Equals(ManualFillRow other) =>
data == other?.data;
public override bool Equals(object obj) => Equals(obj as ManualFillRow);
public override int GetHashCode() => record_id;
static bool _validate(ManualFillRow row) =>
row.security.HasValue &&
row.account.HasValue &&
row.user.HasValue &&
row.side != Side.Undefined &&
!double.IsNaN(row._price);
bool _set<T>(ref T backing, T value, [CallerMemberName] string prop_name = "") =>
_set(ref backing, value, EqualityComparer<T>.Default, prop_name);
bool _set<T>(ref T backing, T value, IEqualityComparer<T> comparer, [CallerMemberName] string prop_name = "") {
if (Set(ref backing, value, comparer, prop_name)) {
_is_valid.refresh();
_data = null;
return true;
}
return false;
}
}
}
@@ -0,0 +1,84 @@
<UserControl
x:Class="mbts.ui.spark.ManualFillRowEditor"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:usp="clr-namespace:mbts.ui.spark"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
FocusVisualStyle="{x:Null}"
>
<UserControl.Resources>
<sys:Double x:Key="HorizontalPush">4</sys:Double>
<sys:Double x:Key="VerticalPush3">8</sys:Double>
<Style x:Key="HeaderText" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{x:Static u:BTSColors.header_text_brush}"/>
<Setter Property="Margin" Value="4"/>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush}}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush}}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush}}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush}}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush}}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush}}"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.ColumnSpan="11" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="{x:Static u:BTSColors.header_brush}"/>
<TextBlock Grid.Column="0" Text="Security" Style="{StaticResource HeaderText}"/>
<TextBlock Grid.Column="2" Text="Account" Style="{StaticResource HeaderText}"/>
<TextBlock Grid.Column="4" Text="User" Style="{StaticResource HeaderText}"/>
<TextBlock Grid.Column="6" Text="Side" Style="{StaticResource HeaderText}"/>
<TextBlock Grid.Column="8" Text="Qty" Style="{StaticResource HeaderText}"/>
<TextBlock Grid.Column="10" Text="Price" Style="{StaticResource HeaderText}"/>
<c:ContractPickerPopup
Grid.Row="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding security}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{DynamicResource ResourceKey={x:Static usp:ManualFillRowEditor.SecuritiesKey}}"
SelectedSecurity="{Binding security}"
ShowDialogSkirt="True"
/>
<ComboBox Grid.Row="2" Grid.Column="2" ItemsSource="{DynamicResource ResourceKey={x:Static usp:ManualFillRowEditor.AccountsKey}}" SelectedItem="{Binding account}"/>
<ComboBox Grid.Row="2" Grid.Column="4" ItemsSource="{DynamicResource ResourceKey={x:Static usp:ManualFillRowEditor.UsersKey}}" SelectedItem="{Binding user}"/>
<ComboBox Grid.Row="2" Grid.Column="6" ItemsSource="{DynamicResource ResourceKey={x:Static usp:ManualFillRowEditor.SidesKey}}" SelectedItem="{Binding side}" Width="48"/>
<c:TickUpDownUInt Grid.Row="2" Grid.Column="8" MinWidth="40" Value="{Binding size}"/>
<usp:PriceEntryControl Grid.Row="2" Grid.Column="10" MinWidth="55" Value="{Binding price}" ContractSpec="{Binding spec}"/>
</Grid>
<c:DialogSkirt Grid.Row="1" Margin="0,6,0,0">
<c:DialogSkirt.RightItems>
<x:Array Type="{x:Type Button}">
<Button Content="OK" Command="{x:Static usp:ManualFillRowEditor.CommitCommand}" CommandParameter="{Binding}" IsDefault="True"/>
<Button Content="Cancel" Command="{x:Static usp:ManualFillRowEditor.CancelCommand}" IsCancel="True"/>
</x:Array>
</c:DialogSkirt.RightItems>
</c:DialogSkirt>
</Grid>
</UserControl>
@@ -0,0 +1,22 @@
using System.Windows.Controls;
using System.Windows.Input;
namespace mbts.ui.spark {
/// <summary>
/// Interaction logic for ManualFillRowEditor.xaml
/// </summary>
partial class ManualFillRowEditor : UserControl {
public const string
SecuritiesKey = nameof(SecuritiesKey),
AccountsKey = nameof(AccountsKey),
UsersKey = nameof(UsersKey),
SidesKey = nameof(SidesKey);
public static readonly RoutedCommand
CommitCommand = new RoutedCommand(),
CancelCommand = new RoutedCommand();
public ManualFillRowEditor() =>
InitializeComponent();
}
}
@@ -0,0 +1,39 @@
<Window
x:Class="mbts.ui.spark.ManualFillsImportConflictsDialog"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:usp="clr-namespace:mbts.ui.spark"
u:WindowStyle.AllowResize="True"
u:WindowStyle.SizeToContent="WidthAndHeight"
u:WindowStyle.TitleContent="Import Fills Conflicts"
Style="{StaticResource BTSDialogTitleTemplate}"
>
<u:WindowStyle.TitleContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Image Height="12" Margin="5,2" Source="{x:Static u:Icons.Import}"/>
<TextBlock Margin="4,0,0,0" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</u:WindowStyle.TitleContentTemplate>
<Window.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<usp:ManualFillsImportConflictsView Margin="12,10,12,8"/>
<c:DialogSkirt Grid.Row="1">
<c:DialogSkirt.RightItems>
<x:Array Type="{x:Type Button}">
<Button Command="{Binding ok_cmd}">Continue</Button>
<Button Command="{Binding cancel_cmd}" IsCancel="True">Cancel</Button>
</x:Array>
</c:DialogSkirt.RightItems>
</c:DialogSkirt>
</Grid>
</Window.Content>
</Window>
@@ -0,0 +1,11 @@
using System.Windows;
namespace mbts.ui.spark {
/// <summary>
/// Interaction logic for ManualFillsImportConflictsDialog.xaml
/// </summary>
partial class ManualFillsImportConflictsDialog : Window {
public ManualFillsImportConflictsDialog() =>
InitializeComponent();
}
}
@@ -0,0 +1,106 @@
using bts.api;
using bts.collections;
using mbts;
using mbts.collections;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Data;
using System.Windows.Input;
using Component = mbts.ui.spark.ManualFillsImportConflictsView.ManualFillComponent;
namespace bts.shell.manual_fills {
class ManualFillsImportVM : DisposableViewModelBase, IRequestViewClose {
readonly RefCountMap<ParsedManualFill> _conflicts = new RefCountMap<ParsedManualFill>();
IReadOnlyDictionary<uint, SecurityMD> _security_map = DictionaryHelper.empty<uint, SecurityMD>();
public ManualFillsImportVM(int num_fills) {
rows_view = CollectionViewSource.GetDefaultView(rows);
ok_cmd = new DelegateCommand(() => import_request?.Invoke());
cancel_cmd = new DelegateCommand(() => RequestClose?.Invoke(this, new EventArgs()));
this.num_fills = num_fills;
_dispose = ObservableAutoWrapper.connect_item_hooks(rows, r =>
r.subscribe_has_error().Subscribe(has_error => {
if (has_error) {
if (r.affected_fills.Count(_conflicts.increment_adds_key) == 0)
return;
} else if (r.affected_fills.Count(_conflicts.decrement_removes_key) == 0)
return;
RaisePropertyChanged(nameof(num_successfully_parsed_fills));
}));
}
public event Action import_request;
public event EventHandler RequestClose;
public ObservableCollection<Row> rows { get; } = new ObservableCollection<Row>();
public ICollectionView rows_view { get; }
public int num_fills { get; }
public int num_successfully_parsed_fills => num_fills - _conflicts.Count;
// misc items sources
public IEnumerable<Side> sides => new[] { Side.Buy, Side.Sell };
public ObservableCollection<AccountTuple?> accounts { get; } = new ObservableCollection<AccountTuple?>();
public ObservableCollection<UserTuple?> users { get; } = new ObservableCollection<UserTuple?>();
public IReadOnlyDictionary<uint, SecurityMD> security_map {
get => _security_map;
set {
if (!ReferenceEquals(_security_map, value)) {
_security_map = value;
_resolve_securities();
RaisePropertyChanged();
}
}
}
// commands
public ICommand ok_cmd { get; }
public ICommand cancel_cmd { get; }
void _resolve_securities() {
if (_security_map == null)
return;
var reverse_lookup = ManualFillsParser.reverse_lookup(MappedDictionary.create(_security_map, v => v.security.ToString()));
foreach (var r in rows) {
if (r.component == Component.Security && r.has_error) {
var id = reverse_lookup(r.text);
if (id != 0 && _security_map.TryGetValue(id, out var smd))
r.value = smd.security;
}
}
}
public class Row : ViewModelBase, ITemplateSelectorKey {
readonly DelegateProperty<bool> _has_error;
object _value;
string _text;
public Row(Component component, string text, IReadOnlyCollection<ParsedManualFill> affected_fills, object error_value, object value) {
this.component = component;
this.text = text;
this.affected_fills = affected_fills;
_value = value;
_has_error = backing(nameof(has_error), () => Equals(this.value, error_value));
}
public Component component { get; }
public string text { get => _text; set => Set(ref _text, value); }
public IReadOnlyCollection<ParsedManualFill> affected_fills { get; }
public object value {
get => _value;
set {
if (Set(ref _value, value))
_has_error.refresh();
}
}
public bool has_error => _has_error.Value;
string ITemplateSelectorKey.key => component.ToString();
public IObservable<bool> subscribe_has_error() => _has_error.observe_current(true);
}
}
}
@@ -0,0 +1,144 @@
<UserControl
x:Class="mbts.ui.spark.ManualFillsImportConflictsView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:usp="clr-namespace:mbts.ui.spark"
MinWidth="400"
>
<UserControl.Resources>
<Style x:Key="VerticallyCenteredText" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!--Value Cell Template Selector-->
<u:KeyedTemplateSelector x:Key="ValueCellTemplateSelector">
<u:KeyedTemplateSelector.Default>
<DataTemplate>
<TextBlock Text="{Binding value}" VerticalAlignment="Center"/>
</DataTemplate>
</u:KeyedTemplateSelector.Default>
<u:TemplateSelectorKeyValue Key="{x:Static Price, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
<DataTemplate>
<TextBlock Text="{Binding value, Converter={StaticResource DoubleTextConverter}}" VerticalAlignment="Center"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</u:KeyedTemplateSelector>
<!--Value Cell Editing Template Selector-->
<u:KeyedTemplateSelector x:Key="ValueCellEditingTemplateSelector">
<u:TemplateSelectorKeyValue Key="{x:Static Security, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
<DataTemplate>
<c:ContractPickerPopup
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding value}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=DataGrid}, Mode=OneWay}"
SelectedSecurity="{Binding value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ShowDialogSkirt="True"
/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="{x:Static Account, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.accounts, RelativeSource={RelativeSource AncestorType=DataGrid}}" SelectedItem="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="{x:Static User, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.users, RelativeSource={RelativeSource AncestorType=DataGrid}}" SelectedItem="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="{x:Static Side, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.sides, RelativeSource={RelativeSource AncestorType=DataGrid}}" SelectedItem="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="{x:Static Size, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
<DataTemplate>
<c:TickUpDownUInt Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
<u:TemplateSelectorKeyValue Key="{x:Static Price, MemberType={u:Nested usp:ManualFillsImportConflictsView.ManualFillComponentNames}}">
<DataTemplate>
<usp:PriceEntryControl Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</u:TemplateSelectorKeyValue>
</u:KeyedTemplateSelector>
</UserControl.Resources>
<!--Content-->
<Grid x:Name="content_root">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image VerticalAlignment="Center" Margin="0,0,8,0">
<Image.Source>
<Binding Source="{x:Static MessageBoxImage.Exclamation}">
<Binding.Converter>
<cv:SystemIconConverter/>
</Binding.Converter>
</Binding>
</Image.Source>
</Image>
<Canvas x:Name="text_canvas" Grid.Column="1" HorizontalAlignment="Stretch"/>
<TextBlock Grid.Column="1" TextWrapping="Wrap" Width="{Binding ActualWidth, ElementName=text_canvas}">
<Run Text="{Binding num_successfully_parsed_fills, Mode=OneWay}"/>
<Run Text="/"/>
<Run Text="{Binding num_fills, Mode=OneWay}"/>
<Run Text=" fills have been successfully converted."/>
<Run Text="Resolve conflicts below by selecting a value to assign each affected fill,"/>
<Run Text="or modify individual fills in the main window."/>
<LineBreak/>
<LineBreak/>
<Run Text="Click 'Continue' to proceed to the main window."/>
</TextBlock>
<DataGrid
x:Name="data_grid"
u:DataGridBehavior.SingleClickEdit="True"
Grid.Row="1"
Grid.ColumnSpan="2"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserSortColumns="True"
HeadersVisibility="All"
ItemsSource="{Binding rows_view}"
Margin="0,8,0,0"
MinRowHeight="17"
RowHeaderWidth="22"
>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource SelectedRowBackground}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Component" Binding="{Binding component}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTextColumn Header="Text" Binding="{Binding text}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTextColumn Header="Affected Fills" Binding="{Binding affected_fills.Count}" ElementStyle="{StaticResource VerticallyCenteredText}" IsReadOnly="True"/>
<DataGridTemplateColumn Header="Value" CellTemplateSelector="{StaticResource ValueCellTemplateSelector}" CellEditingTemplateSelector="{StaticResource ValueCellEditingTemplateSelector}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
@@ -0,0 +1,30 @@
using System.Windows.Controls;
namespace mbts.ui.spark {
/// <summary>
/// Interaction logic for ManualFillsImportConflictsView.xaml
/// </summary>
partial class ManualFillsImportConflictsView : UserControl {
public ManualFillsImportConflictsView() =>
InitializeComponent();
public enum ManualFillComponent {
Security,
Account,
User,
Side,
Size,
Price
}
public static class ManualFillComponentNames {
public static readonly string
Security = ManualFillComponent.Security.ToString(),
Account = ManualFillComponent.Account.ToString(),
User = ManualFillComponent.User.ToString(),
Side = ManualFillComponent.Side.ToString(),
Size = ManualFillComponent.Size.ToString(),
Price = ManualFillComponent.Price.ToString();
}
}
}
@@ -0,0 +1,107 @@
using bts.api.core;
using bts.api.position;
using bts.collections;
using mbts.spark;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
namespace bts.shell.manual_fills {
struct ManualFillKey : IEquatable<ManualFillKey>, IComparable<ManualFillKey> {
public readonly int? persisted;
public readonly long? unpersisted;
public ManualFillKey(int persisted) {
this.persisted = persisted;
unpersisted = null;
}
public ManualFillKey(long unpersisted) {
this.unpersisted = unpersisted;
persisted = null;
}
public int CompareTo(ManualFillKey other) {
if (persisted.HasValue) {
if (other.persisted.HasValue) {
return persisted.Value.CompareTo(other.persisted.Value);
} else if (other.unpersisted.HasValue) {
return -1;
} else return 1;
} else if (unpersisted.HasValue) {
return other.unpersisted.HasValue ? unpersisted.Value.CompareTo(other.unpersisted.Value) : 1;
} else return !other.persisted.HasValue && !other.unpersisted.HasValue ? 0 : -1;
}
public override string ToString() => persisted.HasValue ? persisted.Value.ToString() : unpersisted.HasValue ? unpersisted.Value.ToString() : "(N/A)";
public bool Equals(ManualFillKey other) =>
persisted == other.persisted &&
unpersisted == other.unpersisted;
public override bool Equals(object obj) =>
obj is ManualFillKey other && Equals(other);
public override int GetHashCode() => persisted ?? unpersisted?.GetHashCode() ?? 0;
public static bool operator ==(ManualFillKey a, ManualFillKey b) => a.Equals(b);
public static bool operator !=(ManualFillKey a, ManualFillKey b) => !a.Equals(b);
}
struct ManualFillData : IEquatable<ManualFillData> {
public static readonly Func<ManualFillData, ManualFillKey> key_fn = mfd => mfd.key;
public readonly ManualFillKey key;
public readonly ManualFillTuple value;
public ManualFillData(ManualFillKey key, ManualFillTuple value) {
this.key = key;
this.value = value;
}
public bool Equals(ManualFillData other) =>
key == other.key &&
value.Equals(other.value);
public override bool Equals(object obj) =>
obj is ManualFillData other && Equals(other);
public override int GetHashCode() =>
key.GetHashCode();
public static bool operator ==(ManualFillData a, ManualFillData b) => a.Equals(b);
public static bool operator !=(ManualFillData a, ManualFillData b) => !a.Equals(b);
}
class ManualFillsModel {
readonly OrdersModel _model;
public ManualFillsModel(OrdersModel model)
: this(model, model.on_manual_fill_tuples,
model.factory.subscribe_accounts().Select(d => MappedDictionary.create(d, kv => new AccountTuple(kv.Key, kv.Value))).Table(u => u.id),
model.factory.subscribe_users().Select(d => MappedDictionary.create(d, kv => new UserTuple(kv.Key, kv.Value))).Table(a => a.id)) { }
public ManualFillsModel(OrdersModel model, IObservable<TableUpdate<ManualFillTuple, int>> manual_fills, IObservable<TableUpdate<AccountTuple, uint>> accounts, IObservable<TableUpdate<UserTuple, uint>> users) {
_model = model;
this.manual_fills = manual_fills.SelectKeyValue(
mft => new ManualFillData(new ManualFillKey(mft.manual_fill.RecordId), mft),
ManualFillData.key_fn);
this.accounts = accounts;
this.users = users;
}
public IObservable<TableUpdate<ManualFillData, ManualFillKey>> manual_fills { get; }
public IObservable<TableUpdate<AccountTuple, uint>> accounts { get; }
public IObservable<TableUpdate<UserTuple, uint>> users { get; }
public async Task delete_fills(IEnumerable<ManualFill> fills) =>
await _model.delete_manual_fills(fills);
public async Task<bool> create_fills(IEnumerable<ManualFill> fills) =>
await _model.create_manual_fills(fills);
public async Task<bool> update_fills(IEnumerable<ManualFill> fills) =>
await _model.update_manual_fills(fills);
}
}
@@ -0,0 +1,162 @@
using bts.api;
using bts.api.position;
using bts.collections;
using bts.utils;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Linq;
using ManualFillComponent = mbts.ui.spark.ManualFillsImportConflictsView.ManualFillComponent;
namespace bts.shell.manual_fills {
class ParsedManualFill {
public readonly Guid unpersisted_id = Guid.NewGuid();
public readonly ManualFill result = new ManualFill();
public (string text, bool success) security { get; set; }
public (string text, bool success) account { get; set; }
public (string text, bool success) user { get; set; }
public (string text, bool success) side { get; set; }
public (string text, bool success) quantity { get; set; }
public (string text, bool success) price { get; set; }
public bool Equals(ParsedManualFill other) => other != null && unpersisted_id == other.unpersisted_id;
public override bool Equals(object obj) => Equals(obj as ParsedManualFill);
public override int GetHashCode() => unpersisted_id.GetHashCode();
}
static class ManualFillsParser {
public const string file_extension = "csv";
public static Action<ManualFill, object> assign_component(ManualFillComponent component) {
switch (component) {
case ManualFillComponent.Security: return (mf, val) => mf.SecurityKey = ConvertUtils.try_convert<SecurityImmutable?>(val)?.Id ?? 0;
case ManualFillComponent.Account: return (mf, val) => mf.AccountId = ConvertUtils.try_convert<AccountTuple?>(val)?.id ?? 0;
case ManualFillComponent.User: return (mf, val) => mf.UserId = ConvertUtils.try_convert<UserTuple?>(val)?.id ?? 0;
case ManualFillComponent.Side: return (mf, val) => mf.Side = ConvertUtils.try_convert<Side>(val, Side.Undefined);
case ManualFillComponent.Size: return (mf, val) => mf.Quantity = ConvertUtils.try_convert<uint>(val);
case ManualFillComponent.Price: return (mf, val) => mf.Price = ConvertUtils.try_convert(val, double.NaN);
default: throw new NotImplementedException();
}
}
/// Note: the returned dictionary will have entries for each <see cref="ManualFillComponent"/>, regardless of whether or not that component has any conflicts.
public static Dictionary<ManualFillComponent, Dictionary<string, List<ParsedManualFill>>> get_conflicts(IEnumerable<ParsedManualFill> parsed_fills) {
var missing_components = EnumHelper.get_values<ManualFillComponent>().ToDictionary(c => c, c => new Dictionary<string, List<ParsedManualFill>>());
var mk_list = new Func<string, List<ParsedManualFill>>(_ => new List<ParsedManualFill>());
foreach (var pmf in parsed_fills) {
if (!pmf.security.success) {
add_missing(ManualFillComponent.Security, pmf.security.text, pmf);
}
if (!pmf.account.success) {
add_missing(ManualFillComponent.Account, pmf.account.text, pmf);
}
if (!pmf.user.success) {
add_missing(ManualFillComponent.User, pmf.user.text, pmf);
}
if (!pmf.quantity.success) {
add_missing(ManualFillComponent.Size, pmf.quantity.text, pmf);
}
if (!pmf.side.success) {
add_missing(ManualFillComponent.Side, pmf.side.text, pmf);
}
if (!pmf.price.success) {
add_missing(ManualFillComponent.Price, pmf.price.text, pmf);
}
}
return missing_components;
void add_missing(ManualFillComponent component, string text, ParsedManualFill result) =>
missing_components[component].GetOrAdd(text ?? string.Empty, mk_list).Add(result);
}
// This implementation prioritizes simplicity over speed.
// The security desc lookup could be better optimized, but the speed at which fills are imported is probably not a paramount concern.
public static IEnumerable<ParsedManualFill> parse_rows(IEnumerable<string> rows, IReadOnlyDictionary<uint, SecurityImmutable> security_map, IReadOnlyDictionary<uint, string> account_map, IReadOnlyDictionary<uint, string> user_map) =>
parse_rows(rows,
reverse_lookup(MappedDictionary.create(security_map, kv => kv.Value.ToString())),
reverse_lookup(account_map),
reverse_lookup(user_map));
public static IEnumerable<ParsedManualFill> parse_rows(IEnumerable<string> rows, Func<string, uint> security_id_lookup, Func<string, uint> account_id_lookup, Func<string, uint> user_id_lookup) {
var columns = new[] {
(Headers.Security, Cells.security(security_id_lookup)),
(Headers.Account, Cells.account(account_id_lookup)),
(Headers.User, Cells.user(user_id_lookup)),
(Headers.Side, Cells.side),
(Headers.Quantity, Cells.quantity),
(Headers.Price, Cells.price)
}.ToDictionary(col => col.Item1, col => col.Item2.with_null_check());
return RowParser.parse_rows(rows, columns, Cells.delimiter);
}
public static Func<string, TKey> reverse_lookup<TKey>(IReadOnlyDictionary<TKey, string> map) {
var lookup = map.ToLookup(kv => kv.Value, kv => kv.Key);
return cell_text => lookup.Contains(cell_text) ? lookup[cell_text].First() : default;
}
static class Headers {
public const string
Security = "Security",
Account = "Account",
User = "User",
Side = "Side",
Quantity = "Quantity",
Price = "Price";
}
static class Cells {
public const char delimiter = ',';
public static RowParser.AssignColumnValue<ParsedManualFill> security(Func<string, uint> reverse_id_lookup) {
return impl;
void impl(ref ParsedManualFill pmf, string text) {
pmf.result.SecurityKey = reverse_id_lookup(text);
pmf.security = (text, pmf.result.SecurityKey != 0);
}
}
public static void side(ref ParsedManualFill pmf, string cell_text) {
switch (cell_text[0]) {
case 'B': pmf.result.Side = Side.Buy; break;
case 'S': pmf.result.Side = Side.Sell; break;
default: pmf.side = (cell_text, false); break;
}
pmf.side = (cell_text, true);
}
public static void quantity(ref ParsedManualFill pmf, string cell_text) {
if (uint.TryParse(cell_text, out uint v)) {
pmf.result.Quantity = v;
pmf.quantity = (cell_text, true);
} else {
pmf.quantity = (cell_text, false);
}
}
public static void price(ref ParsedManualFill pmf, string cell_text) {
if (double.TryParse(cell_text, out var v)) {
pmf.result.Price = v;
pmf.price = (cell_text, true);
} else {
pmf.price = (cell_text, false);
}
}
public static RowParser.AssignColumnValue<ParsedManualFill> account(Func<string, uint> reverse_id_lookup) {
return impl;
void impl(ref ParsedManualFill pmf, string text) {
pmf.result.AccountId = reverse_id_lookup(text);
pmf.account = (text, pmf.result.AccountId != 0);
}
}
public static RowParser.AssignColumnValue<ParsedManualFill> user(Func<string, uint> reverse_id_lookup) {
return impl;
void impl(ref ParsedManualFill pmf, string text) {
pmf.result.UserId = reverse_id_lookup(text);
pmf.user = (text, pmf.result.UserId != 0);
}
}
}
}
}
@@ -0,0 +1,23 @@
using mbts.ui;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.shell.manual_fills {
[ProtoContract]
class ManualFillsSettings {
[ProtoMember(1)]
readonly SortingCriteria[] _sorting_criterion;
public static ManualFillsSettings Default =>
new ManualFillsSettings(new[] { new SortingCriteria(nameof(mbts.ui.spark.ManualFillsView.Columns.Security), true) });
public ManualFillsSettings() { }
public ManualFillsSettings(IEnumerable<SortingCriteria> sorting_criterion) =>
_sorting_criterion = sorting_criterion.ToArray();
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criterion ?? Array.Empty<SortingCriteria>();
}
}
@@ -0,0 +1,222 @@
using bts.api;
using bts.api.core;
using bts.api.position;
using bts.collections;
using mbts;
using mbts.spark;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.shell;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Input;
using Disposable = mbts.Disposable;
using ManualFillComponent = mbts.ui.spark.ManualFillsImportConflictsView.ManualFillComponent;
namespace bts.shell.manual_fills {
enum EditableItemState {
None, // Committed and not modified
Modified, // Committed and modified
Uncommitted // Uncommitted (modified n/a)
}
class ManualFillsVM : DisposableViewModelBase {
const int _max_unconfirmed_removes = 16;
static readonly IComparer<ManualFillRow> _row_key_comparer = Comparer<ManualFillRow>.Create((a, b) => a.key.CompareTo(b.key));
readonly IBus _bus;
readonly ManualFillsModel _model;
readonly ObservableCollection<ManualFillRow>
_persisted_rows = new ObservableCollection<ManualFillRow>(),
_placeholder_rows = new ObservableCollection<ManualFillRow>();
long _placeholder_id_counter = long.MinValue;
IDisposable _import_fills_hooks;
public static ManualFillsVM create(IBus bus, OrdersModel model) =>
new ManualFillsVM(bus, new ManualFillsModel(model));
public ManualFillsVM(IBus bus, ManualFillsModel model) {
_bus = bus;
_model = model;
var merged_rows = new ObservableAggregateCollection<ManualFillRow>(_persisted_rows, _placeholder_rows);
rows_view = CollectionViewSource.GetDefaultView(merged_rows);
// mons
var dirty_mon = new ReactiveThresholdMonitor<ManualFillRow>(r => r.subscribe_is_dirty());
var selection_mon = new ReactiveThresholdMonitor<ManualFillRow>(r => r.subscribe_is_selected());
var placeholder_mon = new ReactiveThresholdMonitor<ManualFillRow>();
// commands
commit_all_fills_cmd = ObservantAsyncCommand.create(() => _commit_async(merged_rows), out var commit_all_fils_observer);
commit_selected_fills_cmd = ObservantAsyncCommand.create(() => _commit_async(selection_mon.filtered_items), out var commit_selected_fills_observer);
revert_all_cmd = ObservantCommand.create(() => revert(dirty_mon.filtered_items.ToList()), out var revert_all_observer);
revert_selected_fills_cmd = ObservantCommand.create(() => revert(selection_mon.filtered_items.ToList()), out var revert_selected_fills_observer);
remove_selected_fills_cmd = ObservantAsyncCommand.create(remove_selected_fills_async, out var remove_selected_fills_observer);
add_fill_cmd = new DelegateCommand<ManualFillRow>(_add_fill);
import_fills_cmd = FileDialogCommand.open_file(_import_fills, extensions: ManualFillsParser.file_extension);
//
var dirty_or_new_src = dirty_mon.CombineLatest(placeholder_mon, LogicalDelegates.Or);
_dispose = Disposable.Create(
RXCollection.connect(_persisted_rows,
_model.manual_fills.OnDispatcher(),
Observable.Return(Comparer<ManualFillData>.Create((a, b) => a.key.CompareTo(b.key))),
mfd => new ManualFillRow(mfd),
mfr => mfr.data),
_bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(m => {
security_map = m;
RaisePropertyChanged(nameof(security_map));
}),
ObservableAutoWrapper.connect_item_hooks(_persisted_rows, r => Disposable.Create(dirty_mon.join(r), selection_mon.join(r))),
ObservableAutoWrapper.connect_item_hooks(_placeholder_rows, r => Disposable.Create(selection_mon.join(r), placeholder_mon.join(r, Observable.Return(true)))),
// command param subs
dirty_mon.Subscribe(revert_all_observer),
dirty_or_new_src.OnDispatcher().Subscribe(commit_all_fils_observer),
selection_mon.Subscribe(remove_selected_fills_observer),
selection_mon.CombineLatest(dirty_mon, LogicalDelegates.And).OnDispatcher().Subscribe(revert_selected_fills_observer),
selection_mon.CombineLatest(dirty_or_new_src, LogicalDelegates.And).OnDispatcher().Subscribe(commit_selected_fills_observer),
// accounts/users subs
RXCollection.connect(accounts, model.accounts.OnDispatcher(), a => new AccountTuple?(a), a => a.Value, Comparer<AccountTuple>.Default),
RXCollection.connect(users, model.users.OnDispatcher(), u => new UserTuple?(u), u => u.Value, Comparer<UserTuple>.Default),
Disposable.Create(() => Disposable.dispose(ref _import_fills_hooks))
);
// local methods
void revert(IReadOnlyList<ManualFillRow> rows) {
foreach (var r in rows)
r.revert();
}
async Task remove_selected_fills_async() {
if (selection_mon.filtered_items.Count < _max_unconfirmed_removes || BTSMessageBox.Confirm($"Are you sure you want to delete {selection_mon.filtered_items.Count} fills?", "Delete Fills")) {
var persisted_fills = new List<ManualFill>();
foreach (var r in selection_mon.filtered_items.ToList()) {
if (r.placeholder_key.HasValue)
_placeholder_rows.RemoveSorted(r, _row_key_comparer);
else persisted_fills.Add(r.to_man_fill());
}
await _model.delete_fills(persisted_fills);
}
}
}
public ICollectionView rows_view { get; }
// misc items sources
public IEnumerable<Side> sides => new[] { Side.Buy, Side.Sell };
public ObservableCollection<AccountTuple?> accounts { get; } = new ObservableCollection<AccountTuple?>();
public ObservableCollection<UserTuple?> users { get; } = new ObservableCollection<UserTuple?>();
public IReadOnlyDictionary<uint, SecurityMD> security_map { get; private set; } = DictionaryHelper.empty<uint, SecurityMD>();
// commands
public ICommand add_fill_cmd { get; }
public ICommand commit_all_fills_cmd { get; }
public ICommand commit_selected_fills_cmd { get; }
public ICommand import_fills_cmd { get; }
public ICommand remove_selected_fills_cmd { get; }
public ICommand revert_all_cmd { get; }
public ICommand revert_selected_fills_cmd { get; }
public ManualFillRow row_factory() =>
new ManualFillRow(
new ManualFillData(
new ManualFillKey(_placeholder_id_counter++),
new ManualFillTuple(new ManualFillImmutable(new ManualFill {
Side = Side.Buy
}), null, accounts.FirstOrDefault(), users.FirstOrDefault())));
void _add_fill(ManualFillRow new_row) =>
_placeholder_rows.InsertSorted((new_row ?? (new_row = row_factory())).placeholder_key.HasValue ?
new_row : throw new InvalidOperationException("New row is already persisted."),
_row_key_comparer);
async Task _commit_async(IEnumerable<ManualFillRow> rows) {
var updates = new List<ManualFill>();
var adds = new List<(ManualFill man_fill, long pl_key)>();
foreach (var r in rows) {
if (r.is_dirty) {
updates.Add(r.to_man_fill());
} else if (r.placeholder_key.HasValue) {
adds.Add((r.to_man_fill(), r.placeholder_key.Value));
}
}
if (updates.Count > 0)
await _model.update_fills(updates);
if (adds.Count > 0) {
await _model.create_fills(adds.WrapSelector(r => r.man_fill, null));
_placeholder_rows
.WrapSelector(r => r.placeholder_key.Value, null)
.RemoveSorted(adds.Select(r => r.pl_key));
}
}
void _import_fills(string file_name) {
try {
var lines = File.ReadAllLines(file_name);
var parsed_fills = ManualFillsParser.parse_rows(
lines,
MappedDictionary.create(security_map, kv => kv.Value.security),
accounts.ToDictionary(a => a.Value.id, a => a.Value.name),
users.ToDictionary(u => u.Value.id, u => u.Value.name)
).ToList();
var conflicts = ManualFillsParser.get_conflicts(parsed_fills);
if (conflicts.Values.Any(c => c.Count > 0)) {
// init vm
var vm = new ManualFillsImportVM(parsed_fills.Count) {
security_map = security_map
};
// add rows
foreach (var kv in conflicts) {
var assign_del = ManualFillsParser.assign_component(kv.Key);
foreach (var text_groups in kv.Value) {
var eval = get_error_value(kv.Key);
var row = new ManualFillsImportVM.Row(kv.Key, text_groups.Key, text_groups.Value, eval, eval);
vm.rows.Add(row);
vm.import_request += () => {
foreach (var pmf in text_groups.Value)
assign_del(pmf.result, row.value);
};
}
}
var win = new mbts.ui.spark.ManualFillsImportConflictsDialog {
DataContext = vm,
Title = ManualFillsWindowFactory.window_name,
Topmost = true
};
vm.RequestClose += (s, e) => Disposable.dispose(ref _import_fills_hooks);
vm.import_request += () => {
add_imported_fills();
Disposable.dispose(ref _import_fills_hooks);
};
Disposable.replace(ref _import_fills_hooks, Disposable.Create(
ObservableAutoWrapper.connect(accounts, vm.accounts),
ObservableAutoWrapper.connect(users, vm.users),
vm,
Disposable.Create(close)
));
win.move_to_mouse(-200, -75);
win.Show();
void close() => WindowUtilities.force_close(win);
object get_error_value(ManualFillComponent component) {
switch (component) {
case ManualFillComponent.Price: return double.NaN;
case ManualFillComponent.Size: return 0;
default: return null;
}
}
} else {
add_imported_fills();
}
void add_imported_fills() =>
_placeholder_rows.InsertSorted(parsed_fills.Select(pmf => new ManualFillRow(new ManualFillData(new ManualFillKey(_placeholder_id_counter++), new ManualFillTuple(new ManualFillImmutable(pmf.result),
pmf.result.SecurityKey != 0 && security_map.TryGetValue(pmf.result.SecurityKey, out var smd) ? smd.security : (SecurityImmutable?)null,
accounts.FirstOrDefault(a => a?.id == pmf.result.AccountId),
users.FirstOrDefault(a => a?.id == pmf.result.UserId))))), _row_key_comparer);
} catch (Exception ex) {
BTSMessageBox.Error($"Error importing fills: {ex}.", ManualFillsWindowFactory.window_name);
}
}
}
}
@@ -0,0 +1,247 @@
<UserControl
x:Class="mbts.ui.spark.ManualFillsView"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:usp="clr-namespace:mbts.ui.spark"
>
<AdornerDecorator>
<Grid>
<DataGrid
x:Name="data_grid"
u:DataGridBehavior.SingleClickEdit="True"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserSortColumns="True"
HeadersVisibility="All"
ItemsSource="{Binding rows_view}"
MinRowHeight="17"
RowHeaderWidth="22"
>
<!--Row Header Style-->
<DataGrid.RowHeaderStyle>
<Style TargetType="DataGridRowHeader">
<Setter Property="Content" Value="{Binding}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border x:Name="Root">
<u:StateManager.VisualState>
<MultiBinding Converter="{x:Static usp:ManualFillsView.RowHeaderVisualStateConverter}">
<Binding Path="editable_item_state"/>
<Binding Path="is_valid"/>
</MultiBinding>
</u:StateManager.VisualState>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState Name="None"/>
<VisualState Name="Modified">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="pencil" Storyboard.TargetProperty="(UIElement.Opacity)" BeginTime="0" Duration="0" To="0.7"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="(FrameworkElement.ToolTip)">
<DiscreteObjectKeyFrame KeyTime="0" Value="Fill has modified properties."/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState Name="Uncommitted">
<Storyboard>
<ColorAnimation Storyboard.TargetName="new_item" Storyboard.TargetProperty="(usp:ManualFillsView.ImageBrush).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="#B3006000"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="(FrameworkElement.ToolTip)">
<DiscreteObjectKeyFrame KeyTime="0" Value="New Fill."/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState Name="Error">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="exclamation" Storyboard.TargetProperty="(UIElement.Opacity)" BeginTime="0" Duration="0" To="1"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="(FrameworkElement.ToolTip)">
<DiscreteObjectKeyFrame KeyTime="0" Value="Fill has invalid properties."/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState Name="ErrorUncommitted">
<Storyboard>
<ColorAnimation Storyboard.TargetName="new_item" Storyboard.TargetProperty="(usp:ManualFillsView.ImageBrush).(SolidColorBrush.Color)" BeginTime="0" Duration="0" To="#CCCD4848"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="(FrameworkElement.ToolTip)">
<DiscreteObjectKeyFrame KeyTime="0" Value="New Fill has invalid properties."/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Image x:Name="new_item" usp:ManualFillsView.ImageBrush="Transparent" Height="14">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing Brush="{Binding Path='(usp:ManualFillsView.ImageBrush)', ElementName=new_item}" Geometry="{Binding Source={StaticResource {x:Static u:Icons.DocumentAddGeometryKey}}}"/>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<Image x:Name="exclamation" Height="14" Opacity="0">
<Image.Source>
<Binding Source="{x:Static MessageBoxImage.Exclamation}">
<Binding.Converter>
<cv:SystemIconConverter/>
</Binding.Converter>
</Binding>
</Image.Source>
</Image>
<Image x:Name="pencil" Height="14" Source="{x:Static u:Icons.Pencil}" Opacity="0">
<Image.RenderTransform>
<RotateTransform Angle="45" CenterX="0" CenterY="7"/>
</Image.RenderTransform>
</Image>
</Grid>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowHeaderStyle>
<!--Cell Style-->
<DataGrid.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DataGridCellBaseStyle}">
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</DataGrid.CellStyle>
<!--Row Style-->
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding is_selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Background" Value="{StaticResource SelectedRowBackground}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<!--Record ID-->
<DataGridTextColumn c:DataGridEx.CellKey="{x:Static RecordID, MemberType={u:Nested usp:ManualFillsView.Columns}}" Header="ID" Binding="{Binding record_id_display_text}" SortMemberPath="record_id" IsReadOnly="True"/>
<!---Security-->
<DataGridTemplateColumn c:DataGridEx.CellKey="{x:Static Security, MemberType={u:Nested usp:ManualFillsView.Columns}}" Header="Security" SortMemberPath="security">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding security}" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<c:ContractPickerPopup
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CloseOnCancel="True"
CloseOnOK="True"
Header="{Binding security}"
IsEditMode="False"
IsLazy="True"
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=DataGrid}, Mode=OneWay}"
SelectedSecurity="{Binding security, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ShowDialogSkirt="True"
/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--Account-->
<DataGridTemplateColumn c:DataGridEx.CellKey="{x:Static Account, MemberType={u:Nested usp:ManualFillsView.Columns}}" Header="Account" SortMemberPath="account">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding account}" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<ComboBox
x:Name="mutable_view"
ItemsSource="{Binding DataContext.accounts, RelativeSource={RelativeSource AncestorType=DataGrid}}"
SelectedItem="{Binding account, UpdateSourceTrigger=PropertyChanged}"
Visibility="Visible"
/>
<TextBlock
x:Name="readonly_view"
Text="{Binding account}"
VerticalAlignment="Center"
Visibility="Collapsed"
/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding placeholder_key}" Value="{x:Null}">
<Setter TargetName="mutable_view" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="readonly_view" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--User-->
<DataGridTemplateColumn c:DataGridEx.CellKey="{x:Static User, MemberType={u:Nested usp:ManualFillsView.Columns}}" Header="User" SortMemberPath="user">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding user}" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.users, RelativeSource={RelativeSource AncestorType=DataGrid}}" SelectedItem="{Binding user, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--Side-->
<DataGridTemplateColumn c:DataGridEx.CellKey="{x:Static Side, MemberType={u:Nested usp:ManualFillsView.Columns}}" Header="Side" SortMemberPath="side">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding side}" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.sides, RelativeSource={RelativeSource AncestorType=DataGrid}}" SelectedItem="{Binding side, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--Size-->
<DataGridTemplateColumn c:DataGridEx.CellKey="{x:Static Size, MemberType={u:Nested usp:ManualFillsView.Columns}}" Header="Qty" SortMemberPath="side">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding size}" VerticalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<c:TickUpDownUInt MinWidth="40" Value="{Binding size, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--Price-->
<DataGridTemplateColumn c:DataGridEx.CellKey="{x:Static Price, MemberType={u:Nested usp:ManualFillsView.Columns}}" Header="Price" SortMemberPath="price">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="price"/>
<Binding Path="spec"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<usp:PriceEntryControl ContractSpec="{Binding spec}" Value="{Binding price, UpdateSourceTrigger=PropertyChanged}" AlwaysReformatOnSpecChange="True"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</AdornerDecorator>
</UserControl>
@@ -0,0 +1,97 @@
using bts.shell.manual_fills;
using mbts.ui.controls.adorners;
using mbts.ui.converters;
using mbts.utils;
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
namespace mbts.ui.spark {
/// <summary>
/// Interaction logic for ManualFillsView.xaml
/// </summary>
partial class ManualFillsView : UserControl {
public static readonly IMultiValueConverter
RowHeaderVisualStateConverter = ValueConverter.create<EditableItemState, bool, string>(
(edit_state, is_valid) => is_valid ? edit_state.ToString() : edit_state == EditableItemState.Uncommitted ? "ErrorUncommitted" : "Error");
public enum Columns {
RecordID,
Security,
Account,
User,
Side,
Size,
Price
}
IDisposable _transaction;
public ManualFillsView() =>
InitializeComponent();
public static readonly DependencyProperty ImageBrushProperty =
DependencyProperty.RegisterAttached("ImageBrush", typeof(Brush), typeof(ManualFillsView));
public static Brush GetImageBrush(DependencyObject d) => d.GetValue(ImageBrushProperty) as Brush;
public static void SetImageBrush(DependencyObject d, Brush value) => d.SetValue(ImageBrushProperty, value);
public void begin_add_fill() {
if (!(DataContext is ManualFillsVM vm))
return;
Disposable.dispose(ref _transaction);
var row = vm.row_factory();
var row_editor = new ManualFillRowEditor { DataContext = row };
row_editor.CommandBindings.Add(new CommandBinding(ManualFillRowEditor.CommitCommand, commit_executed));
row_editor.CommandBindings.Add(new CommandBinding(ManualFillRowEditor.CancelCommand, cancel_executed));
// dynamic resources
row_editor.Resources[ManualFillRowEditor.AccountsKey] = vm.accounts;
row_editor.Resources[ManualFillRowEditor.UsersKey] = vm.users;
row_editor.Resources[ManualFillRowEditor.SidesKey] = vm.sides;
FocusManager.SetIsFocusScope(row_editor, true);
var sec_hook = vm.as_demuxer().subscribe(nameof(ManualFillsVM.security_map)).OnDispatcher().Subscribe(v => row_editor.Resources[ManualFillRowEditor.SecuritiesKey] = v);
// popup adorner
var popup_adorner = new PopupAdorner(
data_grid, data_grid,
new Border {
Child = row_editor,
Background = BTSColors.common_background_brush,
BorderBrush = Brushes.Black,
BorderThickness = new Thickness(1),
Padding = new Thickness(0, 6, 0, 0),
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top
},
HorizontalPosition.Middle,
VerticalPosition.Middle,
0,
22) {
OpenRelativeToMouse = true,
EnableDragMove = true,
ClipToBounds = true
};
popup_adorner.attach();
popup_adorner.IsOpen = true;
row_editor.Focus();
_transaction = new CompositeDisposable(sec_hook) { destroy_popup };
// local methods
void commit_executed(object sender, ExecutedRoutedEventArgs e) {
vm.add_fill_cmd?.TryExecuteIfCan(row);
Disposable.dispose(ref _transaction);
}
void cancel_executed(object sender, ExecutedRoutedEventArgs e) {
row.revert();
Disposable.dispose(ref _transaction);
}
void destroy_popup() {
popup_adorner.IsOpen = false;
popup_adorner.detach();
}
}
}
}
@@ -0,0 +1,133 @@
using bts.api;
using bts.shell.manual_fills;
using bts.spark;
using mbts;
using mbts.collections;
using mbts.spark;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.spark;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace bts.shell {
class ManualFillsWindowFactory : ShellWindowFactory<ManualFillsSettings> {
public const string
window_type = "ManualFills",
window_name = "Manual Fills";
const double _window_min_width = 466;
readonly IBus _bus;
readonly OrdersModel _orders_model;
readonly ManualFillsConfig _manual_fills_conf = ManualFillsConfig.read();
public ManualFillsWindowFactory(IShellModule owner, IBus bus, OrdersModel orders_model) : base(owner, window_name) {
_bus = bus;
_orders_model = orders_model;
_dispose = bus.subscribe(SparkTags.ShowManualFillsTag).Subscribe(open_window);
//
async void open_window(ManualFillsSettings state) =>
_bus.post(SparkTags.ShowWindow, await load(state));
}
public override Task<IShellWindow> load(ManualFillsSettings settings) {
return Task.FromResult<IShellWindow>(SparkWindow<ManualFillsSettings>.create(
window_type, window_name, _bus, this, Icons.DataEdit, _manual_fills_conf.thread_id, thread_init, _window_min_width));
//
(UIElement, IEnumerable<SparkCommand>, Func<ManualFillsSettings>, IDisposable) thread_init() {
var vm = ManualFillsVM.create(_bus, _orders_model);
var view = new ManualFillsView { DataContext = vm };
view.data_grid.Sorting += on_sorting;
IReadOnlyList<SortingCriteria> last_sorting_criterion = Array.Empty<SortingCriteria>();
using (apply_sort()) {
var col_sort_tuples =
from sc in settings.sorting_criterion
join col in view.data_grid.Columns on sc.col_name equals _col_id(col)
select (col, sc);
foreach (var (col, sc) in col_sort_tuples) {
col.SortDirection = sc.direction();
vm.rows_view.SortDescriptions.Add(new SortDescription(col.SortMemberPath, sc.direction()));
}
}
var cmds = new[] {
new SparkCommandCommand("Add New", Icons.AddBoxes, _manual_fills_conf.modal_transactions ? new DelegateCommand(() => view.begin_add_fill()) : vm.add_fill_cmd) { group = CommandGroups.Add },
new SparkCommandCommand("Import Fills", Icons.Import, vm.import_fills_cmd) { group = CommandGroups.Add },
new SparkCommandCommand("Revert Selected", Icons.UndoCircular,vm.revert_selected_fills_cmd) { group = CommandGroups.Revert, show_in_toolbar = false },
new SparkCommandCommand("Revert All", Icons.UndoCircular, vm.revert_all_cmd) { group = CommandGroups.Revert },
new SparkCommandCommand("Save Selected", Icons.Disk, vm.commit_selected_fills_cmd) { group = CommandGroups.Save, show_in_toolbar = false },
new SparkCommandCommand("Save All", Icons.Disk, vm.commit_all_fills_cmd) { group = CommandGroups.Save },
new SparkCommandCommand("Delete Selected", Icons.Trash, vm.remove_selected_fills_cmd)
};
return (view, cmds, get_settings, Disposable.Create(
vm.rows_view.subscribe_sorting_criterion(sm => view.data_grid.Columns.TryGetFirst(dc => dc.SortMemberPath == sm, out DataGridColumn match) ? _col_id(match) : null)
.Subscribe(sc => last_sorting_criterion = sc),
vm));
//
void on_sorting(object sender, DataGridSortingEventArgs e) {
e.Handled = true;
using (apply_sort()) {
var new_sort_desc = new SortDescription(e.Column.SortMemberPath, e.Column.SortDirection == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending);
e.Column.SortDirection = new_sort_desc.Direction;
if (KeyboardExtensions.IsShiftDown()) {
if (vm.rows_view.SortDescriptions.TryGetFirst(sd => sd.PropertyName == e.Column.SortMemberPath, out var match, out int index)) {
vm.rows_view.SortDescriptions[index] = new_sort_desc;
} else {
vm.rows_view.SortDescriptions.Add(new_sort_desc);
}
} else {
vm.rows_view.SortDescriptions.Clear();
foreach (var col in view.data_grid.Columns.Except(new[] { e.Column }))
col.SortDirection = null;
vm.rows_view.SortDescriptions.Add(new_sort_desc);
}
}
}
/// Ensures that the primary sort description property (i.e., at index 0) is the placeholder_key.
/// This behavior has the effect of grouping placeholder rows at the bottom, without using <see cref="ICollectionView.GroupDescriptions"/> (which is incompatible with row virtualization).
IDisposable apply_sort() =>
Disposable.Create(() => {
var pk_sort_desc = new SortDescription(nameof(ManualFillRow.placeholder_key), ListSortDirection.Ascending);
if (vm.rows_view.SortDescriptions.Count > 0) {
if (vm.rows_view.SortDescriptions[0].Equals(pk_sort_desc))
return;
vm.rows_view.SortDescriptions.removeFirst(sd => sd.PropertyName == pk_sort_desc.PropertyName);
}
vm.rows_view.SortDescriptions.Insert(0, pk_sort_desc);
});
ManualFillsSettings get_settings() =>
new ManualFillsSettings(last_sorting_criterion);
}
}
static string _col_id(DataGridColumn col) =>
DataGridEx.GetCellKey(col)?.ToString();
readonly struct ManualFillsConfig {
public readonly bool modal_transactions;
public readonly string thread_id;
ManualFillsConfig(string thread_id, bool modal_transactions) {
this.thread_id = thread_id;
this.modal_transactions = modal_transactions;
}
public static ManualFillsConfig read() => new ManualFillsConfig(
mbts.config.Configuration.get<bts.config.MultiThreadedWindows>().manual_fills ? window_type : null,
mbts.config.Configuration.get<bts.config.ManualFillsOptions>().modal_transactions);
}
static class CommandGroups {
public const string
Add = "Add",
Save = "Save",
Revert = "Revert";
}
}
}
@@ -6,7 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sp="clr-namespace:mbts.spark"
xmlns:uc="clr-namespace:mbts.ui"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:usp="clr-namespace:mbts.ui.spark"
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
@@ -22,6 +22,7 @@
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
<ResourceDictionary Source="/Modules/Trading/OrderBook/View/OrderParamsPanel.xaml"/>
</ResourceDictionary.MergedDictionaries>
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
@@ -49,16 +50,56 @@
<Style x:Key="{x:Static sp:MiniOrderBookView.RowStyleKey}" TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
<Style.Triggers>
<!-- Completed order background -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding status}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
<Condition Binding="{Binding status}" Value="{u:Enum Completed, Type=order:OrderStatus}"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={uc:Nested uc:BTSColors.Orders}}"/>
<Setter Property="Background" Value="{x:Static Member=completed_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<!-- Error, RejectedBySpark, Disconnected order background -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding status}" Value="{u:Enum Error, Type=order:OrderStatus}"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static Member=error_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding status}" Value="{u:Enum RejectedBySpark, Type=order:OrderStatus}"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static Member=error_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding status}" Value="{u:Enum Disconnected, Type=order:OrderStatus}"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static Member=error_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<!-- Cancel order background -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding status}" Value="{u:Enum Canceled, Type=order:OrderStatus}"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{x:Static Member=canceled_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="{x:Static sp:MiniOrderBookView.RowTemplateKey}">
<sp:MiniOrderBookRowView/>
@@ -82,7 +123,8 @@
RowTemplate="{StaticResource {x:Static sp:MiniOrderBookView.RowTemplateKey}}"
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
RowsHorizontalScrollBarVisibility="Collapsed"
RowHeight="20"
RowHeight="20"
SelectionMode="Single"
>
<c:DIYGrid.Resources>
<ResourceDictionary>
@@ -95,7 +137,7 @@
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
<Style x:Key="{x:Static sp:OrderBookView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
<Style x:Key="{x:Static sp:OrderBookGridView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
@@ -149,9 +191,8 @@
</ResourceDictionary>
</c:DIYGrid.Resources>
<c:DIYGrid.InputBindings>
<!--<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/> -->
</c:DIYGrid.InputBindings>
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
</c:DIYGrid.InputBindings>
<c:DIYGrid.CommandBindings>
<CommandBinding
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
@@ -1,11 +1,12 @@
using mbts.ui.controls;
using System.Windows.Controls;
using System.Windows.Input;
namespace mbts.spark {
internal partial class MiniOrderBookView : UserControl {
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice, Attach }
public const string
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
@@ -20,7 +21,10 @@ namespace mbts.spark {
}
}
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
public MiniOrderBookRowView() : base() { }
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookGridView.ColumnKeys> {
public MiniOrderBookRowView() : base() {
FocusManager.SetIsFocusScope(this, true);
KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Cycle);
}
}
}
File diff suppressed because it is too large. Load diff
Loaded 100 of 738 files, more files were not shown because too many files have changed in this diff. Show more