Compare commits

..
505 Commits
Author SHA1 Message Date
Jake Norbut 6cc738c1ba can't keept up w/ the version numbers 2018-08-09 20:58:42 -05:00
Jake Norbut e0a2aad2f7 Merged in SPARK-1535 (pull request #54)
SPARK-1535

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-08-10 01:34:16 +00:00
Jake Norbut 2b5c2b9f82 updating notes 2018-08-09 20:33:37 -05:00
Jake Norbut 2a9bbbd182 updating notes post merge 2018-08-09 16:16:37 -05:00
Jake Norbut e4d649b245 Merge branch 'prod' of bitbucket.org:bluetradesys/spark into SPARK-1535 2018-08-09 16:15:51 -05:00
Eric Harding f5b114b9ce SPARK-1548 Enable merging live market data with simulator 2018-08-09 16:54:21 -04:00
Jake Norbut 0a7ef4a4cd Adding Expand/Collapse All to the OrderBook tool-well area (and right click menu) 2018-08-09 15:53:52 -05:00
Eric Harding 6cc1b38358 Update release notes 2018-08-08 17:28:30 -04:00
Eric Harding 0f3169ef05 merge SPARK-1515-OnixS. Updated onixs for CME precision extension 2018-08-08 13:31:41 -04:00
Eric Harding f72753cd77 Onixs debug logging depends on 3 other settings. awesome. 2018-08-08 10:26:59 -04:00
Jake Norbut 2240b3a85d Updating version under which new features will reside 2018-08-07 10:10:02 -05:00
Jake Norbut e792f3a697 updating notes 2018-08-07 10:08:24 -05:00
Jake Norbut 85581ebf41 Symbol Column filter is now a Discrete values; added new column to saved filter settings 2018-08-07 09:48:44 -05:00
Taha Afzal ea180d8ebc In case of a state of position violation, Limiter lets orders in if they are closing the position 2018-08-06 22:32:26 -04:00
Jake Norbut 1f7f091a51 Adding Last Hour hyperlink which sets the Begin time in Period mode to the Now - 1 hour 2018-08-06 17:27:17 -05:00
Jake Norbut 78be34cdd3 adding Symbol column to the Orderbook; column filter is a regex at the moment, which is not ideal 2018-08-06 16:32:09 -05:00
Jake Norbut b59158ea43 Orderbook rows are now collapsed by default; Orders in Error status can be recreated 2018-08-06 09:57:35 -05:00
Pedro Pinto 9937fc7cd2 Updated release notes. 2018-08-03 23:15:01 +00:00
Pedro Pinto f3f53d05c8 Fixed spreader order state update. From the middle of the Atlantic.
Go BTS.
2018-08-03 23:13:30 +00:00
Eric Harding b49b7f01d5 Updated linked onixs version. Not sure how we linked with new headers and old version. 2018-08-03 17:31:23 -04:00
Eric Harding a699865342 SPARK-1515 Add specializations to make things build 2018-08-03 16:54:07 -04:00
Michael Harding 1b5f58c572 SparkAdmin: The return of wildcards and other tweaks 2018-08-03 14:16:20 -04:00
Eric Harding bae0b797e9 advance externals pointer for onixs 4.8 2018-08-02 17:15:08 -04:00
Eric Harding 79a69e29a0 SPARK-1515 Switch MDP3Listener to new templates 2018-08-02 16:37:13 -04:00
Pedro Pinto 38bf38c62d Fixed price conversion for spread leg fill. 2018-08-02 20:22:58 +00:00
Jake Norbut 4e08a43d0c Cpp - updating HO to match RB per Kev 2018-08-02 14:12:13 -05:00
Pedro Pinto 4f912f99bd release_notes.txt edited online with Bitbucket 2018-08-01 21:18:45 +00:00
John Colvin ae7e1761a7 SPARK-1527 Production client crash (ListBoxEx) 2018-08-01 17:10:30 -04:00
Jake Norbut 4891eea5ba Merged in SPARK-1166 (pull request #52)
SPARK-1166

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-08-01 19:06:39 +00:00
Jake Norbut bc36db0480 TimeAndSales - Replacing 444 with calculations made from the the DefaultColumnWidths 2018-08-01 14:03:42 -05:00
Jake Norbut cdaba74c06 Fixing RB cpp; moving known good cpps to a different file just so that future cpp updates will update this other file and not the main script 2018-07-30 10:21:52 -05:00
Jake Norbut b045307f30 updating SI and HG cpp per Taha 2018-07-27 17:23:23 -05:00
Pedro Pinto 60a7b504db SPARK-1530, Iceberg cancels itself (rather than error) if a child is canceled 2018-07-27 22:04:25 +00:00
Jake Norbut 0f62f14a1d Adding Agressor as a column to TaS as well as setting row bg color/opacity based on agg side 2018-07-27 11:17:52 -05:00
Eric Harding 87030dbb88 SPARK-1199 TradeMessage should include AggressorSide 2018-07-26 10:55:45 -05:00
Jake Norbut e209ec59e4 pnl: changing format string for pnl columns from F2 to N0, which removes cents and adds commas 2018-07-26 10:27:02 -05:00
Jake Norbut 92dbedf28f pnl - FillKind out, Security Type in 2018-07-25 17:36:51 -05:00
Jake Norbut 7993f75fb1 security picker - replacing naive comparison logic with the same Comparer mview uses 2018-07-25 10:07:37 -05:00
Jake Norbut 1cddd2bcf3 fixing typos in comment; fings numb from ac 2018-07-25 09:42:41 -05:00
Eric Harding 9f7e5846b8 Remove linebreak in support request log message so description will show up in slack 2018-07-25 10:42:01 -04:00
Eric Harding f7b3aac11a Reduce web apigateway request/response logging to diag level 2018-07-25 10:39:29 -04:00
Jake Norbut ec32101cef pnl: no longer logging when exchanged traded spread has legs with differing cpps 2018-07-25 09:36:39 -05:00
Jake Norbut 4e0cf68fa3 Pnl - pnl prices should not show in ticks 2018-07-24 09:28:44 -05:00
Jake Norbut 4e9a7d576e Merge branch 'qa' into prod 2018-07-23 19:03:39 -05:00
Jake Norbut 5e056dfff4 pnl - account for current time in calculating start of day time 2018-07-23 17:18:22 -05:00
Jake Norbut 4029ccf8e6 pnl - using actual mid and not a tick-rounded one 2018-07-23 16:06:49 -05:00
Pedro Pinto 22911d582b Merge branch 'qa' of bitbucket.org:bluetradesys/spark into qa 2018-07-23 20:35:30 +00:00
Pedro Pinto 6ef7a2ccb5 More release_notes 2018-07-23 20:32:40 +00:00
Jake Norbut 737a07cecb added notes 2018-07-23 14:57:45 -05:00
Eric Harding 1cbea3d1ee SPARK-1467 Favorites misbehaving 2018-07-23 15:47:12 -04:00
Pedro Pinto 4dbedfa1f7 Added ops_notes.txt 2018-07-23 19:41:09 +00:00
Pedro Pinto 074f87dd37 Release notes 2018-07-23 15:38:16 -04:00
Pedro Pinto 6ee0ca9589 Tweaked release_notes.txt 2018-07-23 14:48:33 -04:00
Michael Harding 5745c13355 Sim Order Routing 2018-07-23 11:22:51 -04:00
Kevin Darby 5f99edde0e small typo in test, 0.005 vs 0.05 2018-07-20 17:45:50 -04:00
Pedro Pinto d39bc5c73c Merge branch 'qa' of bitbucket.org:bluetradesys/spark into qa 2018-07-20 21:40:50 +00:00
Pedro Pinto 9cb6d5ab88 Fixed up tests to reflect lack of wild cards in Admin UI. 2018-07-20 21:40:44 +00:00
Pedro Pinto 278432a35b Fixed bootstraping of admin data to avoid wild cards everywhere. 2018-07-20 21:37:23 +00:00
Kevin Darby 13c414f301 fix small typo in Taha's test 2018-07-20 17:33:39 -04:00
Eric Harding 34f5265a42 Update test instructions 2018-07-20 17:13:37 -04:00
Pedro Pinto 83103e6dc4 Merge branch 'qa' of bitbucket.org:bluetradesys/spark into qa 2018-07-20 12:54:09 -04:00
Pedro Pinto add631b04d Fixed test configuration. 2018-07-20 12:53:56 -04:00
Pedro Pinto e08dc5dfba Add back the option to run sim without replay. 2018-07-20 12:53:34 -04:00
John Colvin 6a8e995c9c SPARK-1452 Synth grid is not filterable by sec key 2018-07-20 11:41:19 -04:00
John Colvin d7c460ac98 SPARK-1440 Creating > 2 legs from MD View 2018-07-20 11:22:20 -04:00
Eric Harding 7871dc5e4f Update 2 tests' instructions 2018-07-19 21:48:52 -04:00
Eric Harding 1aab1db4ca Update cmakedeps 2018-07-19 21:48:44 -04:00
John Colvin 373187c091 SPARK-1464 Couple of small things in the trigger UI 2018-07-19 18:35:06 -04:00
Jake Norbut 423d127ee9 Updating order book order to test new Recreate funcionality 2018-07-19 17:08:43 -05:00
Jake Norbut 14f306d50d SPARK-1471 Adding FillKind to PnL; also will always pass 0 as the time to md query 2018-07-19 16:38:49 -05:00
Eric Harding 71fe863f1b Add server_info http endpoint to serve http://versions 2018-07-19 15:12:39 -04:00
Pedro Pinto ac47f0246f Merge branch 'master' into qa 2018-07-19 18:41:34 +00:00
John Colvin 8e43f96331 SPARK-1469 Fills Window - add "FillKind" column 2018-07-19 14:11:59 -04:00
Jake Norbut 3187e4134f Merged in SPARK-1461 (pull request #49)
SPARK-1461 - price columns don't update tick format when security becomes known

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-07-19 18:07:10 +00:00
John Colvin 0331501ce4 fix build 2018-07-19 13:06:16 -04:00
John Colvin bd0f1a6f84 SPARK-1468 FillTupleSource - always use the fill-security-key (as opposed to the order-security-key) 2018-07-19 12:51:19 -04:00
Pedro Pinto 8776b80b07 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-07-19 16:36:30 +00:00
Pedro Pinto f5a72841b4 Tweaked build, hoping to fix jenkins failure. 2018-07-19 16:36:16 +00:00
Taha Afzal f9ada7fca2 manual test for trigger 2018-07-19 12:32:52 -04:00
Pedro Pinto 1cb4f24610 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-07-19 16:05:44 +00:00
Pedro Pinto 021c88f26b Removed obsolete tests. 2018-07-19 16:05:24 +00:00
Jake Norbut 014fa8e291 PnL - fixing bad refactoring job 2018-07-19 11:05:02 -05:00
Jake Norbut 71d54224bb Merged in SPARK-1465 (pull request #51)
SPARK-1465 - adding recreate order functionality to OrderBook window

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-07-19 15:55:07 +00:00
Pedro Pinto bebbf2a0f1 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-07-19 15:48:08 +00:00
Pedro Pinto 7413814e4a Fixed script permissions. 2018-07-19 15:42:49 +00:00
Pedro Pinto 36474ef3ea SPARK-1442, Handling leg fills. 2018-07-19 15:42:10 +00:00
Taha Afzal 974be037fe forgot pushing this merge in 2018-07-19 11:23:56 -04:00
Eric Harding a9ee246436 Share MDConsumer between MDSource and SecuritySrc. Fixes problem with synthetics when nobody calls scan_securities() to do security discovery 2018-07-19 10:44:58 -04:00
Taha Afzal b692742e30 better error messages for TriggerOrder 2018-07-19 10:36:37 -04:00
Taha Afzal c61cbba316 TriggerOrder incorrect assertion 2018-07-19 10:36:37 -04:00
Kevin Darby 4d99ffc86c SPARK-1464
I couldn't get the order price to default to the bid, but sending to John
2018-07-19 10:36:37 -04:00
Eric Harding 52001a55c6 MDConsumer::get_handle() needs to process async work before searching handles 2018-07-19 10:36:37 -04:00
Eric Harding 570dd387d2 MDConsumer::get_handle() needs to check if the handle is a synthetic 2018-07-19 10:36:37 -04:00
Taha Afzal 28a122aa7a TriggerOrder completed 2018-07-19 10:36:36 -04:00
Taha Afzal b2facaecd9 polishing TriggerOrder 2018-07-19 10:36:36 -04:00
Taha Afzal e346858516 2 things left, completed vs cancelled, and price rounding for equality 2018-07-19 10:36:36 -04:00
Taha Afzal be8ca67be5 TriggerOrder handling cancelled state better 2018-07-19 10:36:36 -04:00
Kevin Darby 197e68e357 Trigger order UI 2018-07-19 10:36:36 -04:00
Kevin Darby dfa3e4a38d python... 2018-07-19 10:36:36 -04:00
Taha Afzal 0f7625ff4a Conditional Order mostly done 2018-07-19 10:36:36 -04:00
Taha Afzal 88ab4fbb79 fixing build 2018-07-19 10:36:36 -04:00
Taha Afzal 5cc9b8c875 SPARK-1404 trigger order proto's 2018-07-19 10:36:36 -04:00
Pedro Pinto 187793d47c Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-07-19 13:58:39 +00:00
John Colvin 656c7104f6 fix ticks merge from prod to master 2018-07-18 18:14:24 -04:00
Pedro Pinto 359e01e8bd Merged 2018-07-18 22:07:36 +00:00
John Colvin 2a452d3201 Merge branch 'prod' (ticks) 2018-07-18 17:26:59 -04:00
Eric Harding 806ed427ab convert_security from Security to FixedSecurity needs to always copy tick fraction fields not just when it's glbx 2018-07-18 17:00:34 -04:00
Pedro Pinto 78cefa187a Merge 2018-07-18 16:48:47 -04:00
Pedro Pinto 8531b330dc Made it simpler to run cert. 2018-07-18 20:46:56 +00:00
Jake Norbut 54d9bde6b7 Merged in SPARK-1459 (pull request #48)
SPARK-1459 - order sub type filter not saving

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-07-18 20:23:35 +00:00
John Colvin fe6dabb5fe SPARK-1455 Tick display wrong for ZB, likely others 2018-07-18 15:53:05 -04:00
Jake Norbut f0b089d2e5 SPARK-1465 - adding recreate order functionality to OrderBook window 2018-07-18 09:18:45 -05:00
Pedro Pinto 8979c595a5 SPARK-1142, we now publish leg fills 2018-07-18 13:23:04 +00:00
Eric Harding ba4771aeee Add SIM to platform_for_exchange 2018-07-17 17:30:50 -04:00
Michael Harding 719c1e474b Merged in SparkAdmin (pull request #50)
SparkAdmin

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-07-17 20:21:02 +00:00
Michael Harding 72123a61a9 Merged master into SparkAdmin 2018-07-17 15:59:54 -04:00
Michael Harding 3b1c2dc52b SPARK-1368: better filtering in limits 2018-07-17 15:59:15 -04:00
Eric Harding 0546f0d9fb Move tick data from Glbx to ContractSpec 2018-07-17 15:47:16 -04:00
Pedro Pinto 05f1be77ef Merge 2018-07-17 19:21:32 +00:00
Jake Norbut 79e876e1dc SPARK-1461 - price columns don't update tick format when security becomes known 2018-07-17 13:35:03 -05:00
Michael Harding b6cc413733 SPARK-1368: Limits refinements and fixes 2018-07-17 12:35:26 -04:00
Jake Norbut a5e9f40f01 SPARK-1459 - order sub type filter not saving 2018-07-17 09:45:28 -05:00
Jake Norbut e40dc48fa8 Merged pnl_nits2 into master 2018-07-17 09:07:11 -05:00
Jake Norbut 5aab4312fc merge; fixing merge 2018-07-17 09:05:57 -05:00
John Colvin 9ede4c7736 Merged in exchange-ticks (pull request #46)
SPARK-1455 Tick display wrong for ZB, likely others

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-07-17 02:42:37 +00:00
John Colvin b63b0b6303 Merge remote-tracking branch 'origin/master' into exchange-ticks 2018-07-16 22:36:07 -04:00
John Colvin e004580d99 Exchange Ticks - pr edits 2018-07-16 22:24:26 -04:00
Eric Harding 7403057c58 Threading tweak to ShellDiagnosticService 2018-07-16 22:02:45 -04:00
Eric Harding 47730bd3d4 Make cmake happy 2018-07-16 21:59:55 -04:00
Pedro Pinto 9964d2579a Merge remote-tracking branch 'origin/master' into sim_cert_simultaenously 2018-07-16 21:33:56 +00:00
Pedro Pinto 6960ee1a12 Changing replay mechanis in sim to allow for cert and sim md. 2018-07-16 21:33:01 +00:00
Michael Harding 9edde6b8e3 SPARK-1368: Data table width 2018-07-16 15:12:42 -04:00
Michael Harding 8ef02ea4cb SPARK-1368: Moving buttons again 2018-07-16 15:08:57 -04:00
Eric Harding dc99ea6ea4 SPARK-955 add unit test to cover this case 2018-07-16 15:03:56 -04:00
John Colvin 9c6993d70c Exchange Ticks - pr edits 2018-07-16 14:32:20 -04:00
John Colvin c6be4dad17 Exchange Ticks - truncate display-digits 2018-07-16 14:19:43 -04:00
Jake Norbut bd2e2674bf PR suggestion 2018-07-16 13:09:02 -05:00
Michael Harding 71ddcd135b Merged in SparkAdmin (pull request #47)
SparkAdmin

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-07-16 18:03:27 +00:00
Michael Harding bffb7686a8 Merged master into SparkAdmin 2018-07-16 12:58:59 -04:00
Michael Harding a73bf0ce7c SPARK-1370: Set multiple limits to the same value 2018-07-16 12:58:12 -04:00
Pedro Pinto 94c5a1bcd1 SPARK-1156, bootstraped limits for sim exchange. 2018-07-16 14:41:27 +00:00
Pedro Pinto ca2a346054 Work on a new queue. 2018-07-16 13:39:25 +00:00
Pedro Pinto b990923cf5 Fixed helper script. 2018-07-16 13:38:57 +00:00
Eric Harding 8490151844 Merged in SPARK-1456 (pull request #44)
SPARK-1456 Centralized ladder re-center

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-07-16 11:23:19 +00:00
Eric Harding 78c8234eef SPARK-1456 ladder re-center 2018-07-16 07:22:34 -04:00
Pedro Pinto 1f1f92d337 SPARK-1156 2018-07-15 00:34:15 +00:00
John Colvin a6426e7a98 SPARK-1455 Tick display wrong for ZB, likely others 2018-07-14 17:48:21 -04:00
Eric Harding dc1a7b3f5d Remove defunct DataPersistor 2018-07-13 17:22:59 -04:00
Pedro Pinto 2a549e551f SPARK-1156 2018-07-13 20:09:42 +00:00
Pedro Pinto c158188557 SPARK-1156 2018-07-13 19:40:32 +00:00
Jake Norbut 17614b663e Merge branch 'master' of bitbucket.org:bluetradesys/spark into pnl_nits2 2018-07-13 14:21:46 -05:00
Pedro Pinto 5e36a22aba Merge 2018-07-13 13:19:01 -04:00
Pedro Pinto 442fbd2888 Updating the Web UI to support the SIM exchange. 2018-07-13 17:15:38 +00:00
Jake Norbut d1975cdd00 misc refactoring 2018-07-13 11:44:50 -05:00
Eric Harding a2c75f08d8 Fix up f# for new exchange. 2018-07-13 12:43:16 -04:00
Pedro Pinto 02a867f8a4 Towards sim exchange. 2018-07-13 16:32:08 +00:00
Jake Norbut adc48d7871 Cpps will be manually maintained; adding helper script to aid in their generation 2018-07-13 11:20:14 -05:00
Michael Harding 33bd089e74 SPARK-1370: Remove wildcards, confirmation modals, SMP 2018-07-12 16:27:49 -04:00
Jake Norbut edcb5a5a24 Changing default day start time to 4:30 as a result of new info from dev call 2018-07-12 13:29:21 -05:00
Jake Norbut 6f39705d4f PnL will now use cpp from config rather than security 2018-07-12 13:26:47 -05:00
Jake Norbut 07377b8cc9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into pnl_nits2 2018-07-12 12:07:08 -05:00
Jake Norbut 77ad9ca011 fixing no UTC to UTC time comparison bug; also, somehow swapped day vs start of day comparison 2018-07-12 12:00:46 -05:00
Pedro Pinto 10f24aa2e9 Towards SPARK-1156 2018-07-12 14:21:48 +00:00
Pedro Pinto 37fc5880ea Ran clang-format. 2018-07-12 13:34:28 +00:00
Eric Harding 910d991d92 SPARK-1448 Commit/cancel 'add to favorites' on focus move 2018-07-11 18:20:41 -04:00
Eric Harding ced8bd9fea CMakeDeps wants to update. ok. 2018-07-11 18:18:04 -04:00
Jake Norbut c5004f46ee SPARK-1458 cpp config proto and defaults 2018-07-11 17:15:34 -05:00
Eric Harding 1deb5d25b0 SPARK-1376 - My Windows : Cancel on escape 2018-07-11 16:45:31 -04:00
Jake Norbut b5950ab8c0 actually save the start of day rather than overwriting it anew each time 2018-07-11 13:04:34 -05:00
Jake Norbut a233451333 add start of day to pnl settings 2018-07-11 12:55:15 -05:00
Pedro Pinto deef8136df Adding script to synch up sdk repo 2018-07-11 12:56:16 -04:00
Pedro Pinto 9dda440a48 Cleaned up sdk. 2018-07-11 16:26:26 +00:00
Eric Harding f22442e665 fix build 2018-07-11 12:23:06 -04:00
Michael Harding 1ba7ceaf6a SparkAdmin: Context menus on menu items 2018-07-11 11:58:02 -04:00
Jake Norbut 54676d5aa9 Adding pnl_calc_type to state proro; tidying up VM 2018-07-11 09:12:06 -05:00
Jake Norbut edf5c78405 part of SPARK-1454; column filters and sort criteria 2018-07-10 16:56:23 -05:00
Eric Harding f621d08f72 SPARK-1437 Simple market data producer sample 2018-07-10 17:41:52 -04:00
Eric Harding 724243c762 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-07-10 17:16:07 -04:00
Eric Harding f70e8de497 Update git_switch_linux to purge before checkout 2018-07-10 17:15:08 -04:00
Pedro Pinto c97e4be79e Added libs to sdk 2018-07-10 19:02:22 +00:00
Taha Afzal 95be0015d6 fixing build 2018-07-10 14:30:11 -04:00
Pedro Pinto cbe8917f28 Fixed unit tests. 2018-07-10 16:02:14 +00:00
Pedro Pinto 653b1c7814 clang build fixes. 2018-07-10 15:37:16 +00:00
Michael Harding 9a1098669e SparkAdmin: SPARK-1447 - Limits Changes 2018-07-10 10:54:40 -04:00
Michael Harding dcf6c69ef9 SparkAdmin: Fix Lazy Nav 2018-07-10 10:41:49 -04:00
Michael Harding fdcd245ef5 SparkAdmin: Reorganize limits code, add dropdown menu control. 2018-07-10 10:32:55 -04:00
Taha Afzal cf9133da8d SPARK-1404 Trigger Order proto's polished 2018-07-10 10:29:34 -04:00
Michael Harding 802501a337 Merged SparkAdmin into master 2018-07-10 09:13:22 -04:00
Michael Harding b9c6f8d309 Merged master into SparkAdmin 2018-07-10 09:13:00 -04:00
Michael Harding ee94f43628 fix build 2018-07-10 09:11:58 -04:00
Eric Harding ed06924df1 Merged in SPARK-1376 (pull request #42)
SPARK-1176 Named Window

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-07-09 21:45:19 +00:00
Eric Harding b78179ccee SPARK-1376 Tweak feature name, allow remove from context menu. 2018-07-09 17:44:28 -04:00
Michael Harding e0add4cc47 Merged in SparkAdmin (pull request #43)
SparkAdmin Style and Layout

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-07-09 21:04:06 +00:00
Pedro Pinto cd670c4bd1 Fixed subrepo pointers, yet again 2018-07-09 20:45:44 +00:00
Michael Harding acd637000c Spark Admin: Actually using lazy nav 2018-07-09 16:45:19 -04:00
Michael Harding c09f03a922 SparkAdmin: Lazy nav 2018-07-09 16:41:34 -04:00
Jake Norbut f9fbf9c45a SPARK-1435 2018-07-09 15:40:58 -05:00
Eric Harding 5a00c5eb2a SPARK-1376 Save closed named windows with workspace 2018-07-09 16:17:48 -04:00
Eric Harding 74fa41f151 Quick cleanup of methods that show up on 'object's intellisense 2018-07-09 16:17:48 -04:00
Eric Harding 32d5fc2c37 SPARK-1376 Bookmarks launcher module 2018-07-09 16:17:48 -04:00
Eric Harding 1049046d0b SPARK-1176 Named Windows 2018-07-09 16:17:48 -04:00
Eric Harding 1f8e0a1e2b SPARK-1446 Try to add securities to security DB even if they already have an assigned security key 2018-07-09 16:09:35 -04:00
Michael Harding 846f6d80d8 Merged master into SparkAdmin 2018-07-09 12:32:13 -04:00
Pedro Pinto 9819a01e38 Tweaked benchmark code 2018-07-09 14:12:05 +00:00
Taha Afzal d0ad69e259 SPARK-1439 profile hedger 2018-07-08 18:53:48 -04:00
Taha Afzal f0abd13c48 merging from prod 2018-07-08 00:12:05 -04:00
Taha Afzal ea403b7138 cmake 2018-07-07 23:24:23 -04:00
Pedro Pinto 4a94beaa83 Added release_notes.txt 2018-07-06 21:42:37 +00:00
Pedro Pinto 3f03ceb2b5 SPARK-1438, Added location, smp and smp instructuction tags to order. 2018-07-06 21:40:05 +00:00
Pedro Pinto 0dd988fd1e Added to SDK MD sample. 2018-07-06 19:42:40 +00:00
Pedro Pinto 93d0a21c9d Added some instrumentation for timming hedge orders 2018-07-06 16:41:30 +00:00
Pedro Pinto 92b3cd1da0 SPARK-1384, Extension SDK is now part of the build. 2018-07-06 12:29:48 -04:00
Pedro Pinto 8fc42f6193 Re-architecting build. 2018-07-06 12:28:22 -04:00
Pedro Pinto 73d5b44a7f Added generated files to SDK. 2018-07-06 16:25:37 +00:00
Pedro Pinto e47c4ead2a Fixed the damn build 2018-07-06 16:25:37 +00:00
Pedro Pinto 21dfe5ce43 Updated docker subrepo ptr 2018-07-06 16:25:37 +00:00
Pedro Pinto e032dc822f Updated CMakeDeps.cmake 2018-07-06 16:25:37 +00:00
Pedro Pinto 87148b41f0 Moved docker/linux subrepo ptr 2018-07-06 16:25:37 +00:00
Pedro Pinto 68cb21a0a9 Cleaned up logging during unit testing. Somehow this also fixed unit
test errors.
2018-07-06 16:25:37 +00:00
Eric Harding 093ce9a0c3 Update runsparkd and Cmakedeps for sdk re-org 2018-07-06 16:25:37 +00:00
Pete 5003b23f18 moved docker subrepo pointer 2018-07-06 16:25:37 +00:00
Pedro Pinto 0de0ddbf16 SPARK-1384, Extension SDK is now part of the build. 2018-07-06 16:25:37 +00:00
Pedro Pinto 5d79c0c79a Fixed clang-tidy warnings. 2018-07-06 16:25:37 +00:00
Pedro Pinto 8e4d4782ce Re-architecting build. 2018-07-06 16:25:37 +00:00
Pedro Pinto b8bd3944ce Made Exchange simulator better at handling 0 quantity orders. 2018-07-06 16:25:37 +00:00
Kevin Darby 5d3d5251c7 added git_switch_linux.cmd to switch branches on sparkvm so we don't have to unison linux 2018-07-05 15:07:30 -04:00
Taha Afzal f7cee30511 test for n legs 2018-07-05 10:00:05 -04:00
Taha Afzal 98690ee747 ractional hedging test 2018-07-05 00:04:25 -04:00
Taha Afzal 3e14d83dde Merge branch 'prod' of bitbucket.org:bluetradesys/spark into nlegs 2018-07-03 18:54:56 -04:00
Jake Norbut 1fc38234e5 SPARK-1434 - underscore separator for SenderLocationID in audit logs 2018-07-03 09:50:44 -05:00
Taha Afzal 746622161c SPARK-1366 unit tests 2018-07-02 20:33:34 -04:00
Taha Afzal c064cdd215 SPARK-1366 code complete, writing tests now 2018-07-02 18:59:51 -04:00
Michael Harding 625d47d98e SparkAdmin: Data tables and move buttons again 2018-07-02 11:47:02 -04:00
Eric Harding acce1e3f7d SPARK-1376 Need to explicitly ask for direct book maintenance 2018-07-02 11:30:50 -04:00
Taha Afzal 9bdf084838 SPARK-1366 ciruclar buffer for keeping history of drivers complete 2018-07-01 23:40:57 -04:00
Taha Afzal 5132f81bc1 name change to QuoteLegDriver 2018-07-01 20:17:41 -04:00
Taha Afzal ab797a311e QuoterLegDriverData broken into a cpp 2018-07-01 19:48:51 -04:00
Taha Afzal d032b23841 added 'compute_driver' which calculates the driver price/qty from a quoting leg qty - this is now called by both n legs and 2 legs 2018-07-01 18:33:04 -04:00
Taha Afzal 8c68a66ef1 Merge branch 'prod' of bitbucket.org:bluetradesys/spark into nlegs 2018-06-29 19:14:56 -04:00
Taha Afzal d617d1597c SPARK 1432 fractional legs 2018-06-29 18:58:44 -04:00
Michael Harding 8c8f2d3179 SparkAdmin: Update packages 2018-06-29 16:56:48 -04:00
Michael Harding 669ccd5ffc SPARK-1428, SPARK-1430 2018-06-29 10:50:32 -04:00
Michael Harding 12db326f1d SPARK-1429: Remove index pages 2018-06-28 15:52:15 -04:00
Pedro Pinto 625f76e1f4 Made iceberg order type more robust.
We were asserting that completed orders must have non zero fills, but
its easy for an algo to break that so we handle it.
2018-06-28 15:14:52 -04:00
Michael Harding 55fc880b2e SPARK-1421: Accounts and Connections are now selected with checkboxes 2018-06-28 13:56:03 -04:00
Eric Harding 30ca30442a Fix merge error 2018-06-28 11:34:16 -04:00
Eric Harding 802160abf1 merge from prod 2018-06-28 11:28:08 -04:00
Eric Harding ccff3ee160 SPARK-1427 monitor dispatcher background thread on sparkd 2018-06-28 11:24:35 -04:00
Eric Harding 3da1bfaeb1 Metric for latency between spark shell and server 2018-06-28 11:07:45 -04:00
Eric Harding 03eff1f388 Api generated code namespace ambiguity fix. 2018-06-27 16:49:55 -04:00
Michael Harding b53825baa9 SparkAdmin: Preliminary Style and Layout 2018-06-27 12:44:57 -04:00
Eric Harding 1b1c8a7c1d Allow for discontiguous keys in replay md 2018-06-27 11:25:46 -04:00
Pedro Pinto 5206a378ff Fixed stop order price encoding 2018-06-27 11:12:34 -04:00
Pedro Pinto 7d2047d7ce Fixed the shared data problem that was crashing O'Grady. 2018-06-27 11:12:34 -04:00
Taha Afzal 8a43a4d157 SPARK-1416 (reactivated terminal status) 2018-06-27 11:12:34 -04:00
Jake Norbut 230e9e525f SPARK-1409 - slow picker fixed? 2018-06-27 11:12:34 -04:00
Jake Norbut 034318db5e fix early orders unsub - exception was taking out the observable 2018-06-27 11:12:34 -04:00
Pedro Pinto 0758dd6f46 Fixed clang build. 2018-06-27 11:12:34 -04:00
Pedro Pinto 83f9f3375c Fixed crash on client close. 2018-06-27 11:12:33 -04:00
Pedro Pinto 98a6616804 Removed current link in jetfix, it doesn't work well with docker. 2018-06-27 11:12:33 -04:00
Pedro Pinto b46f811cb5 Tweaks to pp custom build environment. 2018-06-27 11:12:33 -04:00
Eric Harding 0065dba0f0 Update commented out logging for dumping all symbols on a market data channel 2018-06-26 17:16:20 -04:00
Eric Harding 078b9d41b1 Allow for discontiguous keys in replay md 2018-06-26 16:37:30 -04:00
Eric Harding 7cf37bb080 SPARK-1407: Config off consolidated book. CMEChannel { book_type: BOOKTYPE_DIRECT } 2018-06-26 12:56:07 -04:00
Pedro Pinto 564cd3e273 Made cme_market_segment_id mandatory in config to avoid mistakes.
Its too easy to forget this value and end up routing orders
incorrectly. Unfortunately mandatory fields are not yet enforced in
config but we have a case to do it in a next cycle.
2018-06-26 15:36:53 +00:00
John Colvin 6d918c0962 SPARK-1418 Remove constraint on creating NLegs spreads 2018-06-25 17:10:04 -04:00
Pedro Pinto 709229ac14 SPARK-1379, Simulator was inverting queue order for bids. 2018-06-25 18:55:08 +00:00
Eric Harding 375786b283 Tweak test to take const& to avoid stepping though constructor 2018-06-25 13:51:12 -04:00
Pete 9e5970c93e checking for Jake. fixes audit log 39= issue 2018-06-25 10:14:48 -04:00
Pedro Pinto ab5fca73f6 Merge remote-tracking branch 'origin/prod' 2018-06-24 20:41:23 -04:00
Eric Harding 840115db03 SPARK-1359 Config key to turn off running market data 2018-06-22 17:31:08 -04:00
Pedro Pinto db7f74f85d Changed permissions in script 2018-06-22 17:20:23 -04:00
Jake Norbut 3f381f248b Merged in pnl_bg_highlight (pull request #41)
SPARK-1167 - colors on PnL

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-06-22 21:05:33 +00:00
Jake Norbut b5a93e4466 PR suggestions 2018-06-22 15:59:18 -05:00
Pedro Pinto 7cd052979e Fixed stop order price encoding 2018-06-22 16:29:45 -04:00
Pedro Pinto 3b3d44d6f6 Fixed the shared data problem that was crashing O'Grady. 2018-06-22 16:22:27 -04:00
Pedro Pinto 1b56ff3327 Fixed stop order price encoding 2018-06-22 16:19:55 -04:00
Pedro Pinto 1f823003d7 Fixed the shared data problem that was crashing O'Grady. 2018-06-22 19:49:45 +00:00
Jake Norbut a64749cd07 SPARK-1167 - colors on PnL 2018-06-22 14:47:03 -05:00
Jake Norbut 3ac6e882d4 SPARK-1417 - script will parse CME trading session list to create a csv of market segment gateways to symbols 2018-06-22 11:15:03 -05:00
Michael Harding 4c239f31f8 SparkAdmin: changes to ui.fsproj 2018-06-22 10:51:08 -04:00
Eric Harding f9759b9533 Merged in SurpriseUnsubscribes (pull request #40)
SPARK-1414 Exception thrown in api subscribe context is handled silently

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-06-22 14:21:43 +00:00
Taha Afzal 2b2eaac979 SPARK-1416 (reactivated terminal status) 2018-06-21 19:50:32 -04:00
Taha Afzal 5b5274ab5f 1366 added hedging test 2018-06-21 18:02:08 -04:00
Eric Harding 56fc1019de SPARK-1414 Exception thrown in api subscribe context is handled silently 2018-06-21 16:56:38 -04:00
Pedro Pinto abe7c25bf4 SPARK-1137, Incorporated onix time in measurements. 2018-06-21 18:06:51 +00:00
Jake Norbut 484ad656f9 SPARK-1409 - slow picker fixed? 2018-06-21 11:42:16 -05:00
Jake Norbut 0e92ee15f4 fix early orders unsub - exception was taking out the observable 2018-06-21 11:29:27 -05:00
Jake Norbut 4dcaf224ef SPARK-1409 - slow picker fixed? 2018-06-21 09:43:08 -05:00
Taha Afzal 2d5211ac9b SPARK-1366 n legs 2018-06-20 21:13:02 -04:00
Taha Afzal e62d116a44 SPARK-1366 n legs 2018-06-20 20:42:36 -04:00
Kevin Darby 8c492fdad3 fixed SDK 2018-06-20 17:29:21 -04:00
Pedro Pinto 0deb4fbcf4 Fixed clang build. 2018-06-20 20:00:13 +00:00
Pedro Pinto b8b375eca5 Fixed crash on client close. 2018-06-20 20:00:13 +00:00
Pedro Pinto 4a42238cfb Removed current link in jetfix, it doesn't work well with docker. 2018-06-20 20:00:13 +00:00
Pedro Pinto d4081420ae Tweaks to pp custom build environment. 2018-06-20 20:00:13 +00:00
Michael Harding b811fb6a32 SPARK-1390: Add Mkdocs to the dockerfile 2018-06-20 15:32:10 -04:00
Taha Afzal 27d38cf443 SPARK-1400 spreader on_activate_recovered 2018-06-20 14:22:11 -04:00
Taha Afzal 4dad98a8b2 SPARK-1408 Limiter dupe value crash 2018-06-20 14:22:11 -04:00
Michael Harding 0f78cbc80e SPARK-1214: Add SMP and location 2018-06-20 13:27:41 -04:00
Eric Harding eb3067448d Change default symbol list to match 'DOG' 2018-06-20 12:50:41 -04:00
Eric Harding f4a3256cb8 Update OnixS 4.3.0 to 4.5.2 2018-06-20 12:05:32 -04:00
Eric Harding a2b8d53ebb Add $HOST variable to install_config.json to make installer deploy easier 2018-06-20 11:22:00 -04:00
Jake Norbut b7a3d5a23c cme audit - documenting that script should run in a Chicago time zone 2018-06-20 08:55:47 -05:00
Eric Harding 4f4bfea70f remove obsolete config key 2018-06-19 17:31:32 -04:00
Eric Harding 76d6e37f59 Change default market data recording path to something reasonable instead of /home/bts 2018-06-19 17:06:56 -04:00
Jake Norbut 8e535b0303 SPARK-1391 - audit script now requires output_file specified in command line; by default, will no longer print to console but this can be toggled on via -c 2018-06-19 13:11:56 -05:00
Eric Harding 44887d80e6 sparkd web help indentation 2018-06-19 11:47:26 -04:00
Eric Harding bd63c0798e Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-06-19 11:46:43 -04:00
Eric Harding 6e06b3566a Add logging on synthetic security update 2018-06-19 11:43:03 -04:00
Eric Harding ebc7432995 Add market data gap metric. 2018-06-19 11:43:03 -04:00
Eric Harding 33a1762cb7 Add sparkd web command for getting the web UI up in an emergency 2018-06-19 11:40:03 -04:00
Taha Afzal 16e9d100d2 n legged tests 2018-06-19 09:45:19 -04:00
Jake Norbut 5009c4c29a SPARK-1388 - round OB prices to 9 fractional digits when the security is not known 2018-06-19 08:29:20 -05:00
Pedro Pinto f4f1bb7914 Fixed log msg. 2018-06-18 20:56:07 +00:00
Pedro Pinto 82d3e59024 Removed unused config values. 2018-06-18 20:44:25 +00:00
Pedro Pinto 6fe59386df SPARK-1137, We have server performance numbers and they are good. 2018-06-18 20:44:25 +00:00
Pedro Pinto 767cb36856 Ran clang-format. 2018-06-18 20:44:25 +00:00
John Colvin 908958ea3c SPARK-1386 format futures/future-spread-legs with exchange desc (if present) 2018-06-18 15:46:32 -04:00
Kevin Darby 8cef60b5ab top test case, might not have pushed in time 2018-06-18 10:20:44 -04:00
Taha Afzal bbf961ed78 fixing build, .fs file 2018-06-18 08:40:20 -04:00
Taha Afzal f3b8ce8088 adding location to CME User Data 2018-06-17 22:44:37 -04:00
Pedro Pinto 026bda165e Added more data to server side performance tracing. 2018-06-17 15:22:56 +00:00
Pedro Pinto 9727f17edf Tweaks to pp custom build environment. 2018-06-17 15:21:47 +00:00
Pedro Pinto 61b2913a31 Fixed clang build. 2018-06-17 15:21:04 +00:00
Pedro Pinto 0a8bee2c31 Added a couple more digits to exchange price encoding. 2018-06-15 09:51:07 -04:00
Pedro Pinto 2dd8e64272 Perf analysis tools. 2018-06-15 09:51:07 -04:00
John Colvin aa704b8c96 timefilter - make text input filter more flexible 2018-06-14 20:56:19 -04:00
John Colvin 8673b857a7 timefilter - triple-click selects all time-text 2018-06-14 19:56:09 -04:00
John Colvin f83f2c7964 SPARK-1348 time-based period filter requires textbox to lose focus to accept new value 2018-06-14 19:13:18 -04:00
Eric Harding 86d8d7dafb Add seconds_to_recenter and max_open_ladders to UI.conf. Remove seconds and ticks from LadderSettings as it should not be stored per-ladder. 2018-06-14 18:35:15 -04:00
Eric Harding 5363fdd0db Reduce logging level for non-errors in PersistenceServiceServer 2018-06-14 18:08:52 -04:00
Eric Harding d3e7870b9a Fix build with the #ifdef stick 2018-06-14 18:08:32 -04:00
Eric Harding f05692e441 Merged Metrics into qa 2018-06-14 17:38:58 -04:00
Eric Harding 053c460ff6 Market data rate metrics reborn 2018-06-14 17:13:19 -04:00
Eric Harding af7e9dd3a8 Raise WebServer GET request logging to DIAG level 2018-06-14 17:11:57 -04:00
Eric Harding 3e3b6acfd7 Market data QueryServer response time/size. 2018-06-14 17:11:57 -04:00
Eric Harding b75496e665 Add sparkd memory use metric 2018-06-14 17:11:57 -04:00
Eric Harding 03864caa09 Add HistogramMetric type 2018-06-14 17:11:57 -04:00
Eric Harding 1558b79c16 Sparkd promethius metrics 2018-06-14 17:11:57 -04:00
Kevin Darby 57bfe5a85e added manual test cases for algos 2018-06-14 14:50:20 -04:00
John Colvin 3eaeaf0f35 fix LadderSettings equals method 2018-06-14 12:50:02 -04:00
Michael Harding ab3121cd5d Spark Admin: Admin user hack 2018-06-14 11:43:01 -04:00
Taha Afzal ec5bd4a164 SPARK-1332 2018-06-14 11:22:11 -04:00
John Colvin 3877aec09a SPARK-1354 Can't change bid, ask, or last colors in ladder 2018-06-14 10:37:29 -04:00
Jake Norbut fb17596304 updating test text 2018-06-14 08:32:48 -05:00
Michael Harding b36bcc66fb Update PnL manual test 2018-06-14 09:26:53 -04:00
Taha Afzal 73189fbbd8 2 admin limit tests changed slightly 2018-06-13 19:18:04 -04:00
John Colvin 484a9dba12 orderticket priceentrycontrol - update binding source in tickup/down command if has keyboard focus 2018-06-13 17:27:53 -04:00
John Colvin 83f2866604 SPARK-1349 Order Ticket Price Field - DoubleClick 2018-06-13 17:27:53 -04:00
Pedro Pinto 37f40046da Fixed typo in CTI code enum. 2018-06-13 15:48:17 -04:00
Pedro Pinto 2401b9db77 Typo in log msg. 2018-06-13 15:48:17 -04:00
John Colvin a45546690c SPARK-1352 ladder - auto-clear should only reset size to 0 2018-06-13 15:41:20 -04:00
John Colvin bc394ee3b5 SPARK-1350 Fill Window Comes Up Blank From Orders Window 2018-06-13 15:30:55 -04:00
Michael Harding b917fba19a Spark Admin: Assign correct default symbol to new productLimits rows 2018-06-13 14:58:54 -04:00
Michael Harding 63106eb866 SPARK-1245: Wrap Clip Limits help onto two lines 2018-06-13 14:28:27 -04:00
Eric Harding fa79bddb6b Make Spark webserver pipe down a bit 2018-06-13 13:49:34 -04:00
Pedro Pinto 83851c1494 QA assignments. 2018-06-13 10:35:02 -04:00
John Colvin 373f7ee3a1 SPARK-1208 synthetic modification disabled when it should not be 2018-06-12 23:50:56 -04:00
Taha Afzal 2ed8b9caf0 changes to the prehedge tests 2018-06-12 23:16:04 -04:00
John Colvin 470eee431e SPARK-1248 OrderBook - 'last message' cells are always empty 2018-06-12 23:08:00 -04:00
John Colvin e5e6723b3a orderbook - LiveSortingProperties were one step behind SortDescriptions 2018-06-12 22:54:42 -04:00
John Colvin 71be28911b SPARK-1243 crash on server bounce 2018-06-12 19:54:51 -04:00
Michael Harding 8ab3182c21 SPARK-1241: Username.ToLower 2018-06-12 17:33:20 -04:00
Pedro Pinto 508e2d4fcf SPARK-1234, Make sure rejected orders have valid price, quantity 2018-06-12 17:04:29 -04:00
Pedro Pinto 027e434ac9 SPARK-1236, Limit the number of exchange connection attempts. 2018-06-12 16:57:22 -04:00
Michael Harding e9be6d9859 SPARK-1245: Help text 2018-06-12 16:53:25 -04:00
Pedro Pinto 82e63f691c SPARK-1242, Added messaging governor to execution. 2018-06-12 16:01:35 -04:00
Pedro Pinto cb83ad08a6 Perf analysis tools. 2018-06-12 14:14:42 -04:00
Michael Harding e2e78d91c3 SPARK-1240, SPARK-1239: Hide Synthetic, Remove duplicate symbols 2018-06-12 13:21:28 -04:00
Michael Harding 427d03d340 SparkAdmin: Remove onclick from login 2018-06-12 11:29:53 -04:00
John Colvin 32e5111c54 orderbook - remove unused resources in OrderBookView.xaml 2018-06-11 15:11:53 -04:00
John Colvin ef12f81b73 orderbook - fix early-child sorting bug 2018-06-11 15:11:53 -04:00
Eric Harding c0f255f5a7 Spark Admin: Reload page if websocket is disconnected 2018-06-11 14:29:36 -04:00
John Colvin f6861820e4 orderbook - add apply/clear buttons to SubType filter popup 2018-06-11 12:19:26 -04:00
John Colvin 6a3960b33f FillTuple has SecurityMD field (instead of just Security) 2018-06-11 11:44:37 -04:00
Eric Harding e3858fd203 Merged in SPARK-1231 (pull request #39)
Persist should not throw exceptions. Nobody expects that

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-06-11 15:29:28 +00:00
Eric Harding 3f1d9307e9 Persist should not throw exceptions. Nobody expects that 2018-06-11 11:22:21 -04:00
Jake Norbut 5f7c2330f3 Ladder recenter on middle-click is now restricted to price column; this previously interferred with a badge's context menu from opening when the ladder was not centered 2018-06-11 09:56:48 -05:00
Jake Norbut 315b2a8b26 SPARK-1229 - EnumCharacterCasingConverter now checks for null 2018-06-11 09:55:29 -05:00
Taha Afzal 29ebfebcbc synthtic orders ressurected from disk show the correct filled price/qty 2018-06-10 23:32:02 -04:00
John Colvin 4fbaca6d05 orderbook - fix child-order filter bug 2018-06-10 17:15:25 -04:00
John Colvin 4d4f706e7c OrderBook - fix sorting bug (must recreate cell in addition to invoking PropertyChanged) 2018-06-10 16:26:46 -04:00
John Colvin 241396e292 clean up FillTupleSource/ObservableDemuxer 2018-06-10 15:59:52 -04:00
John Colvin 4e68c98cfb fix FillTupleSource 2018-06-08 23:36:06 -04:00
Eric Harding 30c1dfd9a7 Merged in SPARK-1123 (pull request #38)
SPARK-1123 on_orders.Replay().RefCount() does not handle late join correctly

Approved-by: Kevin Darby <kevin.darby@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-06-09 01:37:48 +00:00
Pedro Pinto a64ca702c9 More tracing tools. 2018-06-08 18:24:07 -04:00
Taha Afzal 0a2b6ce17e SPARK-1212, delayed spreader order cancelling when no active achild orders 2018-06-08 18:21:29 -04:00
Pedro Pinto 660f203132 SPARK-1137, Server side tracing script. 2018-06-08 17:47:23 -04:00
Michael Harding 27f27dbcf1 AdminUI -> Spark Admin 2018-06-08 17:05:54 -04:00
Pedro Pinto 1ac6c8cd40 SPARK-1216, Formatted order prices properly. 2018-06-08 16:33:11 -04:00
Pedro Pinto a13cececa1 Formatted code a bit better. 2018-06-08 16:33:11 -04:00
Pedro Pinto 01d2e88097 Made logging more robust in the presence of config errors. 2018-06-08 16:33:11 -04:00
John Colvin 8f96cd07e3 FillTupleSource - actually update the state with incremental changes 2018-06-08 14:31:11 -04:00
Eric Harding b515e45b23 SPARK-1123 on_orders.Replay().RefCount() does not handle late join correctly 2018-06-08 13:49:22 -04:00
Eric Harding a37e2ead87 Hide status change messages in ladder 2018-06-08 13:44:34 -04:00
Kevin Darby 92ae592d31 Fixed a couple of bugs involving comparing doubles in the ladder and some ladder start crashes. 2018-06-08 13:10:50 -04:00
John Colvin db1cbf589f remove 'Latency' from Logger Subsystems in cs 2018-06-08 10:53:17 -04:00
Eric Harding a561ff3aae Fix merge problem with ObserveOn PR 2018-06-08 10:39:39 -04:00
Eric Harding 3eeab708d2 Merged in ObserveOn (pull request #37)
ObserveOnDispatcher -> ObserveOn + Lower priority so as not to block input

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-06-08 14:37:20 +00:00
Eric Harding 082056ea3f Fix crash in ErrorReporter because api thread is not STA 2018-06-08 09:53:32 -04:00
Eric Harding e9f3e92f82 Read spark.version for version information 2018-06-08 09:26:21 -04:00
Jake Norbut 152c79a4a9 Badge should never fill in with completion progress as it previously did 2018-06-08 08:05:35 -05:00
Eric Harding db877c859b ObserveOnDispatcher -> ObserveOn + Lower priority so as not to block input 2018-06-08 08:42:23 -04:00
John Colvin 1ec2423ad2 SPARK-1215 fills performance - do not copy dictionaries in FillTupleSource 2018-06-07 21:17:58 -04:00
Taha Afzal 1a88e52a08 subtle change in the QuoterWaitingForAck where book changes on the hedging side make it dissapear, hence order needs to be pulled 2018-06-07 20:06:40 -04:00
Taha Afzal a1b52d083d SPARK-1200 2018-06-07 17:50:09 -04:00
Michael Harding f364317cfa Better window border compromise 2018-06-07 17:10:16 -04:00
Michael Harding f474af1925 Update a few manual tests 2018-06-07 16:09:53 -04:00
Michael Harding 03ac5035fc AdminUI: Minor tweaks and fixes 2018-06-07 16:09:53 -04:00
Michael Harding e78d8208fa AdminUI: Add application name to login page 2018-06-07 16:09:53 -04:00
Pedro Pinto 7f8c591f75 Instrumenting server for perf measurements. 2018-06-07 13:58:02 -04:00
Taha Afzal 7d9685f995 SPARK-1211 cor id 2018-06-07 13:11:27 -04:00
John Colvin 699736ff52 Merged in badge-style-row-height-unification (pull request #35)
SPARK-1204 Ladder/Badge - row height needs to fit different badge styles

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-06-07 16:36:03 +00:00
John Colvin 8d0e7bc467 pr finishing touch 2018-06-07 12:35:03 -04:00
Michael Harding df784e9961 SPARK-1201: Fix and style table headers 2018-06-07 10:21:14 -04:00
Michael Harding af39aab153 AdminUI: Fix default security symbol for new rows 2018-06-07 10:21:13 -04:00
Taha Afzal 885c1ad9f8 SPARK-1152 remove limit in release.... 2018-06-06 23:26:07 -04:00
Eric Harding 91fdd6ec58 Improve generated structs by adding C# 7.2 readonly keyword 2018-06-06 20:21:20 -04:00
John Colvin 3adfd7056a Merge branch 'master' into badge-style-row-height-unification 2018-06-06 18:48:18 -04:00
Kevin Darby 5f845f1355 python 2.7 vs 3.6 blues 2018-06-06 17:56:53 -04:00
Pedro Pinto 5d4bb1d421 More server side perf instrumentation. 2018-06-06 17:53:18 -04:00
John Colvin 9836e46dd9 SPARK-1092 Inconsitencies in changing spreader params / probably lack of persistence 2018-06-06 17:21:42 -04:00
John Colvin bf9d5c2c6d ExecutionStrategyEditor - fix vertical grid line adorner 2018-06-06 17:21:42 -04:00
Taha Afzal ef1c3a7959 SPARK-1159 admin tool strange state 2018-06-06 16:59:35 -04:00
Taha Afzal b07f6aba59 comments, code style 2018-06-06 16:59:35 -04:00
Eric Harding a7b3b0fa6d SPARK-1197 MarketSegmentID 2018-06-06 16:55:03 -04:00
Jake Norbut f7a3b3ebc1 Fixing badge height overlapping level line 2018-06-06 15:49:46 -05:00
Michael Harding d291e4a022 SPARK-1201: Buttons, Transforms, and Stars 2018-06-06 16:13:51 -04:00
Pedro Pinto 292e5575d0 Made sim not look for secure logon file. 2018-06-06 16:06:43 -04:00
Pedro Pinto 1ad57dbf45 Instrumenting server side for performance tracking. 2018-06-06 16:06:43 -04:00
Kevin Darby b25f5556af added python27 to front of the path in vscode.cmd 2018-06-06 15:54:37 -04:00
Kevin Darby c10a4be411 fixing crash on spreader; the VMs get created no matter what security type, they're filtered later 2018-06-06 15:49:40 -04:00
Eric Harding de312c3023 Update c# language version to 7.2 2018-06-06 15:45:48 -04:00
Jake Norbut 93ff56c5cd Merged in badge_perf (pull request #36)
Badge perf

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-06-06 19:04:17 +00:00
Jake Norbut 5254b7367a PR suggestions; changing summary and description to backings as well since the tooltip was not changing as the lovm updated 2018-06-06 13:52:50 -05:00
Kevin Darby 6d2efb7dc9 clarified min_initial_tick_offset 2018-06-06 13:34:28 -04:00
Jake Norbut 50214741e7 PR - backings for a lazy life 2018-06-06 12:31:22 -05:00
Pete 56950b682d fat fingered 2018-06-06 13:00:45 -04:00
Jake Norbut 83b1bbf087 PR suggestions 2018-06-06 11:44:58 -05:00
Pete 203998e201 change default secure CME login to true 2018-06-06 12:40:25 -04:00
Kevin Darby 59d88d3cc1 colors per Michael 2018-06-06 12:32:13 -04:00
Kevin Darby 13f9717195 Merged in algos (pull request #30)
Algos
2018-06-06 16:15:06 +00:00
Kevin Darby 855e0d7ef9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos 2018-06-06 11:46:36 -04:00
Michael Harding 27a1cb277c SPARK-1201: Remove Connections table borders; Add colSpan to table cells 2018-06-06 11:18:59 -04:00
Michael Harding 3107d5f382 SPARK-1201: Add modal create dialog for Users and Accounts 2018-06-06 11:18:59 -04:00
Michael Harding e1940faeac AdminUI: Add Modal Dialog 2018-06-06 11:18:59 -04:00
Jake Norbut a2f22482f6 removing perf testing code; removing unused orders_cache 2018-06-06 09:36:21 -05:00
Taha Afzal ccea4571a6 SPARK-1206 uninitialized limit returned 2018-06-06 10:24:19 -04:00
Taha Afzal 3a72a4a82e Spreader's bookkeepingdata_ptr is a unique_ptr and some stylistic changes to shorten line widths 2018-06-06 10:11:39 -04:00
John Colvin ed6affb6b5 SPARK-1196 synth dialog - security text in column header is wrong (part 2) 2018-06-05 21:17:16 -04:00
John Colvin 2fc208ef74 SPARK-1205 Orderbook filter bug 2018-06-05 19:57:16 -04:00
Jake Norbut 64f86db86b misc cleanup 2018-06-05 18:07:05 -05:00
Jake Norbut 5e960479c7 slow badges no more 2018-06-05 18:01:04 -05:00
John Colvin 837aa51adf SPARK-1204 pr edits 2018-06-05 18:02:32 -04:00
Pedro Pinto d7a7b480ad Persist exchange book in sim even if merge_md is false. 2018-06-05 17:36:39 -04:00
Pedro Pinto d94afe9867 Tweaked performance analysis tools. 2018-06-05 16:59:54 -04:00
John Colvin 410e17aec5 SPARK-1204 Ladder/Badge - row height needs to fit different badge styles 2018-06-05 16:46:50 -04:00
Taha Afzal a8bd99af56 quoter's leg_prop_data is a ref into spreaderbookkeepingdata 2018-06-05 15:00:55 -04:00
John Colvin e11b5928ce SPARK-1181 touch up time filter dialog 2018-06-05 14:34:29 -04:00
Taha Afzal 6bcf352813 deleting cons's and =operators for SpreaderOrder 2018-06-05 14:10:07 -04:00
Michael Harding eeec7b0076 SPARK-1146: Settings Button in the ladder looks consistent 2018-06-05 13:33:18 -04:00
Michael Harding bacdb1c4cd SPARK-1201: Trading and admin permission columns for Users/Accounts Tab 2018-06-05 10:22:25 -04:00
Michael Harding 1e4ad28088 SPARK-1201: Security column in Secutity Limits 2018-06-05 10:03:32 -04:00
Michael Harding e013f93169 SPARK-1201: Show empty add dropdown 2018-06-05 10:03:32 -04:00
Kevin Darby 002c051c06 make it more clear that detach does not necessarily imply cancellation of the parent 2018-06-01 16:58:18 -04:00
Kevin Darby 42fd7b3f40 added state mutators for base class Pimpl...lovin that c++ 2018-06-01 16:46:28 -04:00
Kevin Darby f73d163312 implemented most of Pedro's review, need to do the mutators still 2018-06-01 15:36:06 -04:00
Kevin Darby ee124401b9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos 2018-06-01 13:46:27 -04:00
Kevin Darby ec7417a9c6 added comments to algo order headers and changed wording of a parameter 2018-06-01 13:46:10 -04:00
Eric Harding 536f4703ab Add visual studio perf session files to gitignore 2018-06-01 13:46:10 -04:00
Michael Harding 5f848a36e2 fix ui.fsproj 2018-06-01 13:46:10 -04:00
Michael Harding a7c714591a Fix Webpack config for development 2018-06-01 13:46:10 -04:00
Michael Harding 4655a68593 SPARK-1155 2018-06-01 13:46:10 -04:00
Eric Harding 9677d8996e SPARK-1189 - Windows C++ build is not producing symbols file 2018-06-01 13:46:10 -04:00
John Colvin 3f0a9e442e change UpdateDemuxer to TableUpdateDemuxer, which updates incrementally 2018-06-01 13:46:10 -04:00
Eric Harding f3387b57af SPARK-1183 3-5 1000 lot iceberg orders can lag/lock up the UI 2018-06-01 13:46:09 -04:00
John Colvin abed4bbff6 CVOrderBook - fix typeo that trapped the root_node() fn in an endless loop 2018-06-01 13:46:09 -04:00
Eric Harding bb90429ada Fix problem where you can't see price moves when mouse is over a cell in the ladder 2018-06-01 13:46:09 -04:00
Kevin Darby 1efbc2fd45 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos 2018-05-31 16:37:00 -04:00
Kevin Darby 04027ed980 Merge branch 'master' of bitbucket.org:bluetradesys/spark into algos 2018-05-29 15:26:55 -04:00
Kevin Darby 5a850c314c Merge branch 'algos' of bitbucket.org:bluetradesys/spark into algos 2018-05-29 15:26:08 -04:00
Kevin Darby b566668d83 pushd, a better cd 2018-05-29 11:52:17 -04:00
Kevin Darby 6679695931 fixed cancel state in TopOrder 2018-05-26 17:01:04 -04:00
Kevin Darby 23a8112dd3 Merge branch 'algos' of bitbucket.org:bluetradesys/spark into algos 2018-05-26 15:13:22 -04:00
Kevin Darby b9a07a8e21 toporder, maybe 2018-05-26 15:13:13 -04:00
Kevin Darby 86deaf2edb Join order works and hedges 2018-05-26 15:13:13 -04:00
Kevin Darby fc347250e3 pushing for join to look at joinordervm validation 2018-05-26 15:13:13 -04:00
Kevin Darby aae867a555 doskey, alive and well 2018-05-26 15:13:13 -04:00
Kevin Darby 37a454cf51 behold, TickOrder 2018-05-26 15:13:13 -04:00
Kevin Darby 5162b9f0c0 toporder, maybe 2018-05-26 15:12:55 -04:00
Taha Afzal ad757495bd comments 2018-05-25 18:42:30 -04:00
Kevin Darby e473c18a30 Join order works and hedges 2018-05-25 18:33:30 -04:00
Taha Afzal 640ec0b1ed spreader tests 2018-05-25 18:06:19 -04:00
Jake Norbut c3e2622a22 Ladder - restoring badge context menu and hooking up crack 2018-05-25 18:06:19 -04:00
John Colvin 7d05f76fe4 DIYGrid HighPerformanceRow - InvalidateVisual when DataContext changes 2018-05-25 18:06:19 -04:00
Kevin Darby 7a8a38f89d pushing for join to look at joinordervm validation 2018-05-25 18:04:58 -04:00
Michael Harding 1e54cb1daf Order Type Colors 2018-05-25 18:04:57 -04:00
Eric Harding 7b714018d2 SPARK-1139 Make Combox faster when system is under load (no AllowsTransparency) 2018-05-25 18:04:57 -04:00
Taha Afzal c3371d0971 re-org'ing SpreaderUtils functions into cpp 2018-05-25 18:04:57 -04:00
Kevin Darby 322e3d81ba doskey, alive and well 2018-05-24 18:24:10 -04:00
Taha Afzal 4ada5a89f4 SPARK-1119 completed 2018-05-24 18:24:10 -04:00
Eric Harding 15aa016aa0 Reduced CPU for cert market data during development 2018-05-24 18:24:10 -04:00
Taha Afzal e77f3169de SPARK-1119 (deleting unused API functions) 2018-05-24 18:24:10 -04:00
Kevin Darby 660c5d3a06 behold, TickOrder 2018-05-24 16:22:19 -04:00
496 changed files with 23787 additions and 9134 deletions

No files matched your search

+2
View File
@@ -21,3 +21,5 @@ src/client/sparkcmd/Properties/launchSettings.json
node_modules
paket-files
project.lock.json
src/server/sparkd/orderman/perf_analysis/data
src/server/sparkd/orderman/perf_analysis/trace_data/
+1 -1
View File
@@ -37,7 +37,7 @@
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
"args": ["md","GLBX","Future","ES","ESM8"],
"args": ["md","GLBX","Future","ZN", "ZNU8"],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
+26 -6
View File
@@ -32,10 +32,30 @@ if (WIN32)
add_subdirectory(src/lib)
else ()
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \
-Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++14 -fdiagnostics-color")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG -O3")
add_subdirectory(src/lib)
add_subdirectory(src/server/sparkd)
add_subdirectory(src/web)
# so that sparkd looks for libspark.so in its directory
set (CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set (CMAKE_INSTALL_RPATH ".")
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \
-fPIC -Wall -pedantic -Wextra -Werror \
-Wno-missing-braces -Wno-ignored-qualifiers \
--std=c++14 -fdiagnostics-color")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG -O3")
add_subdirectory(src/server/sparkd)
add_subdirectory(src/lib)
add_subdirectory(src/web)
add_subdirectory(src/sdk)
add_subdirectory(src/data_files)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/server
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/sparkd ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/defaults ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/web ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
COMMENT "Creating server runtime layout"
DEPENDS sparkd data_files_dir)
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
endif()
+6
View File
@@ -0,0 +1,6 @@
1.1.0
- With this release we dropped wild cards from limits, so any user
that has limits expressed in terms of wild cards will have to have
those re-entered explicitly
- With this release we add per user location and smp id tags. At least
the location will have to be set through the admin UI for every user
+35
View File
@@ -0,0 +1,35 @@
1.1.22
- Orderbook: Orders in Error status can now be recreated
- Orderbook: Orders containing child orders will now be collapsed by default.
- Orderbook: Expand / Collapse All button added to the right-click menu and window action pane, denoted by ▲ icon
- Orderbook: Symbol column added to allow for filtering based on group symbol
- Time filters now have a quick Set Last Hour hyperlink which will set the Period Begin filter to (Now - 1 hour)
1.1.20
- Update CME market data handler for price precision extension.
https://www.cmegroup.com/confluence/display/EPICSANDBOX/Price+Precision+Extension
1.1.8
- Enabled wild cards in risk management UI
- Fixed spreader order updates
1.1.7
- Fixed spread leg price fill
- Fixed HO cash per point value
1.1.6
- Fixed occasional crash when filtering fills
- Fixed several cash per point multipliers
- Colored time of sales row with aggressor side
- Iceberg orders now cancel (rathern than error) when a child is canceled
1.1.0
- Added per user self match protection, location and self match protection FIX tag settings
- Implemented a new "Trigger Order" algo
- Corrected tick formatting for a number of products
- Added the hability to recreate canceled orders
- Added leg fills to facilitate futured based PnL
- PnL report now has PnL columns calculated based on your start of day, current day, and total position
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
- Corrected cash per point values, used in pnl calculations, for a number of products
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
+1 -1
View File
@@ -4,7 +4,7 @@ VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spark_lib", "build\src\lib\spark_lib.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
+1 -1
View File
@@ -5,7 +5,7 @@ VisualStudioVersion = 15.0.26730.10
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sparkd", "src\server\sparkd\sparkd.vcxproj", "{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spark_lib", "build\src\lib\spark_lib.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+1
View File
@@ -39,6 +39,7 @@
<Message Importance="High" Text="Generating service %(Proto3.FileName)" />
<Exec Command="
set PATH=$(spark_build_python_path);%path% &amp;
set PYTHONPATH=$(Externals)\python &amp;
$(protoc) ^
%(Proto3.FullPath) ^
+1 -1
View File
@@ -25,7 +25,7 @@ namespace bts.ui {
public AboutDaemon(IBus bus, [Import("username")]string username, IBrowserWindowManager browser) {
_user = username;
_browser = browser;
_dispose = bus.subscribe(SparkTags.About).ObserveOnDispatcher().Subscribe(_ => show_about());
_dispose = bus.subscribe(SparkTags.About).OnDispatcher().Subscribe(_ => show_about());
}
[Conditional("DEBUG")]
@@ -18,7 +18,7 @@ namespace bts {
public DeleteSyntheticDaemon(IBus bus, OrdersModel orders_model) {
_orders_model = orders_model;
_dispose = bus.subscribe(SparkTags.SyntheticDeleteTag)
.ObserveOnDispatcher()
.OnDispatcher()
.Subscribe(_delete_synthetic);
}
+12 -9
View File
@@ -7,7 +7,7 @@ using mbts.ui;
using System;
using System.ComponentModel.Composition;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Windows;
namespace bts.spark
@@ -22,18 +22,21 @@ namespace bts.spark
public ErrorReporter(ErrorReportService svc, IBus bus, [Import("username")] string username) {
_svc = svc;
_username = username;
_dispose = bus.subscribe(SparkTags.Support).ObserveOnDispatcher().Subscribe(msg => show_support_window(msg, "Report a problem"));
_dispose = bus.subscribe(SparkTags.Support).OnDispatcher().Subscribe(msg => show_support_window(msg, "Report a problem"));
}
public void Dispose() {
Disposable.dispose(ref _dispose);
}
public async Task report_error(string message, string title = null, MessageBoxImage? image = null) {
var result = await BTSMessageBox.ShowAsync(message, title = "Error", BTSMessageBoxButtons.OK | BTSMessageBoxButtons.Support, image ?? MessageBoxImage.Error);
if (result == BTSMessageBoxButtons.Support) {
show_support_window(message, title);
}
public void report_error(string message, string title = null, MessageBoxImage? image = null) {
// Error reports are likely to arrive the api thread (which is not an STA thread)
Application.Current.Dispatcher.BeginInvoke(async () => {
var result = await BTSMessageBox.ShowAsync(message, title = "Error", BTSMessageBoxButtons.OK | BTSMessageBoxButtons.Support, image ?? MessageBoxImage.Error);
if (result == BTSMessageBoxButtons.Support) {
show_support_window(message, title);
}
});
}
public void show_support_window(string message, string title) {
@@ -70,11 +73,11 @@ namespace bts.spark
return (get_successful_rv(result.value), string.Empty);
else {
string error_msg = template.fmt(error);
error_reporter.report_error(error_msg).ignore();
error_reporter.report_error(error_msg);
return (get_unsuccessful_rv(), error_msg);
}
} else {
error_reporter.report_error(result.error_message).ignore();
error_reporter.report_error(result.error_message);
return (get_unsuccessful_rv(), result.error_message);
}
}
-1
View File
@@ -36,7 +36,6 @@ namespace bts {
_deleted_synthetics = new HashSet<uint>(t.Where(kv => kv.Value.Deleted).Select(kv => kv.Key));
_bus.post(SparkTags.SyntheticstrategiesTag, t);
}));
new DataPersistor(_bus);
Task.Run(run);
}
@@ -28,11 +28,11 @@ namespace bts.shell {
_model = model;
_error_reporter = error_reporter;
_dispose = Disposable.Create(
model.on_orders.ObserveOnDispatcher().Subscribe(o => _orders = o),
bus.subscribe(SparkTags.SecuritiesTag).ObserveOnDispatcher().Subscribe(m =>_md_map = m),
bus.subscribe(SparkTags.ShowOrderTicketFromSecurityTag).ObserveOnDispatcher().Subscribe(_show_order_ticket),
bus.subscribe(SparkTags.ShowOrderTicketTag).ObserveOnDispatcher().Subscribe(_show_order_ticket),
bus.subscribe(SparkTags.ShowModifyOrderTag).ObserveOnDispatcher().Subscribe(_show_modify_order)
model.on_orders.OnDispatcher().Subscribe(o => _orders = o),
bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(m =>_md_map = m),
bus.subscribe(SparkTags.ShowOrderTicketFromSecurityTag).OnDispatcher().Subscribe(_show_order_ticket),
bus.subscribe(SparkTags.ShowOrderTicketTag).OnDispatcher().Subscribe(_show_order_ticket),
bus.subscribe(SparkTags.ShowModifyOrderTag).OnDispatcher().Subscribe(_show_modify_order)
);
}
@@ -0,0 +1,48 @@
using bts.api;
using mbts.spark;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui {
[Export(typeof(IShellDaemon))]
class RecenterDaemon : IShellDaemon, IDisposable {
bool _run = true;
readonly IBus _bus;
readonly TimeSpan _time_to_center;
DateTime _last_recenter;
IDisposable _dispose;
[ImportingConstructor]
public RecenterDaemon(IBus bus) {
_bus = bus;
_time_to_center = TimeSpan.FromSeconds(config.Configuration.get<bts.config.LadderKnobs>().seconds_to_recenter);
_last_recenter = DateTime.UtcNow;
_dispose = _bus.subscribe(SparkTags.RequestRecenterLadder).Subscribe(_ => trigger_recenter(SparkTags.RecenterLadderNow));
run();
}
void trigger_recenter(ITag<DateTime> tag) {
_last_recenter = DateTime.UtcNow;
_bus.post(tag, _last_recenter);
}
async void run() {
_run = true;
while (_run) {
await Task.Delay(TimeSpan.FromSeconds(5));
if (_last_recenter + _time_to_center < DateTime.UtcNow) {
trigger_recenter(SparkTags.RecenterLadderBegin);
}
}
}
public void Dispose() {
Disposable.dispose(ref _dispose);
}
}
}
@@ -23,10 +23,10 @@ namespace bts {
public SecurityPickerDaemon(IBus bus) {
_bus = bus;
_dispose = Disposable.Create(
bus.subscribe(SparkTags.ShowPickOne).ObserveOnDispatcher().Subscribe(show_pick_one),
bus.subscribe(SparkTags.FilterPickOne).ObserveOnDispatcher().Subscribe(show_filtered_pick_one),
bus.subscribe(SparkTags.ShowPickMultiple).ObserveOnDispatcher().Subscribe(show_pick_multiple),
bus.subscribe(SparkTags.ShowAndEditPickMultiple).ObserveOnDispatcher().Subscribe(show_and_edit_pick_multiple)
bus.subscribe(SparkTags.ShowPickOne).OnDispatcher().Subscribe(show_pick_one),
bus.subscribe(SparkTags.FilterPickOne).OnDispatcher().Subscribe(show_filtered_pick_one),
bus.subscribe(SparkTags.ShowPickMultiple).OnDispatcher().Subscribe(show_pick_multiple),
bus.subscribe(SparkTags.ShowAndEditPickMultiple).OnDispatcher().Subscribe(show_and_edit_pick_multiple)
);
}
@@ -0,0 +1,44 @@
using bts.api.core;
using bts.metrics;
using mbts.config;
using mbts.ui;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bts.perf {
[Export, Export(typeof(IShellDaemon))]
class ShellLatencyDaemon : IShellDaemon, IDisposable {
readonly ShellLatencyService _service;
readonly string _hostname;
readonly string _version;
IDisposable _subs;
[ImportingConstructor]
public ShellLatencyDaemon(ShellLatencyService service) {
_service = service;
_version = Configuration.get<bts.config.System>().version;
_hostname = Environment.MachineName;
_subs = _service.subscribei_clock().Subscribe(on_tick);
}
void on_tick(Update<TickImmutable, int> update) {
if (update.added.Count > 0) {
var tick = update.added.Last();
_service.report_async(new Report() {
HostName = _hostname,
Version = _version,
Timestamp = tick.Timestamp
});
}
}
public void Dispose() {
throw new NotImplementedException();
}
}
}
+6 -1
View File
@@ -12,6 +12,7 @@ using bts.api.orderman;
using bts.api.synthetic;
using bts.api.diagnostic;
using bts.api.admin;
using bts.metrics;
namespace Spark
{
@@ -41,5 +42,9 @@ namespace Spark
[Export]
public SyntheticSecurityService synthetic_security_service => context.locate<SyntheticSecurityService>();
}
[Export]
public ShellLatencyService shell_latency_service => context.locate<ShellLatencyService>();
}
}
+3 -4
View File
@@ -47,7 +47,7 @@ namespace mbts.ui
//public static ImageSource Ticket { get { return load_image("Ticket.png"); } } // replaced with AddOrders
public static ImageSource Date { get { return load_image("Date.png"); } }
public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; /*return load_image("Cube-Molecule.png");*/ } }
public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; } }
public static ImageSource Algo { get { return Cube3d; } }
public static ImageSource Quote { get { return Math; } }
public static ImageSource Math { get { return (DrawingImage)_resources["Surface"]; } }
@@ -62,9 +62,7 @@ namespace mbts.ui
#pragma warning restore CS0618
// Vector icons
public static ImageSource Favorites { get { return Favorites_Gold; } }
public static ImageSource Favorites_Gold { get { return (DrawingImage)_resources["FavoritesGold"]; } }
public static ImageSource Favorites_White { get { return (DrawingImage)_resources["FavoritesWhite"]; } }
public static ImageSource Bookmarks { get { return (DrawingImage)_resources["Bookmarks"]; } }
public static ImageSource Window { get { return (DrawingImage)_resources["Window"]; } }
public static ImageSource ActiveWindow { get { return (DrawingImage)_resources["ActiveWindow"]; } }
public static ImageSource Trash { get { return (DrawingImage)_resources["Trash"]; } }
@@ -109,6 +107,7 @@ namespace mbts.ui
public static ImageSource Tetris { get { return (DrawingImage)_resources["Tetris"]; } }
public static ImageSource ViewSettings => (DrawingImage)_resources["ViewSettings"];
public static ImageSource Wrench { get { return (DrawingImage)_resources["Wrench"]; } }
public static ImageSource UpArrow { get { return (DrawingImage)_resources["UpArrow"]; } }
public static ImageSource Support => (DrawingImage)_resources["SupportIcon"];
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

+16 -19
View File
@@ -3,33 +3,17 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mbts="clr-namespace:mbts.ui">
<DrawingImage x:Key="FavoritesGold">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M1540.22,2082.07L1546.95,2102.78 1568.73,2102.78 1551.11,2115.58 1557.84,2136.29 1540.22,2123.49 1522.6,2136.29 1529.33,2115.58 1511.71,2102.78 1533.49,2102.78 1540.22,2082.07z">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<SolidColorBrush Color="#ffd932" />
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="FavoritesWhite">
<DrawingImage x:Key="Bookmarks">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M1540.22,2082.07L1546.95,2102.78 1568.73,2102.78 1551.11,2115.58 1557.84,2136.29 1540.22,2123.49 1522.6,2136.29 1529.33,2115.58 1511.71,2102.78 1533.49,2102.78 1540.22,2082.07z">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
<Pen Thickness="3" Brush="Black" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<SolidColorBrush Color="#ffd932" />
<SolidColorBrush Color="Transparent" />
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -769,6 +753,19 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="UpArrow" x:Shared="False">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing
Brush="Black"
Geometry="M16,0L32,19.745 0,19.745z"
/>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<!-- Pin
<Path Data="F1M3,11L3,7 0,7 0,6 1,6 1,1 1,0 6,0 6,1 6,6 7,6 7,7 4,7 4,11 3,11z M2,1L2,6 4,6 4,1 2,1z" />
Unpin
@@ -0,0 +1,81 @@
using bts.spark;
using mbts.spark;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace bts.ui {
[Export(typeof(IShellModule))]
public class BookmarkShellModule : ILazyMenuModule {
readonly WindowManager _wm;
readonly IBookmarks _bookmarks;
readonly ISavedWindowFactory _window_factory;
readonly WorkspacesModel _workspaces;
[ImportingConstructor]
public BookmarkShellModule(
WorkspacesModel workspaces,
WindowManager wm,
IBookmarks bm,
ISavedWindowFactory swf) {
_workspaces = workspaces;
_wm = wm;
_bookmarks = bm;
_window_factory = swf;
}
public ImageSource icon => Icons.Bookmarks;
public string name => "My Windows";
public int priority => 35;
public IEnumerable<SparkCommand> actions {
get {
// The windows that are open
var open =
_wm.list_documents()
.Select(td => td.ViewModel)
.Where(vm => vm.HasCustomTitle)
.Select(vm =>
SparkCommand.create(vm.Title, vm.Icon, SparkTags.ActivateWindow, () => vm.Model))
.Cast<SparkCommand>();
var open_set = new HashSet<string>(open.Select(c => c.title));
// The windows that are not open
var closed =
_bookmarks.bookmarks
.Where(state => !open_set.Contains(state.title))
.Select(state =>
SparkCommand.create(state.title, Icons.Bookmarks, SparkTags.ShowWindowAsync,
() => {
_bookmarks.remove(state);
return _window_factory.restore(state);
}))
.Cast<SparkCommand>();
// todo: The windows that are closed
foreach (var w in open.Concat(closed).OrderBy(w => w.title)) {
yield return w;
}
}
}
public Task<IShellWindow> create(Stream load_stream) {
// This module shouldn't have any windows
throw new NotImplementedException();
}
public void Dispose() { }
}
}
@@ -0,0 +1,48 @@
using mbts.ui.settings;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui.shell {
public interface IBookmarks {
void save_bookmark(IShellWindow window);
void save_bookmark(WindowState window);
void remove(WindowState window);
void clear();
IEnumerable<WindowState> bookmarks { get; }
}
[Export(typeof(IBookmarks))]
public class BookmarksModel : IBookmarks {
readonly ISettingsCollector _settings_collector;
readonly Dictionary<string, WindowState> _bookmarks = new Dictionary<string, WindowState>();
[ImportingConstructor]
public BookmarksModel(ISettingsCollector settings) {
_settings_collector = settings;
}
public void save_bookmark(IShellWindow window) {
var state = _settings_collector.get_window_state(window);
if (state != null) {
_bookmarks[state.title] = state;
}
}
public void save_bookmark(WindowState window) {
_bookmarks[window.title] = window;
}
public void clear() {
_bookmarks.Clear();
}
public void remove(WindowState window) {
_bookmarks.Remove(window.title);
}
public IEnumerable<WindowState> bookmarks => _bookmarks.Values;
}
}
@@ -78,11 +78,11 @@ namespace mbts.spark {
_animation.AnimationsChanged += on_animations_changed;
void on_animations_changed() => RaisePropertyChanged(nameof(animations));
_dispose = Disposable.Create(
_orders_model.on_security_orders(security.Id).ObserveOnDispatcher().Subscribe(_on_orders),
_position.ObserveOnDispatcher().Subscribe(v => _animation.animate_position(v.newval > v.oldval)),
_inside_bids.ObserveOnDispatcher().Subscribe(v => _animation.animate_working_bids_inside(v.newval)),
_inside_asks.ObserveOnDispatcher().Subscribe(v => _animation.animate_working_asks_inside(v.newval)),
_day_change.ObserveOnDispatcher().Subscribe(v => _animation.animate_day_change(v.newval > v.oldval)),
_orders_model.on_security_orders(security.Id).OnDispatcher().Subscribe(_on_orders),
_position.OnDispatcher().Subscribe(v => _animation.animate_position(v.newval > v.oldval)),
_inside_bids.OnDispatcher().Subscribe(v => _animation.animate_working_bids_inside(v.newval)),
_inside_asks.OnDispatcher().Subscribe(v => _animation.animate_working_asks_inside(v.newval)),
_day_change.OnDispatcher().Subscribe(v => _animation.animate_day_change(v.newval > v.oldval)),
Disposable.Create(() => _animation.AnimationsChanged -= on_animations_changed)
);
}
@@ -81,7 +81,7 @@ namespace mbts.spark {
}
//
_resources.AddRange(
state.ObserveOnDispatcher().Subscribe(s => {
state.OnDispatcher().Subscribe(s => {
using (rows_view.DeferRefresh()) {
rows_view.SetSortingCriteria(s.sorting_criteria);
var filters = s.filters;
@@ -55,9 +55,9 @@ namespace mbts.spark {
})
),
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count < 3, secs => new SecurityCriteria(secs))
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs))
};
_subscriptions = _bus.subscribe(update_securities_tag).ObserveOnDispatcher().Subscribe(c =>
_subscriptions = _bus.subscribe(update_securities_tag).OnDispatcher().Subscribe(c =>
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states())));
}
@@ -67,4 +67,4 @@ namespace mbts.spark {
Commands.OfType<IDisposable>().DisposeAll();
}
}
}
}
@@ -16,7 +16,7 @@ namespace bts.shell {
public MarketViewWindowFactory(MarketShellModule module, IBus bus, OrdersModel orders_model) : base(module, "MarketView") {
_bus = bus;
_orders_model = orders_model;
_dispose = bus.subscribe(SparkTags.ShowMarketViewTag).ObserveOnDispatcher().Subscribe(_show);
_dispose = bus.subscribe(SparkTags.ShowMarketViewTag).OnDispatcher().Subscribe(_show);
}
public override Task<IShellWindow> load(StartupData state) =>
@@ -6,11 +6,14 @@ using mbts.ui.controls;
using mbts.ui.spark;
using mbts.ui.utility;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Media;
using static bts.shell.TimeAndSalesWindowFactory;
using static mbts.ui.controls.DIYGrid;
namespace mbts.spark {
class TimeAndSalesVM : SecurityGrid<TimeAndSalesVM.Row, TradeInfo> {
@@ -26,23 +29,32 @@ namespace mbts.spark {
security_filter.load_state(filters.security);
price_filter.load_state(filters.price);
size_filter.load_state(filters.size);
aggressor_filter.load_state(filters.aggressor_sides);
trade_key_filter.load_state(filters.trade_key);
security_key_filter.load_state(filters.security_key);
}
// Columns
add_column(TimeAndSalesView.Columns.Security, "Security", DIYGrid.DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.Time, "Time", DIYGrid.DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter);
add_column(TimeAndSalesView.Columns.Price, "Price", DIYGrid.DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
add_column(TimeAndSalesView.Columns.Size, "Size", DIYGrid.DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax);
add_column(TimeAndSalesView.Columns.TradeKey, "Trade Key", DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.SecurityKey, "Security Key", DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template) =>
columns.Add(new DIYGrid.RenderTextColumn<Row>(r => r[col_key], filter, filter_template) { Header = header, Width = width });
//
add_column(TimeAndSalesView.Columns.Security, DefaultColumnHeaders.Security, DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.Time, DefaultColumnHeaders.Time, DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter);
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DIYGrid.DefaultColumnWidths.Aggressor, aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex);
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
columns.Add(new TimeAndSalesColumn(
r => r[col_key]
, (r) => _get_aggressor_brush(r)
, filter
, filter_template
)
{ Header = header, Width = width, horizontal_alignment = h_alignment }
);
_resources.AddRange(
show_ladder,
DataGridFilterHelper.hook_filters(
rows_view, time_filter, security_filter, price_filter, size_filter, trade_key_filter, security_key_filter
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, trade_key_filter, security_key_filter
)
);
}
@@ -50,11 +62,22 @@ namespace mbts.spark {
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
public DelegateCommandParamObserver<Row> show_ladder { get; }
private Brush _get_aggressor_brush(Row r) {
BTSColor c;
Brush b;
if (r.aggressor_side == AggressorSide.Buy) c = BTSColors.MarketData.bid;
else if (r.aggressor_side == AggressorSide.Sell) c = BTSColors.MarketData.ask;
else c = Brushes.Transparent;
(b = new SolidColorBrush(c) { Opacity = 0.33 }).Freeze();
return b;
}
#region Filters
public IDataGridColumnPersistentFilter<Row, TimeFilterTuple> time_filter { get; } = new TimeFilterVM<Row>(r => r.time, nameof(Row.time));
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<Row>(r => r.price, nameof(Row.price));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> size_filter { get; } = new UIntMinMax<Row>(r => r.size, nameof(Row.size));
public IDataGridColumnPersistentFilter<Row, AggressorSide[]> aggressor_filter { get; } = DiscreteValuesFilterVM.Create<Row, AggressorSide>(EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side, nameof(Row.aggressor_side));
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
public IDataGridColumnPersistentFilter<Row, string> trade_key_filter { get; } = new RegexFilterVM<Row>(r => r.trade_key.ToString(), nameof(Row.trade_key));
@@ -65,7 +88,8 @@ namespace mbts.spark {
price = price_filter.get_state(),
size = size_filter.get_state(),
trade_key = trade_key_filter.get_state(),
security_key = security_key_filter.get_state()
security_key = security_key_filter.get_state(),
aggressor_sides = aggressor_filter.get_state()
};
#endregion
@@ -90,6 +114,7 @@ namespace mbts.spark {
_time_text,
_price_text,
_size_text,
_aggressor_text,
_trade_key_text,
_security_key_text;
@@ -98,11 +123,13 @@ namespace mbts.spark {
time = trade.Time.lldatetime();
price = trade.Mantissa * Math.Pow(10, trade.Exponent);
size = trade.Size;
aggressor_side = trade.AggressorSide;
show_ladder = new DelegateCommand(() => parent._show_ladder_async(this).ignore());
_security_text = security.ToString();
_time_text = time.ToString("M/d h:mm:ss tt");
_price_text = security.ContractSpec.format(price);
_size_text = size.ToString();
_aggressor_text = aggressor_side.ToString();
_trade_key_text = _trade_key.ToString();
_security_key_text = security_key.ToString();
}
@@ -113,6 +140,7 @@ namespace mbts.spark {
public DateTime time { get; }
public double price { get; }
public uint size { get; }
public AggressorSide aggressor_side { get; }
public ICommand show_ladder { get; }
public string this[TimeAndSalesView.Columns col] {
@@ -122,6 +150,7 @@ namespace mbts.spark {
case TimeAndSalesView.Columns.Time: return _time_text;
case TimeAndSalesView.Columns.Price: return _price_text;
case TimeAndSalesView.Columns.Size: return _size_text;
case TimeAndSalesView.Columns.Aggressor: return _aggressor_text;
case TimeAndSalesView.Columns.TradeKey: return _trade_key_text;
case TimeAndSalesView.Columns.SecurityKey: return _security_key_text;
default: return null;
@@ -7,7 +7,7 @@ namespace mbts.ui.spark {
/// Interaction logic for TimeAndSalesView.xaml
/// </summary>
partial class TimeAndSalesView : UserControl {
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey }
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey, Aggressor }
public TimeAndSalesView() =>
InitializeComponent();
@@ -12,6 +12,7 @@ using System.Reactive.Linq;
using System.Windows;
using System.Windows.Media;
using static bts.shell.TimeAndSalesWindowFactory;
using static mbts.ui.controls.DIYGrid;
namespace mbts.spark {
class TimeAndSalesWindow : SparkWindow<StartupData>, ISlowWindow {
@@ -36,7 +37,16 @@ namespace mbts.spark {
public override string window_type => _window_type;
public override IEnumerable<SparkCommand> Commands => _commands;
public override UIElement Content => _view;
public override double MinWidth => 444 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
public override double MinWidth => DefaultColumnWidths.Security
+ DefaultColumnWidths.Time
+ DefaultColumnWidths.Price
+ DefaultColumnWidths.Size
+ DefaultColumnWidths.Aggressor
+ DefaultColumnWidths.TradeKey
+ DefaultColumnWidths.SecurityKey
+ SPARK_MENU_COLLAPSED_WIDTH
+ LAUNCHER_BUTTON_BORDER_WIDTH;
public override StartupData SaveState => _state = new StartupData(_state.security_criteria, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
public override void Dispose() =>
@@ -47,8 +57,8 @@ namespace mbts.spark {
_vm = new SingleValuePump<TimeAndSalesVM>();
var update_securities_tag = new SparkTags.SparkTag<SecurityCriteria>("TimeAndSales Callback");
var update_securities_cmd = SparkCommand.create("Update Time and Sales", Icon, update_securities_tag);
var current_smd_source = _vm.observable.SelectMany(vm => vm.current_security_md_observable(_bus)).ObserveOnDispatcher();
var selected_secs_source = _vm.observable.SelectMany(vm => vm.selected_securities_observable()).ObserveOnDispatcher();
var current_smd_source = _vm.observable.SelectMany(vm => vm.current_security_md_observable(_bus)).OnDispatcher();
var selected_secs_source = _vm.observable.SelectMany(vm => vm.selected_securities_observable()).OnDispatcher();
_commands = new SparkCommand[] {
SparkCommand.create("Change Securities", Icons.Admin, SparkTags.ShowAndEditPickMultiple, () => (update_securities_cmd, _state.security_criteria)),
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
@@ -56,7 +66,7 @@ namespace mbts.spark {
SparkCommands.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)
};
_subscriptions = _bus.subscribe(update_securities_tag).ObserveOnDispatcher().Subscribe(update_criteria);
_subscriptions = _bus.subscribe(update_securities_tag).OnDispatcher().Subscribe(update_criteria);
_initialize();
bool always_true<T>(T _) => true;
@@ -23,7 +23,7 @@ namespace bts.shell {
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
bus.subscribe(SparkTags.ShowTimeAndSalesTag).ObserveOnDispatcher().Subscribe(show)
bus.subscribe(SparkTags.ShowTimeAndSalesTag).OnDispatcher().Subscribe(show)
);
}
@@ -70,6 +70,8 @@ namespace bts.shell {
public string trade_key;
[ProtoMember(6)]
public string security_key;
[ProtoMember(7)]
public co2.AggressorSide[] aggressor_sides;
}
}
}
@@ -34,7 +34,7 @@ namespace bts.ui {
public int priority => 40;
public IEnumerable<SparkCommand> actions => new []{
public IEnumerable<SparkCommand> actions => new SparkCommand[]{
SparkCommands.ShowWorkspaces
//SparkCommands.ShowSettings
};
@@ -14,35 +14,35 @@
<ResourceDictionary Source="/view/Resources/DataTemplates.xaml"/>
<ResourceDictionary Source="/view/Controls/Popup/PopupControl.xaml"/>
<ResourceDictionary Source="/Modules/Spreader/ParameterSectionView.xaml"/>
<ResourceDictionary Source="/view/Controls/TickFactorsPicker/TickFactorsPicker.xaml"/>
</ResourceDictionary.MergedDictionaries>
<sys:Double x:Key="GroupLevelIndent">18</sys:Double>
<sys:Double x:Key="RowHeaderWidth">285</sys:Double>
<sys:Double x:Key="RowHeight">16</sys:Double>
<sys:Double x:Key="NegativeRowHeight">-16</sys:Double>
<sys:Double x:Key="ItemsHostCanBeDisabledCollapsedHeight">15</sys:Double>
<Thickness
x:Key="GroupLevelMargin"
Left="{StaticResource GroupLevelIndent}"
Left="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
/>
<Thickness
x:Key="GroupContentAlignedWithHeaderMargin"
Left="{StaticResource GroupLevelIndent}"
Left="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
Top="{StaticResource NegativeRowHeight}"
/>
<cv:SyntheticPriceCalcDisplayTextConverter x:Key="SyntheticPriceCalcDisplayConverter"/>
<cv:TickFactorsTextConverter x:Key="TickFactorsTextConverter"/>
<usp:RowHeaderGroupIndentConverter
x:Key="RowHeaderGroupIndentConverter"
Indent="{StaticResource GroupLevelIndent}"
Indent="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
/>
<usp:RowHeaderGroupIndentConverter
x:Key="ReverseRowHeaderGroupIndentConverter"
Indent="{StaticResource GroupLevelIndent}"
Indent="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
Reverse="True"
/>
<usp:RowGroupExpanderWidthConverter
x:Key="RowGroupExpanderWidthConverter"
Indent="{StaticResource GroupLevelIndent}"
Indent="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
Offset="{StaticResource RowHeight}"
RowHeaderWidth="{StaticResource RowHeaderWidth}"
/>
@@ -293,7 +293,7 @@
</DataTrigger>
</Style.Triggers>
</Style>
<DataGridTextColumn x:Key="AddColumnPlaceholder" x:Shared="False" IsReadOnly="True">
<DataGridTextColumn x:Key="{x:Static usp:ExecutionStrategyEditor.AddColumnPlaceholderKey}" x:Shared="False" IsReadOnly="True">
<DataGridTextColumn.Header>
<c:PopupControl
ShowExpanderArrow="False" PopupPlacement="Right" Header="Add Leg" Padding="6,0"
@@ -308,7 +308,7 @@
</c:PopupControl>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataTemplate x:Key="RemovableColumnHeaderTemplate">
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.RemovableColumnHeaderTemplateKey}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@@ -321,7 +321,9 @@
<TextBlock Grid.Column="1" Margin="4,0,0,0" Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.NonRemovableColumnHeaderTemplateKey}">
<TextBlock Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
</DataTemplate>
<ControlTemplate x:Key="ExecutionStrategyEditorTemplate" TargetType="usp:ExecutionStrategyEditor">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
@@ -364,20 +366,11 @@
Grid.Column="2" VerticalAlignment="Center" Margin="6,0,0,0" Text="Ticks" IsEnabled="{TemplateBinding IsNew}"
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
/>
<ComboBox
x:Name="PART_Tick_ComboBox" Grid.Column="3" Margin="6,0,0,0" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
ItemsSource="{TemplateBinding AvailableTickFactors}" SelectedItem="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}}"
<c:TickFactorsPicker
x:Name="PART_TickFactorsPicker" Grid.Column="3" Margin="6,0,0,0" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
SelectedTickFactors="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
>
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ComboBox.DelineatedGroupContainerStyle}"/>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource TickFactorsTextConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
/>
<TextBlock Grid.Column="4" VerticalAlignment="Center" Margin="6,0,0,0">Pricing</TextBlock>
<ComboBox
Grid.Column="5" Margin="6,0,0,0" IsEnabled="False"
@@ -25,7 +25,13 @@ namespace mbts.ui.spark {
/// <summary>
/// Interaction logic for ExecutionStrategyEditor.xaml
/// </summary>
partial class ExecutionStrategyEditor : Control {
partial class ExecutionStrategyEditor : Control {
public const string
RemovableColumnHeaderTemplateKey = "RemovableColumnHeaderTemplate",
NonRemovableColumnHeaderTemplateKey = "ReadOnlyColumnHeaderTemplate",
AddColumnPlaceholderKey = "AddColumnPlaceholder";
public const double GroupLevelIndent = 18;
static ExecutionStrategyEditor() =>
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExecutionStrategyEditor), new FrameworkPropertyMetadata(typeof(ExecutionStrategyEditor)));
@@ -44,13 +50,6 @@ namespace mbts.ui.spark {
public TickFactors TickFactors { get => GetValue(TickFactorsProperty) as TickFactors; set => SetValue(TickFactorsProperty, value); }
#endregion
#region AvailableTickFactors
public static readonly DependencyProperty AvailableTickFactorsProperty = DependencyProperty.Register(
nameof(AvailableTickFactors), typeof(IEnumerable), typeof(ExecutionStrategyEditor), new PropertyMetadata(new TickFactors[0]));
public IEnumerable AvailableTickFactors { get => GetValue(AvailableTickFactorsProperty) as IEnumerable; set => SetValue(AvailableTickFactorsProperty, value); }
#endregion
#region IsNew
public static readonly DependencyProperty IsNewProperty =
DependencyProperty.Register(
@@ -196,7 +195,7 @@ namespace mbts.ui.spark {
if (dg != null) {
var legs = LegTuples;
dg.Columns.SetItems(legs.Select(_column_factory));
if (IsNew) dg.Columns.Add((DataGridColumn)FindResource("AddColumnPlaceholder"));
if (IsNew) dg.Columns.Add((DataGridColumn)FindResource(AddColumnPlaceholderKey));
_dg_line_adorner = AdornerHelper.attach_continuously_on_loaded(() => new ExecutionStrategyGridLineAdorner(dg), dg);
_legs_collection_changed_sub = (legs as INotifyCollectionChanged)?.Subscribe((s, e) => dg.Columns.ReflectChange(legs, _column_factory, e));
}
@@ -204,7 +203,7 @@ namespace mbts.ui.spark {
DataGridColumn _column_factory(LegSpreaderParamsTuple l) {
var col = new DataGridColumnInternal(l.leg.Security, ParameterCellTemplateConverter, ParameterCellEditingTemplateConverter) { Width = 75, };
if (IsNew) col.HeaderTemplate = (DataTemplate)FindResource("RemovableColumnHeaderTemplate");
col.HeaderTemplate = (DataTemplate)FindResource(IsNew ? RemovableColumnHeaderTemplateKey : NonRemovableColumnHeaderTemplateKey);
DataGridEx.SetRightBorder(col, new DataGridExVerticalLine(false, true, Colors.DarkGray, 1));
return col;
}
@@ -240,18 +239,18 @@ namespace mbts.ui.spark {
FrameworkElement _generate_element(IValueConverter cell_template_converter) {
var rv = new ContentControl();
rv.SetBinding(ContentControl.ContentProperty, new Binding());
rv.SetBinding(ContentControl.ContentTemplateProperty, new Binding("data_type") { Converter = cell_template_converter });
rv.SetBinding(ContentControl.ContentTemplateProperty, new Binding(nameof(ICell.data_type)) { Converter = cell_template_converter });
return rv;
}
}
class ExecutionStrategyErrorAdorner : NotifyDataErrorAdorner<ExecutionStrategyEditor, SyntheticEditorVM>, IDisposable {
readonly ChildProperty<TextBox> _name_text_box;
readonly ChildProperty<ComboBox> _tick_combo_box;
readonly ChildProperty<TickFactorsPicker> _tick_factors_picker;
public ExecutionStrategyErrorAdorner(ExecutionStrategyEditor editor) : base(editor) {
_name_text_box = child_property<TextBox>("PART_Name_TextBox");
_tick_combo_box = child_property<ComboBox>("PART_Tick_ComboBox");
_tick_factors_picker = child_property<TickFactorsPicker>("PART_TickFactorsPicker");
}
protected override void OnRender(DrawingContext drawing_context) {
@@ -263,7 +262,7 @@ namespace mbts.ui.spark {
if (_vm != null && _vm.GetErrors(nameof(SyntheticEditorVM.name)).Cast<string>().Any())
this.draw_border(drawing_context, _name_text_box.value, null, new Pen(Brushes.Red, 1));
if (_vm != null && _vm.GetErrors(nameof(SyntheticEditorVM.tick_factors)).Cast<string>().Any())
this.draw_border(drawing_context, _tick_combo_box.value, null, new Pen(Brushes.Red, 1));
this.draw_border(drawing_context, _tick_factors_picker.value, null, new Pen(Brushes.Red, 1));
string error_text = string.Join(", ", _vm.AllErrors);
this.draw_relative_text(RelativePosition.Below, error_text, drawing_context, _name_text_box.value, BTSColors.header_error_text_brush, 0, y_offset);
}
@@ -25,7 +25,10 @@ namespace mbts.ui.spark
void _items_presenter_SizeChanged(object sender, SizeChangedEventArgs e) { InvalidateVisual(); }
protected override void draw_vertical_line(DrawingContext dc, double x, DataGridExVerticalLine line) {
if (_items_presenter != null) dc.DrawLine(line.pen.Value, new Point(x - 10, 58), new Point(x - 10, _items_presenter.ActualHeight + 58));
x -= GroupLevelIndent;
var start = _grid.TranslatePoint(new Point(x, _grid.super_header_height + _col_header_presenter.ActualHeight), this);
var stop = _grid.TranslatePoint(new Point(x, _grid.ActualHeight), this);
dc.DrawLine(line.pen.Value, start, stop);
}
}
}
@@ -98,9 +98,9 @@ namespace bts.shell {
void IInitializable.initialize() {
Disposable.dispose(ref _dispose);
_dispose = Disposable.Create(
_bus.subscribe(SparkTags.ShowSyntheticCreateTag).ObserveOnDispatcher().Subscribe(_show_synthetic_create),
_bus.subscribe(SparkTags.ShowSyntheticStrategyTag).ObserveOnDispatcher().Subscribe(_show_synthetic_strategy),
_bus.subscribe(SparkTags.ShowSyntheticCloneTag).ObserveOnDispatcher().Subscribe(_show_synthetic_clone)
_bus.subscribe(SparkTags.ShowSyntheticCreateTag).OnDispatcher().Subscribe(_show_synthetic_create),
_bus.subscribe(SparkTags.ShowSyntheticStrategyTag).OnDispatcher().Subscribe(_show_synthetic_strategy),
_bus.subscribe(SparkTags.ShowSyntheticCloneTag).OnDispatcher().Subscribe(_show_synthetic_clone)
);
}
@@ -23,7 +23,6 @@
HorizontalAlignment="Stretch" Margin="6"
SyntheticName="{Binding name}"
TickFactors="{Binding tick_factors}"
AvailableTickFactors="{Binding available_tick_factors_view}"
IsNew="{Binding is_new}"
CanUserAddNewLeg="{Binding can_add_leg}"
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
@@ -72,9 +72,9 @@
c:DataGridFilterProperties.Filter="{Binding DataContext.security_key_filter, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_key_filter.active, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_key_filter.open, Source={u:DirectReference grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
/>
</DataGrid.Columns>
</c:DataGridEx>
</Grid>
</UserControl>
</UserControl>
@@ -32,14 +32,14 @@ namespace mbts.spark {
SparkCommand.create("Add", Icons.AddSynthetic, SparkTags.ShowSyntheticCreateTag).complete(
() => new SecurityCriteria(), () => true
), SparkCommand.create("Remove", Icons.RemoveSynthetic, SparkTags.SyntheticDeleteTag).complete(
_vm.observable.SelectMany(vm => vm.current_row_observable),
can_row_change, r => r == null ? 0 : r.security_key
_vm.observable.SelectMany(vm => vm.current_row_observable.SelectMany(observe_row_mod_params)),
can_execute, select_security_id
), SparkCommand.create("Change", Icons.ModifySynthetic, SparkTags.ShowSyntheticStrategyTag).complete(
_vm.observable.SelectMany(vm => vm.current_row_observable),
can_row_change, r => r == null ? SecurityDataHelper.Defaults.SecurityImmutable : r.security
r => r != null, r => r?.security ?? SecurityDataHelper.Defaults.SecurityImmutable
), SparkCommand.create("Clone", Icons.CloneSynthetic, SparkTags.ShowSyntheticCloneTag).complete(
_vm.observable.SelectMany(vm => vm.current_row_observable),
r => r != null && r.strategy.HasValue, r => r == null ? default : (r.security, r.strategy.Value)
_vm.observable.SelectMany(vm => vm.current_row_observable.SelectMany(observe_row_strategy)),
rs => rs.HasValue, select_row_strategy_tuple
), SparkCommands.ShowLadder.complete(
_vm.observable.SelectMany(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault
), SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData {
@@ -49,10 +49,19 @@ namespace mbts.spark {
})
};
bool can_row_change(SyntheticManagementDialogVM.Row r) =>
r != null && r.n_working_orders == 0;
}
IObservable<RowModificationParams?> observe_row_mod_params(SyntheticManagementDialogVM.Row r) =>
r == null ? Observable.Return(new RowModificationParams?()) :
_orders_model.security_has_working_orders(r.security_key).Select(hwo => RowModificationParams.create(r.security, hwo));
IObservable<RowStrategy?> observe_row_strategy(SyntheticManagementDialogVM.Row r) =>
r == null ? Observable.Return(new RowStrategy?()) :
_orders_model.spreader.security_strategy(r.security.Id).Select(strategy => RowStrategy.create(r.security, strategy));
uint select_security_id(RowModificationParams? rmp) => rmp.HasValue ? rmp.Value.security.Id : 0;
(SecurityImmutable, ExecutionStrategyImmutable) select_row_strategy_tuple(RowStrategy? rs) => rs.HasValue ? (rs.Value.security, rs.Value.strategy) : default;
bool can_execute(RowModificationParams? rmp) => rmp.HasValue && !rmp.Value.has_working_orders;
}
public void initialize(StartupData state) {
Logger.notnull(state);
_state = state;
@@ -71,5 +80,31 @@ namespace mbts.spark {
Disposable.dispose(ref _vm);
Commands.OfType<IDisposable>().DisposeAll();
}
struct RowModificationParams {
public readonly SecurityImmutable security;
public readonly bool has_working_orders;
RowModificationParams(SecurityImmutable security, bool has_working_orders) {
this.security = security;
this.has_working_orders = has_working_orders;
}
public static RowModificationParams? create(SecurityImmutable security, bool has_working_orders) =>
new RowModificationParams(security, has_working_orders);
}
struct RowStrategy {
public readonly SecurityImmutable security;
public readonly ExecutionStrategyImmutable strategy;
RowStrategy(SecurityImmutable security, ExecutionStrategyImmutable strategy) {
this.security = security;
this.strategy = strategy;
}
public static RowStrategy? create(SecurityImmutable security, ExecutionStrategyImmutable strategy) =>
new RowStrategy(security, strategy);
}
}
}
@@ -16,7 +16,7 @@ namespace bts.shell {
public SyntheticManagementWindowFactory(SpreaderShellModule module, IBus bus, OrdersModel orders_model) : base(module, "SyntheticManagement") {
_bus = bus;
_orders_model = orders_model;
_dispose = bus.subscribe(SparkTags.ShowSyntheticManagementTag).ObserveOnDispatcher().Subscribe(show);
_dispose = bus.subscribe(SparkTags.ShowSyntheticManagementTag).OnDispatcher().Subscribe(show);
}
public override Task<IShellWindow> load(StartupData state) =>
@@ -53,6 +53,8 @@ namespace bts.shell {
public IntMinMaxState n_legs;
[ProtoMember(5)]
public UIntMinMaxState n_working_orders;
[ProtoMember(6)]
public string security_key;
}
}
}
}
+2 -2
View File
@@ -10,7 +10,7 @@ namespace mbts.spark {
readonly Property<bool> _enabled;
protected CellBase(IRowModel model, LegSpreaderParamsTuple leg_tup) =>
_enabled = backing(nameof(enabled), model.observe_enabled(leg_tup).ObserveOnDispatcher());
_enabled = backing(nameof(enabled), model.observe_enabled(leg_tup).OnDispatcher());
public bool enabled => _enabled.Value;
}
@@ -24,7 +24,7 @@ namespace mbts.spark {
: base(model, leg_tup) {
_model = model;
_leg_tup = leg_tup;
_override_value_sub = model.observe_override_value(leg_tup).ObserveOnDispatcher().Subscribe(v => {
_override_value_sub = model.observe_override_value(leg_tup).OnDispatcher().Subscribe(v => {
value = v;
RaisePropertyChanged(nameof(value));
});
@@ -1,6 +1,7 @@
using bts.api;
using bts.shell;
using bts.spark;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.spark;
@@ -10,11 +11,9 @@ using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
@@ -23,7 +22,6 @@ namespace mbts.spark {
/// The view model designed to serve as the DataContext for the SyntheticEditorView
/// </summary>
partial class SyntheticEditorVM : DisposableViewModelBase, IRequestViewClose, IRequestBindingSourceUpdate, IDisposable {
readonly ObservableCollection<TickFactors> _available_tick_factors = new ObservableCollection<TickFactors>(TickUtils.all_present_ticks());
readonly QuantityModel _quantity_model;
readonly PriceMultiplierModel _price_mult_model;
readonly List<IDisposable> _resources = new List<IDisposable>();
@@ -44,10 +42,10 @@ namespace mbts.spark {
_tick_factors = TickUtils.get_regional_ticks(synthetic.ContractSpec).low_tick;
title = $"Edit '{synthetic.Synthetic.Name}'";
icon = Icons.ModifySynthetic;
_has_working_orders = backing(nameof(has_working_orders), security_has_working_orders.ObserveOnDispatcher());
_has_working_orders = backing(nameof(has_working_orders), security_has_working_orders.OnDispatcher());
var commit = new ObservantAsyncCommand(commit_async);
_resources.Add(security_has_working_orders.Select(v => !v).ObserveOnDispatcher().Subscribe(commit));
_resources.Add(_has_working_orders.ObserveOnDispatcher().Subscribe(vals => {
_resources.Add(security_has_working_orders.Select(v => !v).OnDispatcher().Subscribe(commit));
_resources.Add(_has_working_orders.OnDispatcher().Subscribe(vals => {
if (vals.newval)
set_error(nameof(has_working_orders), "Security has working orders");
else clear_error(nameof(has_working_orders));
@@ -121,7 +119,7 @@ namespace mbts.spark {
flash_error_msg("Name already taken", SparkUtils.UI.MessageFlashDuration, nameof(name)).ignore();
valid = false;
}
if (_tick_factors == null || !_tick_factors.is_valid() || !_available_tick_factors.Contains(_tick_factors)) {
if (_tick_factors == null || !_tick_factors.is_valid()) {
flash_error_msg("Please select ticks", SparkUtils.UI.MessageFlashDuration, nameof(tick_factors)).ignore();
valid = false;
}
@@ -133,6 +131,8 @@ namespace mbts.spark {
_synthetic.ContractSpec.LowTick = tick_frac;
_synthetic.ContractSpec.HighTick = tick_frac;
_synthetic.ContractSpec.PriceBreak = double.MaxValue;
_synthetic.ContractSpec.MainFraction = (uint)MathUtils.factor_out(_tick_factors.tick_base, 10);
_synthetic.ContractSpec.PriceDisplayFormat = (uint)_tick_factors.display_digits();
uint? id = await model.create_synthetic(_synthetic);
if (id.HasValue && id != 0) {
if (await model.save_strategy(id.Value, get_strategy())) {
@@ -152,8 +152,6 @@ namespace mbts.spark {
_set_ticks_automatically = is_new;
_set_quantities_automatically = is_new;
_set_price_mults_automatically = is_new;
available_tick_factors_view = CollectionViewSource.GetDefaultView(_available_tick_factors);
available_tick_factors_view.GroupDescriptions.Add(new PropertyGroupDescription(nameof(TickFactors.tick_base)));
_quantity_model = new QuantityModel(!is_new);
_price_mult_model = new PriceMultiplierModel(!is_new);
@@ -173,7 +171,8 @@ namespace mbts.spark {
}
_is_setting_price_mults = false;
};
_can_add_leg = backing(nameof(can_add_leg), leg_tuples.ObserveSelf().Select(_ => leg_count < 2), leg_count < 2);
//_can_add_leg = backing(nameof(can_add_leg), leg_tuples.ObserveSelf().Select(_ => leg_count < 2), leg_count < 2);
_can_add_leg = backing(nameof(can_add_leg), leg_tuples.ObserveSelf().Select(_ => true), true);
} else {
_can_add_leg = backing(nameof(can_add_leg), false);
}
@@ -244,9 +243,11 @@ namespace mbts.spark {
public TickFactors tick_factors {
get => _tick_factors;
set {
_tick_factors = value;
_set_ticks_automatically = false;
RaisePropertyChanged();
if (_tick_factors != value) {
_tick_factors = value;
_set_ticks_automatically = false;
RaisePropertyChanged();
}
}
}
public bool has_working_orders => _has_working_orders.Value;
@@ -260,7 +261,6 @@ namespace mbts.spark {
public IRaiseCanExecuteChangedCommand commit { get; }
public ICommand remove_leg { get; }
public ContractPickerModel contract_picker_model { get; }
public ICollectionView available_tick_factors_view { get; }
public ExecutionStrategyImmutable get_strategy() {
var mutable_strategy = _strategy.CloneToMutable();
@@ -327,8 +327,6 @@ namespace mbts.spark {
void _deduce_ticks() {
_tick_factors = TickUtils.deduce_ticks(leg_tuples.Select(l => l.leg.Security.ContractSpec));
if (_tick_factors != null && !_available_tick_factors.Contains(tick_factors))
_available_tick_factors.Add(_tick_factors);
RaisePropertyChanged(nameof(tick_factors));
}
@@ -337,4 +335,4 @@ namespace mbts.spark {
else clear_error(nameof(leg_count));
}
}
}
}
@@ -26,8 +26,9 @@ namespace mbts.spark {
description_filter.load_state(filters.description);
n_legs_filter.load_state(filters.n_legs);
n_working_orders_filter.load_state(filters.n_working_orders);
security_key_filter.load_state(filters.security_key);
}
_dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter);
_dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter);
}
protected override Row row_factory((ExecutionStrategyImmutable? strategy, SecurityMD md) row_model) =>
@@ -41,6 +42,7 @@ namespace mbts.spark {
public IDataGridColumnPersistentFilter<Row, string> description_filter { get; } = new RegexFilterVM<Row>(r => r.description, nameof(Row.description));
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> n_legs_filter { get; } = new IntMinMax<Row>(r => r.n_legs, nameof(Row.n_legs));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> n_working_orders_filter { get; } = new UIntMinMax<Row>(r => r.n_working_orders, nameof(Row.n_legs));
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
public FilterStates get_filter_states() =>
new FilterStates {
@@ -48,7 +50,8 @@ namespace mbts.spark {
create_time = create_time_filter.get_state(),
name = name_filter.get_state(),
n_legs = n_legs_filter.get_state(),
n_working_orders = n_working_orders_filter.get_state()
n_working_orders = n_working_orders_filter.get_state(),
security_key = security_key_filter.get_state()
};
#endregion
@@ -66,7 +69,7 @@ namespace mbts.spark {
.Select(t => (uint)(t.state
.Where(kv => kv.Value.is_active())
.Count())
).ObserveOnDispatcher());
).OnDispatcher());
name = security.Synthetic.Name;
n_legs = security.Synthetic.Legs.Count();
description = SecurityFormatter.format_contract_legs(security.Synthetic.Legs);
@@ -104,4 +107,4 @@ namespace mbts.spark {
});
}
}
}
}
@@ -38,7 +38,7 @@ namespace bts.shell
}
public void thread_init() {
_dispose = _message_src.ObserveOnDispatcher().Subscribe(_messages.AddLog);
_dispose = _message_src.OnDispatcher().Subscribe(_messages.AddLog);
_content =
new LogViewer {
ItemsSource = _messages,
@@ -23,7 +23,7 @@ namespace bts.shell {
public HelpShellModule(IBus bus, IBrowserWindowManager browser) {
_awf = new ActivityWindowFactory(this, bus);
_dispose = Disposable.Create(
bus.subscribe(SparkTags.ShowUri).ObserveOnDispatcher().Subscribe(u => browser.Pages.Add(u))
bus.subscribe(SparkTags.ShowUri).OnDispatcher().Subscribe(u => browser.Pages.Add(u))
);
}
@@ -12,6 +12,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows.Data;
using static bts.api.orderman.Fill.Types;
using static bts.shell.FillsWindowFactory;
using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.FillsView;
@@ -24,13 +25,16 @@ namespace mbts.spark {
public FillsVM(IBus bus, OrdersModel model, StartupData state)
: this(bus, model, _get_observable(bus, model, state), state.sorting_criteria, state.filters) { }
FillsVM(IBus bus, OrdersModel model, IObservable<ITableUpdateTransform<FillTuple, string>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
: base(RXCollection.CreateTableUpdate(fills_source), sorting_criteria) {
FillsVM(IBus bus, OrdersModel model, IObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
: base(RXCollection.CreateIncrementalUpdate(fills_source), sorting_criteria) {
_model = model;
account_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_accounts(), r => r.account, nameof(Row.account));
user_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_users(), r => r.user, nameof(Row.user));
security_filter = DiscreteValuesFilterVM.Create<Row, SecurityImmutable, SecurityCriteria>(
fills_source.with_respect_to(ft => ft.security ?? SecurityDataHelper.Defaults.SecurityImmutable, s => s.Id, ft => ft.key).Select(t => (t.added, t.removed)),
fills_source.with_respect_to(
ft => ft.security_md.HasValue ? ft.security_md.Value.security : SecurityDataHelper.Defaults.SecurityImmutable,
s => s.Id, ft => ft.key
).Select(t => (t.added.Values, t.removed.Values)),
r => r.security,
SecurityCriteria.from_securities,
SecurityCriteria.get_to_securities(bus),
@@ -49,6 +53,7 @@ namespace mbts.spark {
side_filter.load_state(filters.side);
price_filter.load_state(filters.price);
size_filter.load_state(filters.size);
fill_kind_filter.load_state(filters.fill_kind);
fill_key_filter.load_state(filters.fill_key);
order_key_filter.load_state(filters.order_key);
security_key_filter.load_state(filters.security_key);
@@ -71,6 +76,7 @@ namespace mbts.spark {
columns.Add(new SideColumn<Row, Columns>(Columns.Side, r => r.side, side_filter) { sort_member_path = nameof(Row.side) });
add_col(Columns.Size, DefaultColumnHeaders.Size, r => r.size.ToString(), nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.Size);
add_col(Columns.Price, DefaultColumnHeaders.Price, r => r.format_price(r.price), nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price);
add_col(Columns.FillKind, "Fill Kind", r => r.fill_kind.ToString(), nameof(Row.fill_kind), fill_kind_filter, DataGridFilterKnownTemplates.DiscreteValues, 70);
add_col(Columns.FillKey, DefaultColumnHeaders.FillKey, r => r.fill_key.ToString(), nameof(Row.fill_key), fill_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.FillKey);
add_col(Columns.OrderKey, DefaultColumnHeaders.OrderKey, r => r.order_key.ToString(), nameof(Row.order_key), order_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.OrderKey);
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
@@ -84,7 +90,7 @@ namespace mbts.spark {
//
_resources.Add(DataGridFilterHelper.hook_filters(rows_view,
fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter,
security_filter, side_filter, price_filter, size_filter, fill_key_filter, order_key_filter, security_key_filter
security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter
));
}
@@ -101,6 +107,7 @@ namespace mbts.spark {
public IDataGridColumnPersistentFilter<Row, Side[]> side_filter { get; } = DiscreteValuesFilterVM.Create<Row, Side>(new[] { Side.Buy, Side.Sell }, r => r.side, nameof(Row.side));
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<Row>(r => r.price, nameof(Row.price));
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> size_filter { get; } = new DoubleMinMax<Row>(r => r.size, nameof(Row.size));
public IDataGridColumnPersistentFilter<Row, FillKind[]> fill_kind_filter { get; } = DiscreteValuesFilterVM.Create<Row, FillKind>(EnumHelper.get_values<FillKind>().ToArray(), r => r.fill_kind, nameof(Row.fill_kind));
public IDataGridColumnPersistentFilter<Row, string> fill_key_filter { get; } = new RegexFilterVM<Row>(r => r.fill_key, nameof(Row.fill_key));
public IDataGridColumnPersistentFilter<Row, string> order_key_filter { get; } = new RegexFilterVM<Row>(r => r.order_key.ToString(), nameof(Row.order_key));
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
@@ -117,6 +124,7 @@ namespace mbts.spark {
side = side_filter.get_state(),
price = price_filter.get_state(),
size = size_filter.get_state(),
fill_kind = fill_kind_filter.get_state(),
fill_key = fill_key_filter.get_state(),
order_key = order_key_filter.get_state(),
security_key = security_key_filter.get_state()
@@ -126,8 +134,8 @@ namespace mbts.spark {
protected override Row row_factory(FillTuple row_model) => new Row(row_model, _model);
protected override IEnumerable<SortingCriteria> get_default_sorting() { yield return new SortingCriteria(nameof(Row.fill_time), false); }
static IObservable<ITableUpdateTransform<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, StartupData state) {
if (state.orders != null && state.orders.Length == 0) {
static IObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> _get_observable(IBus bus, OrdersModel model, StartupData 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;
@@ -138,7 +146,7 @@ namespace mbts.spark {
readonly FillImmutable _fill;
readonly OrderRecordImmutable _order;
public Row(FillTuple fill_tuple, OrdersModel model) : base(fill_tuple.security ?? SecurityDataHelper.Defaults.Security) {
public Row(FillTuple fill_tuple, OrdersModel model) : base(fill_tuple.security_md.HasValue ? fill_tuple.security_md.Value.security : SecurityDataHelper.Defaults.Security) {
_fill = fill_tuple.fill;
_order = fill_tuple.order ?? OrderDataHelper.Defaults.OrderRecordImmutable;
_model = model;
@@ -154,6 +162,7 @@ namespace mbts.spark {
public Side side => _fill.Side;
public double price => _fill.LastPrice;
public double size => _fill.LastShares;
public FillKind fill_kind => _fill.FillKind;
public string fill_key => _fill.FillKey;
public string order_key => _order.Key.ToString();
@@ -7,7 +7,7 @@ namespace mbts.ui.spark {
/// Interaction logic for FillsView.xaml
/// </summary>
partial class FillsView : UserControl {
public enum Columns { FillTime, User, Account, Exchange, Security, TimeInForce, OrderType, Side, Price, Size, FillKey, OrderKey, SecurityKey, ExchangeOrderID }
public enum Columns { FillTime, User, Account, Exchange, Security, TimeInForce, OrderType, Side, Price, Size, FillKind, FillKey, OrderKey, SecurityKey, ExchangeOrderID }
public FillsView() =>
InitializeComponent();
@@ -18,7 +18,7 @@ namespace bts.shell {
: base(module, "Fills") {
_bus = bus;
_orders_model = orders_model;
_dispose = bus.subscribe(SparkTags.ShowFillsTag).ObserveOnDispatcher().Subscribe(show);
_dispose = bus.subscribe(SparkTags.ShowFillsTag).OnDispatcher().Subscribe(show);
}
public override Task<IShellWindow> load(StartupData state) =>
@@ -74,6 +74,8 @@ namespace bts.shell {
public string order_key;
[ProtoMember(13)]
public string security_key;
[ProtoMember(14)]
public Fill.Types.FillKind[] fill_kind;
}
}
}
@@ -30,12 +30,11 @@ namespace bts.shell {
_wm = wm;
_settings_hub = settings_hub;
_orders_model = orders_model;
var window_props = Configuration.get<bts.config.IShellWindowProps>();
_max_ladders_open = (int)(window_props?.ladder?.max_total_open ?? 75);
_max_ladders_open = (int)(Configuration.get<bts.config.LadderKnobs>().max_open_ladders);
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
bus.subscribe(SparkTags.ShowLadderTag).ObserveOnDispatcher().Subscribe(s => show_windows(new[] { s })),
bus.subscribe(SparkTags.ShowLaddersTag).ObserveOnDispatcher().Subscribe(show_windows)
bus.subscribe(SparkTags.ShowLadderTag).OnDispatcher().Subscribe(s => show_windows(new[] { s })),
bus.subscribe(SparkTags.ShowLaddersTag).OnDispatcher().Subscribe(show_windows)
);
}
@@ -77,6 +76,7 @@ namespace bts.shell {
s.ask_break_line = LadderSettings.Default.ask_break_line;
if (s.mid_break_line == null)
s.mid_break_line = LadderSettings.Default.mid_break_line;
s.row_height = LadderRender.measure_row_height(s);
return s;
}
}
@@ -17,7 +17,7 @@ namespace bts.shell {
_bus = bus;
_model = model;
_dispose = bus.subscribe(SparkTags.ShowOrderBookTag).ObserveOnDispatcher().Subscribe(show);
_dispose = bus.subscribe(SparkTags.ShowOrderBookTag).OnDispatcher().Subscribe(show);
}
public override Task<IShellWindow> load(OrderBookStartupData state) {
@@ -85,6 +85,10 @@ namespace bts.shell {
public TimeFilterTuple last_change;
[ProtoMember(18)]
public string security_regex;
[ProtoMember(19)]
public api.orderman.OrderSubType[] order_sub_types;
[ProtoMember(20)]
public string[] symbols;
}
}
}
@@ -1,6 +1,8 @@
using bts.api;
using bts.api.orderman;
using bts.spark;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.spark;
using ProtoBuf;
@@ -17,10 +19,9 @@ namespace bts.shell {
_bus = bus;
_model = model;
_dispose = bus.subscribe(SparkTags.ShowPAndLTag).ObserveOnDispatcher().Subscribe(show);
_dispose = bus.subscribe(SparkTags.ShowPAndLTag).OnDispatcher().Subscribe(show);
}
public override Task<IShellWindow> load(PAndLStartupData state) {
var w = new PAndLWindow(_model, this, _bus, state, SparkCommands.ShowPAndL.icon) as IShellWindow;
return Task.FromResult(w);
@@ -33,6 +34,61 @@ namespace bts.shell {
[ProtoContract]
public class PAndLStartupData {
/// <summary>
/// Parameterless constructor for protobuf
/// </summary>
public PAndLStartupData() { }
public PAndLStartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, pnl_calc_type calc_type, TimeSpan start_of_day_time) {
this.sort_criteria = sorting_criteria;
this.filters = filters;
this.calc_type = calc_type;
this.start_of_day_time = start_of_day_time;
}
[ProtoMember(5)]
public readonly List<SortingCriteria> sort_criteria;
[ProtoMember(10)]
public FilterStates filters;
[ProtoMember(15)]
public pnl_calc_type calc_type;
[ProtoMember(20)]
public TimeSpan? start_of_day_time;
}
[ProtoContract]
public class FilterStates {
[ProtoMember(1)]
public string account_regex;
[ProtoMember(2)]
public string exchange_regex;
[ProtoMember(3)]
public string symbol_regex;
[ProtoMember(4)]
public string security_regex;
[ProtoMember(5)]
public IntMinMaxState start_net_pos;
[ProtoMember(6)]
public DoubleMinMaxState start_pnl;
[ProtoMember(7)]
public UIntMinMaxState day_long_pos;
[ProtoMember(8)]
public UIntMinMaxState day_short_pos;
[ProtoMember(9)]
public DoubleMinMaxState day_pnl;
[ProtoMember(10)]
public IntMinMaxState total_net_pos;
[ProtoMember(11)]
public DoubleMinMaxState total_pnl;
[ProtoMember(12)]
public DoubleMinMaxState pnl_price;
// [ProtoMember(13)]
// ProtoMember 13 cannot be used further
[ProtoMember(14)]
public SecurityType[] security_types;
}
}
@@ -21,10 +21,9 @@ namespace mbts.spark {
public ExistingOrderTicketVM(OrdersModel model, IObservable<OrderRecordTuple> order_source, IObservable<int[]> calculator_buttons) {
uint? key = null;
var security_md_source = order_source.Where(ot => ot.security_md.HasValue).Select(ot => ot.security_md.Value);
var security_source = security_md_source.Select(smd => smd.security);
//
_security = backing(nameof(security), security_source, SecurityDataHelper.Defaults.SecurityImmutable);
properties_vm = new OrderPropertiesEditorVM(model, security_source.ObserveOnDispatcher(), order_source.Select(ot => new InlineOrderProperties(model, ot.order.Def)));
_security = backing(nameof(security), security_md_source.Select(smd => smd.security), SecurityDataHelper.Defaults.SecurityImmutable);
properties_vm = new OrderPropertiesEditorVM(model, security_md_source, order_source.Select(ot => new InlineOrderProperties(model, ot.order.Def)));
_price = backing(nameof(price), double.NaN);
size_calculator = new CalculatorLogic(null, calculator_buttons);
_current_price = backing(nameof(current_price), order_source.Select(ort => ort.order.State.Price));
@@ -83,15 +82,15 @@ namespace mbts.spark {
size_calculator,
properties_vm,
bbo = new BBOVM(security_md_source),
order_source.ObserveOnDispatcher().Subscribe(ort => {
order_source.OnDispatcher().Subscribe(ort => {
key = ort.order.Key;
if (DoubleHelper.IsNaNOrInfinity(price))
price = ort.order.State.Price;
if (!size_calculator.value.HasValue || !size_calculator.value_setter_called)
size_calculator.bypass_value_setter(ort.order.State.Quantity);
}),
Observable.Merge(_price_dirty, _size_dirty, _account_dirty, _time_in_force_dirty, _order_type_dirty).ObserveOnDispatcher().Subscribe(_ => RaisePropertyChanged(nameof(dirty_items_text))),
_price_dirty.observe_current(true).CombineLatest(_size_dirty.observe_current(true), (pd, sd) => pd || sd).DistinctUntilChanged().ObserveOnDispatcher().Subscribe(modify_order));
Observable.Merge(_price_dirty, _size_dirty, _account_dirty, _time_in_force_dirty, _order_type_dirty).OnDispatcher().Subscribe(_ => RaisePropertyChanged(nameof(dirty_items_text))),
_price_dirty.observe_current(true).CombineLatest(_size_dirty.observe_current(true), (pd, sd) => pd || sd).DistinctUntilChanged().OnDispatcher().Subscribe(modify_order));
this.modify_order = modify_order;
set_price_command = new DelegateCommand<double>(_price.set);
}
+33 -12
View File
@@ -1,32 +1,53 @@
using bts.api;
using bts.api.orderman;
using mbts.utils;
using System;
namespace mbts.spark
{
namespace mbts.spark {
class FillTuple : IEquatable<FillTuple> {
public readonly FillImmutable fill;
public readonly OrderRecordImmutable? order;
public readonly SecurityImmutable? security;
public readonly string key;
public readonly SecurityMD? security_md;
public readonly FillTupleKey key;
public FillTuple(FillImmutable fill, OrderRecordImmutable? order, SecurityImmutable? security) {
public FillTuple(FillImmutable fill, OrderRecordImmutable? order, SecurityMD? security_md) {
this.fill = fill;
this.order = order;
this.security = security;
// a FillTuple that DOES NOT HAVE associated order/security needs to be distinguishable
// from a FillTuple (of the same underlying FillImmutable) that DOES HAVE associated order/security
key = fill.FillKey;
if (!order.HasValue) key += "!o";
if (!security.HasValue) key += "!s";
this.security_md = security_md;
key = new FillTupleKey(fill.FillKey, order.HasValue ? order.Value.Key : 0u, security_md.HasValue ? security_md.Value.security.Id : 0u);
}
public bool Equals(FillTuple other) => other != null
&& other.order.HasValue == order.HasValue
&& other.security.HasValue == security.HasValue
&& other.security_md.HasValue == security_md.HasValue
&& other.fill.Equals(fill);
public override bool Equals(object obj) => Equals(obj as FillTuple);
public override int GetHashCode() => key.GetHashCode();
}
struct FillTupleKey : IEquatable<FillTupleKey> {
public readonly string fill_key;
public readonly uint order_key, security_key;
public FillTupleKey(string fill_key, uint order_key, uint security_key) {
this.fill_key = fill_key;
this.order_key = order_key;
this.security_key = security_key;
}
public bool Equals(FillTupleKey other) =>
other.order_key == order_key &&
other.security_key == security_key &&
other.fill_key == fill_key;
public override bool Equals(object obj) =>
obj is FillTupleKey k && Equals(k);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(
fill_key.GetHashCode(),
(int)order_key,
(int)security_key);
}
}
+131 -128
View File
@@ -1,5 +1,4 @@
using bts.api;
using bts.api.core;
using bts.api.core;
using bts.api.orderman;
using mbts.collections;
using mbts.utils;
@@ -7,23 +6,25 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
namespace mbts.spark {
class FillTupleSource : IDisposable {
public static IObservable<ITableUpdateTransform<FillTuple, string>> create_observable(
class FillTupleSource : IObservable<IIncrementalUpdate<FillTuple, FillTupleKey>>, IDisposable {
// returns a ref-count Observable.Create-wrapper that initializes/destroys the [hot-observable] FillTupleSource.
// (initializes when the first subscription is made, and destroys when the last subscription is disposed).
public static IObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> create_observable(
IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities,
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders,
IObservable<TableUpdate<FillImmutable, string>> fills
) {
IObservable<TableUpdate<FillImmutable, string>> fills) {
FillTupleSource source = null;
int ref_count = 0;
return Observable.Create<TableUpdateTransform<FillTuple, string>>(observer => {
return Observable.Create<IIncrementalUpdate<FillTuple, FillTupleKey>>(observer => {
if (Interlocked.Increment(ref ref_count) == 1) {
SparkUtils.lazy_assert(source == null, () => fmt_assert_msg("source should be null, there must be a leak."));
source = new FillTupleSource(securities, orders, fills);
}
IDisposable sub = source.fill_tuples.Subscribe(observer);
IDisposable sub = source.Subscribe(observer);
return Disposable.Create(() => {
Disposable.dispose_once(ref sub);
if (Interlocked.Decrement(ref ref_count) == 0)
@@ -33,153 +34,155 @@ namespace mbts.spark {
string fmt_assert_msg(string msg) => $"{nameof(FillTupleSource)}.{nameof(create_observable)}: {msg}";
}
readonly Subject<IIncrementalUpdate<FillTuple, FillTupleKey>> _subject = new Subject<IIncrementalUpdate<FillTuple, FillTupleKey>>();
readonly Dictionary<FillTupleKey, FillTuple> _state = new Dictionary<FillTupleKey, FillTuple>();
readonly SetMap<uint, string> _order_fills = new SetMap<uint, string>();
readonly SetMap<uint, string> _security_fills = new SetMap<uint, string>();
IReadOnlyDictionary<string, FillImmutable> _fills_cache = new Dictionary<string, FillImmutable>(0);
IReadOnlyDictionary<uint, OrderRecordImmutable> _orders_cache = new Dictionary<uint, OrderRecordImmutable>(0);
IReadOnlyDictionary<uint, SecurityMD> _security_md_cache = new Dictionary<uint, SecurityMD>(0);
// _n_write_ops should never be greater than 1, bc all 3 input observables fire 'OnDispatcher'
int _n_read_ops, _n_write_ops;
IDisposable _dispose;
FillTupleSource(
IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities,
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders,
IObservable<TableUpdate<FillImmutable, string>> fills) {
State state = new State();
var src = Observable.FromEvent<Action<TableUpdateTransform<FillTuple, string>>, TableUpdateTransform<FillTuple, string>>(cb => _updated += cb, cb => _updated -= cb);
fill_tuples = Observable.Create<TableUpdateTransform<FillTuple, string>>(observer => {
var init_state = state.fill_tuples.state.ToDictionary();
observer.OnNext(new TableUpdateTransform<FillTuple, string>(init_state, init_state.Values.ToList(), new List<FillTuple>(0)));
return src.Subscribe(observer);
});
IObservable<TableUpdate<FillImmutable, string>> fills) =>
//
_dispose = Disposable.Create(
securities.ObserveOnDispatcher().Subscribe(s => update(state.update_securities(s))),
orders.ObserveOnDispatcher().Subscribe(o => update(state.update_orders(o))),
fills.ObserveOnDispatcher().Subscribe(f => update(state.update_fills(f)))
);
void update(State new_state) {
state = new_state;
_updated?.Invoke(state.fill_tuples);
securities.OnDispatcher().Subscribe(_update_securities),
orders.OnDispatcher().Subscribe(_update_orders),
fills.OnDispatcher().Subscribe(_update_fills));
public IDisposable Subscribe(IObserver<IIncrementalUpdate<FillTuple, FillTupleKey>> observer) {
using (_read()) {
observer.OnNext(new IncrementalUpdate<FillTuple, FillTupleKey>(new Dictionary<FillTupleKey, FillTuple>(_state), new Dictionary<FillTupleKey, FillTuple>(0)));
return _subject.Subscribe(observer);
}
}
event Action<TableUpdateTransform<FillTuple, string>> _updated;
public void Dispose() => Disposable.dispose(ref _dispose);
IObservable<TableUpdateTransform<FillTuple, string>> fill_tuples { get; }
public void Dispose() =>
Disposable.dispose(ref _dispose);
class State {
readonly IReadOnlyDictionary<string, FillImmutable> _fills;
readonly IReadOnlyDictionary<uint, OrderRecordImmutable> _orders;
readonly IReadOnlyDictionary<uint, SecurityMD> _securities;
readonly TableUpdateTransform<FillTuple, string> _fill_tuples;
readonly SetMap<uint, string> _order_fills;
readonly SetMap<uint, string> _security_fills;
public State() : this(
new Dictionary<string, FillImmutable>(),
new Dictionary<uint, OrderRecordImmutable>(),
new Dictionary<uint, SecurityMD>(),
new TableUpdateTransform<FillTuple, string>(new Dictionary<string, FillTuple>(), new List<FillTuple>(), new List<FillTuple>()),
new SetMap<uint, string>(),
new SetMap<uint, string>()
) { }
State(
IReadOnlyDictionary<string, FillImmutable> fills,
IReadOnlyDictionary<uint, OrderRecordImmutable> orders,
IReadOnlyDictionary<uint, SecurityMD> securities,
TableUpdateTransform<FillTuple, string> fill_tuples,
SetMap<uint, string> order_fill_tuples,
SetMap<uint, string> security_fill_tuples
) {
_fills = fills;
_orders = orders;
_securities = securities;
_fill_tuples = fill_tuples;
_order_fills = order_fill_tuples;
_security_fills = security_fill_tuples;
void _update_fills(TableUpdate<FillImmutable, string> fills) {
using (_write()) {
Dictionary<FillTupleKey, FillTuple> removed = new Dictionary<FillTupleKey, FillTuple>(), added = new Dictionary<FillTupleKey, FillTuple>();
foreach (FillImmutable fill in fills.removed) {
FillTuple old_ft = new FillTuple(fill, _get_order(fill.OrderKey), _get_security_md(fill.SecurityKey));
removed.Add(old_ft.key, old_ft);
_security_fills.remove(fill.SecurityKey, fill.FillKey);
_order_fills.remove(fill.OrderKey, fill.FillKey);
}
foreach (FillImmutable fill in fills.added) {
FillTuple new_ft = new FillTuple(fill, _get_order(fill.OrderKey), _get_security_md(fill.SecurityKey));
added.Add(new_ft.key, new_ft);
_security_fills.add(fill.SecurityKey, fill.FillKey);
_order_fills.add(fill.OrderKey, fill.FillKey);
}
_fills_cache = fills.state;
_apply_changes(added, removed);
}
}
public TableUpdateTransform<FillTuple, string> fill_tuples => _fill_tuples;
public State update_fills(TableUpdate<FillImmutable, string> fills) {
var removed = fills.removed.Select(get_fill_tuple).ToList();
var added = fills.added.Select(get_fill_tuple).ToList();
var order_fills = _order_fills.clone();
var security_fills = _security_fills.clone();
HashSet<string> added_keys = new HashSet<string>(fills.added.Select(f => f.FillKey)),
removed_keys = new HashSet<string>(fills.removed.Select(f => f.FillKey));
foreach (var ft in removed) {
string k = ft.fill.FillKey;
if (!added_keys.Contains(k)) {
order_fills.remove(ft.fill.OrderKey, k);
security_fills.remove(ft.fill.SecurityKey, k);
void _update_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
using (_write()) {
Dictionary<FillTupleKey, FillTuple> added = new Dictionary<FillTupleKey, FillTuple>(), removed = new Dictionary<FillTupleKey, FillTuple>();
HashSet<uint> changed_keys = new HashSet<uint>();
foreach (var o in orders.removed) {
if (_order_fills.try_lookup(o.Key, out ISet<string> fill_keys)) {
OrderRecordImmutable? curr_order = null;
if (orders.try_get(o.Key, out OrderRecordImmutable newval)) {
curr_order = newval;
changed_keys.Add(o.Key);
}
IEnumerable<FillImmutable> fills = fill_keys.Select(fk => _fills_cache[fk]);
foreach (FillImmutable fill in fills) {
SecurityMD? sec_md = _get_security_md(fill.SecurityKey);
FillTuple old_ft = new FillTuple(fill, o, sec_md);
removed.Add(old_ft.key, old_ft);
FillTuple new_ft = new FillTuple(fill, curr_order, sec_md);
added.Add(new_ft.key, new_ft);
}
}
}
foreach (var ft in added) {
string k = ft.fill.FillKey;
if (!removed_keys.Contains(k)) {
order_fills.add(ft.fill.OrderKey, k);
security_fills.add(ft.fill.SecurityKey, k);
foreach (var o in orders.added) {
if (!changed_keys.Contains(o.Key) && _order_fills.try_lookup(o.Key, out ISet<string> fill_keys)) {
IEnumerable<FillImmutable> fills = fill_keys.Select(fk => _fills_cache[fk]);
foreach (FillImmutable fill in fills) {
SecurityMD? sec_md = _get_security_md(fill.SecurityKey);
FillTuple old_ft = new FillTuple(fill, null, sec_md);
removed.Add(old_ft.key, old_ft);
FillTuple new_ft = new FillTuple(fill, o, sec_md);
added.Add(new_ft.key, new_ft);
}
}
}
return new State(fills.state, _orders, _securities, _fill_tuples.update(removed, added, f => f.key), order_fills, security_fills);
//
FillTuple get_fill_tuple(FillImmutable fill) =>
new FillTuple(fill, _orders.TryGetValue(fill.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(),
_securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
_orders_cache = orders.state;
_apply_changes(added, removed);
}
}
public State update_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
List<FillTuple> added = new List<FillTuple>(), removed = new List<FillTuple>();
orders.detect_changed_keys(o => o.Key, out HashSet<uint> removed_keys, out HashSet<uint> added_keys, out HashSet<uint> changed_keys);
foreach (var k in removed_keys) {
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(_orders[k], fill_keys));
added.AddRange(get_fill_tuples(null, fill_keys));
}
}
foreach (var k in added_keys) {
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(null, fill_keys));
added.AddRange(get_fill_tuples(orders.get(k), fill_keys));
}
}
foreach (var k in changed_keys) {
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(_orders[k], fill_keys));
added.AddRange(get_fill_tuples(orders.get(k), fill_keys));
}
}
return new State(_fills, orders.state, _securities, _fill_tuples.update(removed, added, ft => ft.key), _order_fills, _security_fills);
//
IEnumerable<FillTuple> get_fill_tuples(OrderRecordImmutable? o, ISet<string> fill_keys) =>
fill_keys.Select(k => {
var fill = _fills[k];
return new FillTuple(fill, o, _securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
});
}
public State update_securities(IReadOnlyDictionary<uint, SecurityMD> securities) {
List<FillTuple> added = new List<FillTuple>(), removed = new List<FillTuple>();
_securities.Keys.difference(securities.Keys, out IEnumerable<uint> added_keys, out IEnumerable<uint> removed_keys);
void _update_securities(IReadOnlyDictionary<uint, SecurityMD> securities) {
using (_write()) {
Dictionary<FillTupleKey, FillTuple> added = new Dictionary<FillTupleKey, FillTuple>(), removed = new Dictionary<FillTupleKey, FillTuple>();
_security_md_cache.Keys.difference(securities.Keys, out IEnumerable<uint> added_keys, out IEnumerable<uint> removed_keys);
foreach (var k in removed_keys) {
if (_security_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(_securities[k].security, fill_keys));
added.AddRange(get_fill_tuples(null, fill_keys));
IEnumerable<FillImmutable> fills = fill_keys.Select(fk => _fills_cache[fk]);
SecurityMD old_sec_md = _security_md_cache[k];
foreach (FillImmutable fill in fills) {
OrderRecordImmutable? order = _get_order(fill.OrderKey);
FillTuple old_ft = new FillTuple(fill, order, old_sec_md);
removed.Add(old_ft.key, old_ft);
FillTuple new_ft = new FillTuple(fill, order, null);
added.Add(new_ft.key, new_ft);
}
}
}
foreach (var k in added_keys) {
if (_security_fills.try_lookup(k, out ISet<string> fill_keys)) {
removed.AddRange(get_fill_tuples(null, fill_keys));
added.AddRange(get_fill_tuples(securities[k].security, fill_keys));
IEnumerable<FillImmutable> fills = fill_keys.Select(fk => _fills_cache[fk]);
SecurityMD new_sec_md = securities[k];
foreach (FillImmutable fill in fills) {
OrderRecordImmutable? order = _get_order(fill.OrderKey);
FillTuple old_ft = new FillTuple(fill, order, null);
removed.Add(old_ft.key, old_ft);
FillTuple new_ft = new FillTuple(fill, order, new_sec_md);
added.Add(new_ft.key, new_ft);
}
}
}
return new State(_fills, _orders, securities, _fill_tuples.update(removed, added, f => f.key), _order_fills, _security_fills);
//
IEnumerable<FillTuple> get_fill_tuples(SecurityImmutable? s, ISet<string> fill_keys) =>
fill_keys.Select(k => {
var fill = _fills[k];
return new FillTuple(fill, _orders.TryGetValue(fill.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(), s);
});
_security_md_cache = securities;
_apply_changes(added, removed);
}
}
SecurityMD? _get_security_md(uint key) =>
_security_md_cache.TryGetValue(key, out SecurityMD smd) ? smd : new SecurityMD?();
OrderRecordImmutable? _get_order(uint key) =>
_orders_cache.TryGetValue(key, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?();
void _apply_changes(Dictionary<FillTupleKey, FillTuple> added, Dictionary<FillTupleKey, FillTuple> removed) {
foreach (var kv in removed)
_state.Remove(kv.Key);
foreach (var kv in added)
_state.Add(kv.Key, kv.Value);
_subject.OnNext(new IncrementalUpdate<FillTuple, FillTupleKey>(added, removed));
}
IDisposable _read() {
while (_n_write_ops > 0)
Thread.Yield();
Interlocked.Increment(ref _n_read_ops);
return Disposable.Create(() => Interlocked.Decrement(ref _n_read_ops));
}
IDisposable _write() {
while (_n_read_ops > 0)
Thread.Yield();
Interlocked.Increment(ref _n_write_ops);
return Disposable.Create(() => Interlocked.Decrement(ref _n_write_ops));
}
}
}
+3 -4
View File
@@ -47,9 +47,8 @@ namespace mbts.spark {
IObservable<InlineOrderTicketProperties> props_source,
IObservable<int[]> calculator_buttons
) {
var security_source = security_md_source.Select(smd => smd.security);
_security = backing(nameof(security), security_source, SecurityDataHelper.Defaults.SecurityImmutable);
properties_vm = new OrderPropertiesEditorVM(model, security_source, props_source.Select(p => p.order_props));
_security = backing(nameof(security), security_md_source.Select(smd => smd.security), SecurityDataHelper.Defaults.SecurityImmutable);
properties_vm = new OrderPropertiesEditorVM(model, security_md_source, props_source.Select(p => p.order_props));
bbo = new BBOVM(security_md_source);
send_order = new DelegateCommand<Side>(side => {
bool price_valid = _validate_price(), size_valid = size_calculator.validate();
@@ -61,7 +60,7 @@ namespace mbts.spark {
set_price_command = new DelegateCommand<double>(set_price);
_dispose = Disposable.Create(
size_calculator = new CalculatorLogic(0, calculator_buttons),
props_source.ObserveOnDispatcher().Subscribe(p => on_props(p.price, p.size))
props_source.OnDispatcher().Subscribe(p => on_props(p.price, p.size))
);
void on_props(InlineUserProperty<double> price_prop, InlineUserProperty<uint> size_prop) {
@@ -114,7 +114,7 @@ namespace bts.spark {
(var security, var spec) = get_security(neworder.Def.SecurityKey);
//
// description
var mod_desc = modified_description(oldorder, neworder, spec.Value);
var mod_desc = modified_description(oldorder, neworder, spec);
if (!string.IsNullOrEmpty(mod_desc)) {
log_status(ActivityLogCategory.modify, security, mod_desc);
}
@@ -20,6 +20,11 @@ namespace mbts.ui.spark {
public DataTemplate StopLimitOrderTemplate { get; set; }
public DataTemplate StopMarketOrderTemplate { get; set; }
public DataTemplate TimeBoxOrderTemplate { get; set; }
public DataTemplate TickOrderTemplate { get; set; }
public DataTemplate LeanOrderTemplate { get; set; }
public DataTemplate JoinOrderTemplate { get; set; }
public DataTemplate TopOrderTemplate { get; set; }
public DataTemplate TriggerOrderTemplate { get; set; }
public DataTemplate IcebergOrderTemplate { get; set; }
public DataTemplate SpreaderTemplate { get; set; }
public DataTemplate BTSStopOrderTemplate { get; set; }
@@ -33,6 +38,11 @@ namespace mbts.ui.spark {
case CustomOrderDefinition.CustomOneofCase.Stop: return StopMarketOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.StopLimit: return StopLimitOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.TimeBox: return TimeBoxOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Tick: return TickOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Lean: return LeanOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Join: return JoinOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Top: return TopOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Trigger: return TriggerOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Iceberg: return IcebergOrderTemplate;
case CustomOrderDefinition.CustomOneofCase.Spreader: return SpreaderTemplate;
}
@@ -8,7 +8,6 @@ using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace mbts.spark {
class InlineOrderProperties {
@@ -44,9 +43,10 @@ namespace mbts.spark {
readonly SelectorProperty<IOrderTypeVM> _order_type;
IDisposable _order_type_param_errors_sub;
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityImmutable> security_source, IObservable<InlineOrderProperties> order_properties) {
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineOrderProperties> order_properties) {
_model = model;
_security = backing(nameof(security), security_source.ObserveOnDispatcher(), SecurityDataHelper.Defaults.SecurityImmutable);
var security_source = security_md_source.Select(smd => smd.security);
_security = backing(nameof(security), security_source.OnDispatcher(), SecurityDataHelper.Defaults.SecurityImmutable);
// guarentee that "this.security" will always be set before observers are notified
// this is important bc some observers will reference "this.security" in their callbacks
// - namely the [SelectorProperty]order_type's "get_default" function,
@@ -75,7 +75,7 @@ namespace mbts.spark {
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
// local methods
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) {
var vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_source);
var vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source);
vm.load_from(cd);
return vm;
}
@@ -136,6 +136,7 @@ namespace mbts.spark {
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
Disposable.dispose(ref _order_type_param_errors_sub);
_order_type.items.OfType<IDisposable>().DisposeAll();
}
}
}
@@ -26,7 +26,7 @@ namespace mbts.spark {
}
class IcebergOrderVM : OrderTypeVMBase<IcebergOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
readonly IObservable<SecurityImmutable> _security_source;
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
@@ -34,19 +34,19 @@ namespace mbts.spark {
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public IcebergOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
: this(model, security_source, security_source.Select(s =>
new InlineIcebergOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineIcebergOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
public IcebergOrderVM(OrdersModel model,
IObservable<SecurityImmutable> security_source,
IObservable<SecurityMD> security_md_source,
IObservable<InlineIcebergOrderProperties> inline_properties)
: base(model) {
_security_source = security_source;
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_source.ObserveOnDispatcher()
.Subscribe(s => _security = s),
inline_properties.ObserveOnDispatcher()
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p => {
if (_order_types.Count == 0 || p.child_order_types.force_override) {
_order_types.SetItems(p.child_order_types.value);
@@ -68,7 +68,7 @@ namespace mbts.spark {
public ICollection<IOrderTypeVM> child_order_types {
get {
if (_child_order_choices == null) {
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
}
return _child_order_choices;
}
@@ -0,0 +1,287 @@
using bts.api;
using bts.api.orderman;
using mbts.ui;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark
{
static partial class OrderTypeVMFactory
{
class InlineJoinOrderProperties
{
public InlineJoinOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
var hedgetype = CustomOneofCase.Lean;
if(!child_order_types.Contains(hedgetype))
hedgetype = CustomOneofCase.Limit;
//ExceptionAssert.InvalidOperation.That(child_order_types.Contains(hedgetype), $"Hedge order type {hedgetype} is not in hedge_order_types");
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
join_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
enable_hedge = new InlineUserProperty<bool>(false, false);
hedge_ticks = new InlineUserProperty<int>(1, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
public readonly InlineUserProperty<bts.spark.QuantityMeasure> join_threshold;
public readonly InlineUserProperty<bool> enable_hedge;
public readonly InlineUserProperty<int> hedge_ticks;
}
class JoinOrderVM : OrderTypeVMBase<JoinOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
InlineJoinOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
IOrderTypeVM _hedge_order;
IDisposable _hedge_order_errors_sub;
public uint tick_offset_on_deploy { get; set; } = 0;
public bts.spark.QuantityMeasure join_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
bool _enable_hedge = false;
public bool enable_hedge
{
get { return _enable_hedge; }
set
{
_enable_hedge = value;
RaisePropertyChanged(nameof(enable_hedge));
}
}
public int hedge_ticks { get; set; } = 1;
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineJoinOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineJoinOrderProperties> inline_properties) : base(model)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p => {
if (_order_types.Count == 0 || p.child_order_types.force_override)
{
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
}
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
{
_select_hedge_order(p.selected_hedge_order_type.value);
}
if (p.tick_offset_on_deploy.force_override)
{
tick_offset_on_deploy = p.tick_offset_on_deploy.value;
RaisePropertyChanged(nameof(tick_offset_on_deploy));
}
if (p.join_threshold.force_override)
{
join_threshold = p.join_threshold.value;
RaisePropertyChanged(nameof(join_threshold));
}
if (p.enable_hedge.force_override)
{
enable_hedge = p.enable_hedge.value;
RaisePropertyChanged(nameof(enable_hedge));
}
if (p.hedge_ticks.force_override)
{
hedge_ticks = p.hedge_ticks.value;
RaisePropertyChanged(nameof(hedge_ticks));
}
_inline_props = p;
_validate();
})
);
_validate();
}
public override void assign_to(CustomOrderDefinition value)
{
if (_child_order == null)
_select_child_order(model.factory.default_order_type(_security.Value, true));
if (_hedge_order == null && enable_hedge)
_select_hedge_order(model.factory.default_order_type(_security.Value, true));
_validate();
if (!HasErrors)
{
var i = factory.join_order();
i.TickOffsetOnDeploy = tick_offset_on_deploy;
i.JoinThreshold = join_threshold;
i.EnableHedge = enable_hedge;
i.HedgeTicks = hedge_ticks;
child_order.assign_to(i.Child);
if(enable_hedge && hedge_order != null)
hedge_order.assign_to(i.Hedge);
value.Join = i;
}
}
public ICollection<IOrderTypeVM> child_order_types
{
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _child_order_choices;
}
}
public ICollection<IOrderTypeVM> hedge_order_types
{
get
{
if (_hedge_order_choices == null)
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _hedge_order_choices;
}
}
public IOrderTypeVM child_order
{
get
{
init();
return _child_order;
}
set
{
Disposable.dispose(ref _child_order_errors_sub);
_child_order = value;
_child_order?.init();
if (_child_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(child_order));
}
}
public IOrderTypeVM hedge_order
{
get
{
init();
return _hedge_order;
}
set
{
Disposable.dispose(ref _hedge_order_errors_sub);
_hedge_order = value;
_hedge_order?.init();
if (_hedge_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_hedge_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(hedge_order));
}
}
public void Dispose()
{
Disposable.dispose(ref _dispose);
if (_child_order_choices != null)
_child_order_choices.OfType<IDisposable>().DisposeAll();
if (_hedge_order_choices != null)
_hedge_order_choices.OfType<IDisposable>().DisposeAll();
}
public override void init()
{
if (_child_order == null && _security.HasValue)
_select_child_order();
if (_hedge_order == null && _security.HasValue && enable_hedge)
_select_hedge_order();
_validate();
}
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
void _select_child_order(CustomOneofCase ot)
{
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_child_order == null)
_child_order = child_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(child_order));
}
void _select_hedge_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
_inline_props.selected_hedge_order_type.value :
model.factory.default_order_type(_security.Value, true);
_select_hedge_order(ot);
}
void _select_hedge_order(CustomOneofCase ot)
{
_hedge_order = hedge_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_hedge_order == null)
_hedge_order = hedge_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(hedge_order));
}
void _validate()
{
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
set_error(nameof(child_order), string.Empty, true);
else clear_error(nameof(child_order));
if ( (_hedge_order == null && enable_hedge) || (_hedge_order is INotifyDataErrorInfo hdei && hdei.HasErrors))
set_error(nameof(hedge_order), string.Empty, true);
else clear_error(nameof(hedge_order));
}
}
}
}
@@ -0,0 +1,174 @@
using bts.api;
using bts.api.orderman;
using mbts.ui;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark
{
static partial class OrderTypeVMFactory
{
class InlineLeanOrderProperties
{
public InlineLeanOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
max_chase_ticks = new InlineUserProperty<uint>(10, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
public readonly InlineUserProperty<uint> max_chase_ticks;
}
class LeanOrderVM : OrderTypeVMBase<LeanOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
InlineLeanOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
public uint max_chase_ticks { get; set; } = 10;
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineLeanOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineLeanOrderProperties> inline_properties) : base(model)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p => {
if (_order_types.Count == 0 || p.child_order_types.force_override)
{
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
}
if (p.cross_threshold.force_override)
{
cross_threshold = p.cross_threshold.value;
RaisePropertyChanged(nameof(cross_threshold));
}
if (p.max_chase_ticks.force_override)
{
max_chase_ticks = p.max_chase_ticks.value;
RaisePropertyChanged(nameof(max_chase_ticks));
}
_inline_props = p;
_validate();
})
);
_validate();
}
public override void assign_to(CustomOrderDefinition value)
{
if (_child_order == null)
_select_child_order(model.factory.default_order_type(_security.Value, true));
_validate();
if (!HasErrors)
{
var i = factory.lean_order();
i.CrossThreshold = cross_threshold;
i.MaxChaseTicks = max_chase_ticks;
child_order.assign_to(i.Child);
value.Lean = i;
}
}
public ICollection<IOrderTypeVM> child_order_types
{
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _child_order_choices;
}
}
public IOrderTypeVM child_order
{
get
{
init();
return _child_order;
}
set
{
Disposable.dispose(ref _child_order_errors_sub);
_child_order = value;
_child_order?.init();
if (_child_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(child_order));
}
}
public void Dispose()
{
Disposable.dispose(ref _dispose);
if (_child_order_choices != null)
_child_order_choices.OfType<IDisposable>().DisposeAll();
}
public override void init()
{
if (_child_order == null && _security.HasValue)
_select_child_order();
_validate();
}
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
void _select_child_order(CustomOneofCase ot)
{
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_child_order == null)
_child_order = child_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(child_order));
}
void _validate()
{
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
set_error(nameof(child_order), string.Empty, true);
else clear_error(nameof(child_order));
}
}
}
}
@@ -4,6 +4,7 @@ using bts.spark;
using mbts.spark.utilities;
using mbts.ui.shell;
using System;
using System.Reactive.Linq;
using System.Windows.Input;
using static bts.api.orderman.CustomOrderDefinition;
@@ -12,23 +13,30 @@ namespace mbts.spark {
public static IOrderTypeVM market(OrdersModel model) => new MarketOrderVM(model);
public static IOrderTypeVM limit(OrdersModel model) => new LimitOrderVM(model);
public static IOrderTypeVM stop(OrdersModel model) => new StopOrderVM(model);
public static IOrderTypeVM stop_limit(OrdersModel model) => new StopLimitOrderVM(model);
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TimeBoxOrderVM(model, security_source);
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityImmutable> security_source) => new IcebergOrderVM(model, security_source);
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source) => new StopLimitOrderVM(model, security_md_source);
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TimeBoxOrderVM(model, security_md_source);
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TickOrderVM(model, security_md_source);
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source) => new LeanOrderVM(model, security_md_source);
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source) => new JoinOrderVM(model, security_md_source);
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TopOrderVM(model, security_md_source);
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TriggerOrderVM(model, security_md_source);
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source) => new IcebergOrderVM(model, security_md_source);
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source) => new SpreaderOrderVM(model, security_source);
static Func<CustomOneofCase, IOrderTypeVM> _get_factory(OrdersModel orders_model, IObservable<SecurityImmutable> security_source) =>
new Func<CustomOneofCase, IOrderTypeVM>(ot => create(ot, orders_model, security_source));
public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model, IObservable<SecurityImmutable> security_source) {
public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model, IObservable<SecurityMD> security_md_source) {
switch (order_type) {
case CustomOneofCase.Market: return market(model);
case CustomOneofCase.Limit: return limit(model);
case CustomOneofCase.Stop: return stop(model);
case CustomOneofCase.StopLimit: return stop_limit(model);
case CustomOneofCase.TimeBox: return time_box(model, security_source);
case CustomOneofCase.Iceberg: return iceberg(model, security_source);
case CustomOneofCase.Spreader: return spreader(model, security_source);
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source);
case CustomOneofCase.TimeBox: return time_box(model, security_md_source);
case CustomOneofCase.Tick: return tick(model, security_md_source);
case CustomOneofCase.Lean: return lean(model, security_md_source);
case CustomOneofCase.Join: return join(model, security_md_source);
case CustomOneofCase.Top: return top(model, security_md_source);
case CustomOneofCase.Trigger: return trigger(model, security_md_source);
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source);
case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security));
default: throw new NotImplementedException();
}
}
@@ -1,15 +1,17 @@
using bts.api;
using bts.api.orderman;
using mbts.ui;
using mbts.ui.controls;
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
namespace mbts.spark {
static partial class OrderTypeVMFactory {
private class SpreaderOrderVM : OrderTypeVMBase<SpreaderOrderDefinition>, IOrderTypeVM, IDisposable {
readonly Property<SpreaderOrderDefinitionImmutable?> _template;
readonly StrategyEditorWrapper _strategy_editor_wrapper;
IDisposable _dispose;
SyntheticEditorVM _strategy_editor;
public SpreaderOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
: this(model, security_source, security_source.SelectMany(s => model.spreader.security_strategy(s.Id)
@@ -26,15 +28,15 @@ namespace mbts.spark {
IObservable<InlineUserProperty<SpreaderOrderDefinitionImmutable>> order_template_source)
: base(model) {
_template = backing(nameof(template), new SpreaderOrderDefinitionImmutable?());
_strategy_editor_wrapper = new StrategyEditorWrapper(this);
_dispose = Disposable.Create(
_template.ObserveOnDispatcher().Subscribe(vals => {
_strategy_editor_wrapper,
_template.OnDispatcher().Subscribe(vals => {
_validate();
}),
order_template_source.ObserveOnDispatcher().Subscribe(prop => {
order_template_source.OnDispatcher().Subscribe(prop => {
if (prop.force_override || (!template.HasValue && prop.has_value)) {
_template.Value = prop.value;
if (_strategy_editor != null)
_strategy_editor.set_synthetic(prop.value.Security, prop.value.ExecutionStrategy);
_validate();
}
})
@@ -43,23 +45,14 @@ namespace mbts.spark {
}
public SpreaderOrderDefinitionImmutable? template => _template.Value;
public SyntheticEditorVM strategy_editor {
get {
if (_strategy_editor == null && template.HasValue) {
var def = template.Value;
_strategy_editor = new SyntheticEditorVM(model.spreader, def.Security, def.ExecutionStrategy, model.security_has_working_orders(def.Security.Id));
}
return _strategy_editor;
}
}
public SyntheticEditorVM strategy_editor => _strategy_editor_wrapper.vm;
public IEnumerable<DialogSkirtItem> skirt_items => _strategy_editor_wrapper.skirt_items;
public override bool editable_properties_open {
get => base.editable_properties_open;
set {
if (!value && template.HasValue && _strategy_editor != null) {
if (!value && template.HasValue && strategy_editor != null) {
var mutable_template = template.Value.CloneToMutable();
mutable_template.ExecutionStrategy = _strategy_editor.get_strategy().CloneToMutable();
mutable_template.ExecutionStrategy = _strategy_editor_wrapper.vm.get_strategy().CloneToMutable();
_template.Value = new SpreaderOrderDefinitionImmutable(mutable_template);
}
base.editable_properties_open = value;
@@ -80,6 +73,39 @@ namespace mbts.spark {
set_error(nameof(template), "Waiting for security and/or strategy.", true);
else clear_error(nameof(template));
}
// lazily initializes SyntheticEditorVM and associated dialog skirt items
class StrategyEditorWrapper : IDisposable {
readonly SpreaderOrderVM _owner;
SyntheticEditorVM _vm;
DialogSkirtItem _save_strategy_item;
public StrategyEditorWrapper(SpreaderOrderVM owner) {
_owner = owner;
_owner._template.Changed += _order_template_changed;
}
public SyntheticEditorVM vm { get { _init(); return _vm; } }
public IEnumerable<DialogSkirtItem> skirt_items { get { _init(); yield return _save_strategy_item; } }
public void Dispose() {
Disposable.dispose(ref _vm);
_owner._template.Changed -= _order_template_changed;
}
void _init() {
if (_vm == null && _owner.template.HasValue) {
var def = _owner.template.Value;
_vm = new SyntheticEditorVM(_owner.model.spreader, def.Security, def.ExecutionStrategy, _owner.model.security_has_working_orders(def.Security.Id));
_save_strategy_item = new DialogSkirtItem("Save Strategy", _vm.commit);
}
}
void _order_template_changed(SpreaderOrderDefinitionImmutable? oldval, SpreaderOrderDefinitionImmutable? newval) {
if (_vm != null && newval.HasValue)
_vm.set_synthetic(newval.Value.Security, newval.Value.ExecutionStrategy);
}
}
}
}
}
@@ -1,18 +1,61 @@
using bts.api;
using bts.api.orderman;
using mbts.spark.utilities;
using mbts.utils;
using mbts.co2;
using mbts.ui;
using System;
using System.Reactive.Linq;
namespace mbts.spark {
static partial class OrderTypeVMFactory {
private class StopLimitOrderVM : OrderTypeVMBase<StopLimitOrderDefinition> {
double _stop_price = 0;
class InlineStopLimitOrderProperties {
readonly Func<InlineUserProperty<double>> _get_stop_price;
internal StopLimitOrderVM(OrdersModel model) : base(model) { }
public static IObservable<InlineStopLimitOrderProperties> subscribe(IObservable<SecurityMD> security_md_source) {
ulong time = 0;
return security_md_source.Select(smd => new InlineStopLimitOrderProperties(() => {
smd.md.get_bbo(ref time, out NativePriceSize bid, out NativePriceSize ask);
return new InlineUserProperty<double>(bid.price);
}));
}
public InlineStopLimitOrderProperties(Func<InlineUserProperty<double>> get_stop_price) =>
_get_stop_price = get_stop_price;
public InlineUserProperty<double> stop_price => _get_stop_price();
}
private class StopLimitOrderVM : OrderTypeVMBase<StopLimitOrderDefinition>, IDisposable {
UserProperty<double> _stop_price;
InlineStopLimitOrderProperties _inline_props;
IDisposable _dispose;
internal StopLimitOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source)) { }
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties) : base(model) {
_stop_price = new UserProperty<double>(backing(nameof(stop_price), double.NaN));
_dispose = Disposable.Create(
spec_source.OnDispatcher().Subscribe(s => {
spec = s;
RaisePropertyChanged(nameof(spec));
}), inline_properties.OnDispatcher().Subscribe(p => _inline_props = p));
}
public override bool editable_properties_open {
get => base.editable_properties_open;
set {
if (value) {
if (_update_inline_props())
_validate();
}
base.editable_properties_open = value;
}
}
public double stop_price {
get => _stop_price;
get => _stop_price.Value;
set {
_stop_price = value;
_stop_price.Value = value;
_validate();
}
}
@@ -21,6 +64,8 @@ namespace mbts.spark {
SecurityDataHelper.Defaults.ContractSpecificationImmutable;
public override void assign_to(CustomOrderDefinition value) {
_update_inline_props();
_validate();
if (!HasErrors) {
var sl = factory.stop_limit_order();
sl.StopPrice = stop_price;
@@ -28,9 +73,22 @@ namespace mbts.spark {
}
}
public void Dispose() => Disposable.dispose(ref _dispose);
// no need to constantly poll the md, just check the inline props when the popup opens, and right before sending an order
// (if the user never looks at the popup, assume that the bid_price will be used)
// TODO: add settings so the trigger-price can default to something relative to the md, e.g "bid-price" or "ask-price - nticks" or "best-bid-price with a minimum size of 10"
bool _update_inline_props() {
var inline_stop_price = _inline_props.stop_price;
if (inline_stop_price.has_value && (inline_stop_price.force_override || !_stop_price.setter_called)) {
_stop_price.bypass_setter(inline_stop_price.value);
return true;
} else return false;
}
void _validate() {
using (validation())
validation_assert(!DoubleHelper.IsNaNOrInfinity(_stop_price), string.Empty, nameof(stop_price));
validation_assert(!DoubleHelper.IsNaNOrInfinity(stop_price), string.Empty, nameof(stop_price));
}
}
}
@@ -0,0 +1,201 @@
using bts.api;
using bts.api.orderman;
using mbts.ui;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark
{
static partial class OrderTypeVMFactory
{
class InlineTickOrderProperties
{
public InlineTickOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
cross_ticks = new InlineUserProperty<uint>(0, false);
max_chase_ticks = new InlineUserProperty<uint>(10, false);
detach_on_toward = new InlineUserProperty<bool>(true, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
public readonly InlineUserProperty<uint> cross_ticks;
public readonly InlineUserProperty<uint> max_chase_ticks;
public readonly InlineUserProperty<bool> detach_on_toward;
}
class TickOrderVM : OrderTypeVMBase<TickOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
InlineTickOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public uint tick_offset_on_deploy { get; set; } = 0;
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
public uint cross_ticks { get; set; } = 0;
public uint max_chase_ticks { get; set; } = 10;
public bool detach_on_toward { get; set; } = true;
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTickOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineTickOrderProperties> inline_properties) : base(model)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p => {
if (_order_types.Count == 0 || p.child_order_types.force_override)
{
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
}
if (p.tick_offset_on_deploy.force_override)
{
tick_offset_on_deploy = p.tick_offset_on_deploy.value;
RaisePropertyChanged(nameof(tick_offset_on_deploy));
}
if (p.cross_threshold.force_override)
{
cross_threshold = p.cross_threshold.value;
RaisePropertyChanged(nameof(cross_threshold));
}
if (p.cross_ticks.force_override)
{
cross_ticks = p.cross_ticks.value;
RaisePropertyChanged(nameof(cross_ticks));
}
if (p.max_chase_ticks.force_override)
{
max_chase_ticks = p.max_chase_ticks.value;
RaisePropertyChanged(nameof(max_chase_ticks));
}
if (p.detach_on_toward.force_override)
{
detach_on_toward = p.detach_on_toward.value;
RaisePropertyChanged(nameof(detach_on_toward));
}
_inline_props = p;
_validate();
})
);
_validate();
}
public override void assign_to(CustomOrderDefinition value)
{
if (_child_order == null)
_select_child_order(model.factory.default_order_type(_security.Value, true));
_validate();
if (!HasErrors)
{
var i = factory.tick_order();
i.TickOffsetOnDeploy = tick_offset_on_deploy;
i.CrossThreshold = cross_threshold;
i.CrossTicks = cross_ticks;
i.MaxChaseTicks = max_chase_ticks;
i.DetachOnToward = detach_on_toward;
child_order.assign_to(i.Child);
value.Tick = i;
}
}
public ICollection<IOrderTypeVM> child_order_types
{
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _child_order_choices;
}
}
public IOrderTypeVM child_order
{
get
{
init();
return _child_order;
}
set
{
Disposable.dispose(ref _child_order_errors_sub);
_child_order = value;
_child_order?.init();
if (_child_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(child_order));
}
}
public void Dispose()
{
Disposable.dispose(ref _dispose);
if (_child_order_choices != null)
_child_order_choices.OfType<IDisposable>().DisposeAll();
}
public override void init()
{
if (_child_order == null && _security.HasValue)
_select_child_order();
_validate();
}
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
void _select_child_order(CustomOneofCase ot)
{
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_child_order == null)
_child_order = child_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(child_order));
}
void _validate()
{
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
set_error(nameof(child_order), string.Empty, true);
else clear_error(nameof(child_order));
}
}
}
}
@@ -28,7 +28,7 @@ namespace mbts.spark {
class TimeBoxOrderVM : OrderTypeVMBase<TimeBoxOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
const double _start_immediately_delay_ms = 500;
readonly IObservable<SecurityImmutable> _security_source;
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
@@ -40,19 +40,19 @@ namespace mbts.spark {
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
: this(model, security_source, security_source.Select(s =>
new InlineTimeBoxOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTimeBoxOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
public TimeBoxOrderVM(OrdersModel model,
IObservable<SecurityImmutable> security_source,
IObservable<SecurityMD> security_md_source,
IObservable<InlineTimeBoxOrderProperties> inline_properties)
: base(model) {
_security_source = security_source;
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_source.ObserveOnDispatcher()
.Subscribe(s => _security = s),
inline_properties.ObserveOnDispatcher()
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p => {
if (_order_types.Count == 0 || p.child_order_types.force_override) {
_order_types.SetItems(p.child_order_types.value);
@@ -94,7 +94,7 @@ namespace mbts.spark {
public ICollection<IOrderTypeVM> child_order_types {
get {
if (_child_order_choices == null) {
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
}
return _child_order_choices;
}
@@ -0,0 +1,287 @@
using bts.api;
using bts.api.orderman;
using mbts.ui;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark
{
static partial class OrderTypeVMFactory
{
class InlineTopOrderProperties
{
public InlineTopOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
var hedgetype = CustomOneofCase.Lean;
if(!child_order_types.Contains(hedgetype))
hedgetype = CustomOneofCase.Limit;
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
watch_level_tick_offset = new InlineUserProperty<uint>(0, false);
top_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 }, false);
enable_hedge = new InlineUserProperty<bool>(false, false);
hedge_ticks = new InlineUserProperty<int>(1, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
public readonly InlineUserProperty<uint> watch_level_tick_offset;
public readonly InlineUserProperty<bts.spark.QuantityMeasure> top_threshold;
public readonly InlineUserProperty<bool> enable_hedge;
public readonly InlineUserProperty<int> hedge_ticks;
}
class TopOrderVM : OrderTypeVMBase<TopOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
InlineTopOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
IOrderTypeVM _hedge_order;
IDisposable _hedge_order_errors_sub;
public uint watch_level_tick_offset { get; set; } = 0;
public bts.spark.QuantityMeasure top_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
bool _enable_hedge = false;
public bool enable_hedge
{
get { return _enable_hedge; }
set
{
_enable_hedge = value;
RaisePropertyChanged(nameof(enable_hedge));
}
}
public int hedge_ticks { get; set; } = 1;
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTopOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
{ }
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineTopOrderProperties> inline_properties) : base(model)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p =>
{
if (_order_types.Count == 0 || p.child_order_types.force_override)
{
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
}
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
{
_select_hedge_order(p.selected_hedge_order_type.value);
}
if (p.watch_level_tick_offset.force_override)
{
watch_level_tick_offset = p.watch_level_tick_offset.value;
RaisePropertyChanged(nameof(watch_level_tick_offset));
}
if (p.top_threshold.force_override)
{
top_threshold = p.top_threshold.value;
RaisePropertyChanged(nameof(top_threshold));
}
if (p.enable_hedge.force_override)
{
enable_hedge = p.enable_hedge.value;
RaisePropertyChanged(nameof(enable_hedge));
}
if (p.hedge_ticks.force_override)
{
hedge_ticks = p.hedge_ticks.value;
RaisePropertyChanged(nameof(hedge_ticks));
}
_inline_props = p;
_validate();
})
);
_validate();
}
public override void assign_to(CustomOrderDefinition value)
{
if (_child_order == null)
_select_child_order(model.factory.default_order_type(_security.Value, true));
if (_hedge_order == null && enable_hedge)
_select_hedge_order(model.factory.default_order_type(_security.Value, true));
_validate();
if (!HasErrors)
{
var i = factory.top_order();
i.WatchLevelTickOffset = watch_level_tick_offset;
i.TopThreshold = top_threshold;
i.EnableHedge = enable_hedge;
i.HedgeTicks = hedge_ticks;
child_order.assign_to(i.Child);
if (enable_hedge && hedge_order != null)
hedge_order.assign_to(i.Hedge);
value.Top = i;
}
}
public ICollection<IOrderTypeVM> child_order_types
{
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _child_order_choices;
}
}
public ICollection<IOrderTypeVM> hedge_order_types
{
get
{
if (_hedge_order_choices == null)
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _hedge_order_choices;
}
}
public IOrderTypeVM child_order
{
get
{
init();
return _child_order;
}
set
{
Disposable.dispose(ref _child_order_errors_sub);
_child_order = value;
_child_order?.init();
if (_child_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(child_order));
}
}
public IOrderTypeVM hedge_order
{
get
{
init();
return _hedge_order;
}
set
{
Disposable.dispose(ref _hedge_order_errors_sub);
_hedge_order = value;
_hedge_order?.init();
if (_hedge_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_hedge_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(hedge_order));
}
}
public void Dispose()
{
Disposable.dispose(ref _dispose);
if (_child_order_choices != null)
_child_order_choices.OfType<IDisposable>().DisposeAll();
if (_hedge_order_choices != null)
_hedge_order_choices.OfType<IDisposable>().DisposeAll();
}
public override void init()
{
if (_child_order == null && _security.HasValue)
_select_child_order();
if (_hedge_order == null && _security.HasValue && enable_hedge)
_select_hedge_order();
_validate();
}
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
void _select_child_order(CustomOneofCase ot)
{
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_child_order == null)
_child_order = child_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(child_order));
}
void _select_hedge_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
_inline_props.selected_hedge_order_type.value :
model.factory.default_order_type(_security.Value, true);
_select_hedge_order(ot);
}
void _select_hedge_order(CustomOneofCase ot)
{
_hedge_order = hedge_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_hedge_order == null)
_hedge_order = hedge_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(hedge_order));
}
void _validate()
{
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
set_error(nameof(child_order), string.Empty, true);
else clear_error(nameof(child_order));
if ((_hedge_order == null && enable_hedge) || (_hedge_order is INotifyDataErrorInfo hdei && hdei.HasErrors))
set_error(nameof(hedge_order), string.Empty, true);
else clear_error(nameof(hedge_order));
}
}
}
}
@@ -0,0 +1,194 @@
using bts.api;
using bts.api.orderman;
using mbts.ui;
using mbts.ui.utility;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark
{
static partial class OrderTypeVMFactory
{
class InlineTriggerOrderProperties
{
public InlineTriggerOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
trigger_price_type = new InlineUserProperty<TriggerPriceType>(TriggerPriceType.Bid, false);
order_price = new InlineUserProperty<double>(double.NaN, true);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<TriggerPriceType> trigger_price_type;
public readonly InlineUserProperty<double> order_price;
}
class TriggerOrderVM : OrderTypeVMBase<TriggerOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
InlineTriggerOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public TriggerPriceType trigger_price_type { get; set; } = TriggerPriceType.Bid;
public double order_price { get; set; } = double.NaN;
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTriggerOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineTriggerOrderProperties> inline_properties) : base(model)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p => {
if (_order_types.Count == 0 || p.child_order_types.force_override)
{
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
}
if (p.trigger_price_type.force_override)
{
trigger_price_type = p.trigger_price_type.value;
RaisePropertyChanged(nameof(trigger_price_type));
}
if (p.order_price.force_override)
{
order_price = p.order_price.value;
RaisePropertyChanged(nameof(order_price));
}
_inline_props = p;
_validate();
})
);
_validate();
}
public IEnumerable<TriggerPriceType> trigger_price_types => EnumHelper.get_values_except<TriggerPriceType>(TriggerPriceType.LastTradePrice);
public override void assign_to(CustomOrderDefinition value)
{
if (_child_order == null)
_select_child_order(model.factory.default_order_type(_security.Value, true));
_validate();
if (!HasErrors)
{
var i = factory.trigger_order();
i.TriggerCondition = new TriggerCondition()
{
PriceType = trigger_price_type,
AbsolutePriceTrigger = new AbsolutePriceTrigger() { TriggerQuantity = new TriggerQuantity() }
};
i.TriggerAction = new TriggerAction()
{
Price = order_price,
SecurityId = _security.Value.Id
};
child_order.assign_to(i.Child);
value.Trigger = i;
}
}
public double price_tick
{
get { return _security.Value.ContractSpec.LowTick; }
}
public ContractSpecificationImmutable spec
{
get { return _security.Value.ContractSpec; }
}
public ICollection<IOrderTypeVM> child_order_types
{
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _child_order_choices;
}
}
public IOrderTypeVM child_order
{
get
{
init();
return _child_order;
}
set
{
Disposable.dispose(ref _child_order_errors_sub);
_child_order = value;
_child_order?.init();
if (_child_order is INotifyDataErrorInfo ndei)
{
ndei.ErrorsChanged += on_errors_changed;
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
_validate();
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
}
_validate();
RaisePropertyChanged(nameof(child_order));
}
}
public void Dispose()
{
Disposable.dispose(ref _dispose);
if (_child_order_choices != null)
_child_order_choices.OfType<IDisposable>().DisposeAll();
}
public override void init()
{
if (_child_order == null && _security.HasValue)
_select_child_order();
_validate();
}
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
void _select_child_order(CustomOneofCase ot)
{
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
if (_child_order == null)
_child_order = child_order_types.FirstOrDefault();
RaisePropertyChanged(nameof(child_order));
}
void _validate()
{
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
set_error(nameof(child_order), string.Empty, true);
else clear_error(nameof(child_order));
}
}
}
}
+4 -3
View File
@@ -31,17 +31,18 @@ namespace mbts.spark {
_service = service;
_spreader = spreader;
_error = error;
on_orders = _service.subscribei_orders().Table().Replay(1).RefCount();
on_orders = _service.subscribei_orders().Table().Cache();
IReadOnlyDictionary<uint, OrderRecordImmutable> empty_orders = new Dictionary<uint, OrderRecordImmutable>();
working_orders =
on_orders.Scan(empty_orders, (worders, table) => _update_working_orders(worders, table)).Replay(1).RefCount();
on_fills = _service.subscribei_fills().Table().Replay(1).RefCount();
on_fills = _service.subscribei_fills().Table().Cache();
_activity = new OrderActivitySummarizer(bus, on_orders);
on_fill_tuples = FillTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders, on_fills);
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
_dispose = Disposable.Create(
on_orders.Subscribe(_security_orders_demuxer),
_bus.subscribe(SparkTags.CreateOrder).Subscribe(a => send_order(a.definition, a.price, a.size).ignore()),
_bus.subscribe(SparkTags.RecreateOrder).Subscribe(a => a.ForEach( x => send_order(x.definition, x.price, x.size).ignore())),
_bus.subscribe(SparkTags.CancelOrders).Subscribe(x => cancel_orders(x).ignore()),
_bus.subscribe(SparkTags.CrackOrders).Subscribe(x => crack_orders(x).ignore()),
_bus.subscribe(SparkTags.UpdateOrder).Subscribe(x => this.update_order(x.key, x.price, x.size).ignore())
@@ -53,7 +54,7 @@ namespace mbts.spark {
public SpreaderModel spreader => _spreader;
public IObservable<TableUpdate<OrderRecordImmutable, uint>> on_orders { get; }
public IObservable<TableUpdate<FillImmutable, string>> on_fills { get; }
public IObservable<ITableUpdateTransform<FillTuple, string>> on_fill_tuples { get; }
public IObservable<IIncrementalUpdate<FillTuple, FillTupleKey>> on_fill_tuples { get; }
public IObservable<IReadOnlyDictionary<uint,OrderRecordImmutable>> working_orders { get; }
public IObservable<ITableUpdateTransform<OrderRecordImmutable, uint>> on_security_orders(uint security) =>
@@ -75,7 +75,7 @@ namespace mbts.ui.tests
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_windows_disposed");
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
var thread_factory = new ShellThreadFactory();
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), null, new SimpleBus());
Assert.IsNotNull(Application.Current);
Assert.IsNotNull(Application.Current.Windows);
Assert.AreEqual(Application.Current.Windows.Count, 0);
@@ -100,7 +100,7 @@ namespace mbts.ui.tests
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_slow_windows_disposed");
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
var thread_factory = new ShellThreadFactory();
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), null, new SimpleBus());
Assert.AreEqual(Application.Current.Windows.Count, 0);
// an ISlowWindow gets its own thread
+2 -2
View File
@@ -177,9 +177,9 @@ namespace mbts.api.Test {
class ApiTestsNativeMethods {
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern IntPtr apitest_start_apitest_service(IntPtr context);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void apitest_stop_apitest_service(IntPtr server);
}
+2 -2
View File
@@ -175,8 +175,8 @@ namespace bts.spark
public SparkCommandParamObserver(string title, ImageSource icon, ITag<T> tag, IObservable<T> parameter, IObservable<bool> can_execute) : base(title, icon) {
_tag = tag;
_subs = Disposable.Create(
parameter.ObserveOnDispatcher().Subscribe(p => _parameter = p),
can_execute.ObserveOnDispatcher().Subscribe(ce => {
parameter.OnDispatcher().Subscribe(p => _parameter = p),
can_execute.OnDispatcher().Subscribe(ce => {
_can_execute = ce;
raiseCanExecuteChanged();
})
@@ -102,5 +102,7 @@ namespace bts.spark
SparkCommand.create("Cancel Sells", Icons.CancelSells, SparkTags.CancelOrders);
public static PartialCommand<uint> ShowModifyOrder =>
SparkCommand.create("Modify", Icons.ModifyOrder, SparkTags.ShowModifyOrderTag);
public static PartialCommand<IEnumerable<(OrderDefinitionImmutable, double, uint)>> RecreateSelectedOrders =>
SparkCommand.create("Recreate", Icons.Beaker, SparkTags.RecreateOrder);
}
}
+10 -3
View File
@@ -5,6 +5,7 @@ using bts.spark;
using mbts.ui;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static mbts.spark.SparkBusData;
namespace mbts.spark
@@ -18,6 +19,8 @@ namespace mbts.spark
#region Order
public static ITag<(OrderDefinitionImmutable definition, double price, uint size)> CreateOrder { get; } = new SparkTag<(OrderDefinitionImmutable, double, uint)>();
public static ITag<IEnumerable<(OrderDefinitionImmutable definition, double price, uint size)>> RecreateOrder { get; } = new SparkTag<IEnumerable<(OrderDefinitionImmutable, double, uint)>>();
public static ITag<IEnumerable<uint>> CancelOrders { get; } = new SparkTag<IEnumerable<uint>>();
public static ITag<IEnumerable<uint>> CrackOrders { get; } = new SparkTag<IEnumerable<uint>>();
public static ITag<(uint key, double price, uint size)> UpdateOrder { get; } = new SparkTag<(uint, double, uint)>();
@@ -34,6 +37,8 @@ namespace mbts.spark
public static ITag<(PartialCommand<SecurityMD>, SecurityCriteria)> FilterPickOne { get; } = new SparkTag<(PartialCommand<SecurityMD>, SecurityCriteria)>("FilterPick1");
public static ITag<IShellWindow> ShowWindow { get; } = new SparkTag<IShellWindow>("Show Window");
public static ITag<Task<IShellWindow>> ShowWindowAsync { get; } = new SparkTag<Task<IShellWindow>>("ShowWindowAsync");
public static ITag<IShellWindow> ActivateWindow { get; } = new SparkTag<IShellWindow>("Activate Window");
public static ITag<Nothing> ShowWorkspaces { get; } = new SparkTag<Nothing>(nameof(ShowWorkspaces));
public static ITag<Nothing> ShowSettings { get; } = new SparkTag<Nothing>(nameof(ShowSettings));
public static ITag<Uri> ShowUri { get; } = new SparkTag<Uri>("ShowUri");
@@ -64,9 +69,11 @@ namespace mbts.spark
public static ITag<SecurityCriteria> ShowTimeAndSalesTag { get; } = new SparkTag<SecurityCriteria>("Show TimeAndSales");
#endregion
#region Persist
public static ITag<PersistMessage> PersistCommandTag { get; } = new SparkTag<PersistMessage>("PersistCommandTag");
public static ITag<PersistedItems> PersistedItemsTag { get; } = new SparkTag<PersistedItems>("PersistedItemsTag");
#region Ladder
public static ITag<Nothing> RequestRecenterLadder { get; } = new SparkTag<Nothing>(nameof(RequestRecenterLadder));
public static ITag<DateTime> RecenterLadderBegin { get; } = new SparkTag<DateTime>(nameof(RecenterLadderBegin));
public static ITag<DateTime> RecenterLadderNow { get; } = new SparkTag<DateTime>(nameof(RecenterLadderNow));
#endregion
public static ITag<uint> SyntheticDeleteTag { get; } = new SparkTag<uint>(nameof(SyntheticDeleteTag));
@@ -6,6 +6,7 @@ using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using bts.api.core;
using IronPython.Runtime.Types;
using mbts;
@@ -49,6 +50,9 @@ namespace bts.api.diagnostic {
readonly StringBuilder _pending = new StringBuilder();
readonly OutputPublisher _out;
readonly Thread _thread;
Dispatcher _dispatcher;
public ShellScriptServer(ServiceContext context, ShellDiagnosticsServiceServer parent, string qualifier) : base(context, qualifier) {
_parent = parent;
_out = make_output_publisher();
@@ -59,11 +63,19 @@ namespace bts.api.diagnostic {
_scope.SetVariable("shell_objects", new Func<object,object>(o => get_exports(o,null)));
_scope.SetVariable("shell_named_object", new Func<object,string,object>(get_export));
_scope.SetVariable("shell_named_objects", new Func<object,string,object>(get_exports));
_thread = new Thread(_ => {
_dispatcher = Dispatcher.CurrentDispatcher;
Thread.CurrentThread.Name = $"ShellScriptServer-{qualifier}";
Dispatcher.Run();
});
}
public override Task<VoidMessage> dispose(VoidMessage request) {
log("Finished", true);
_parent.terminate(this);
_dispatcher.BeginInvokeShutdown(DispatcherPriority.ApplicationIdle);
return Task.FromResult(new VoidMessage());
}
@@ -124,45 +136,58 @@ namespace bts.api.diagnostic {
});
}
Task<ScriptResultMessage> run_code(ScriptSource code) {
ScriptResultMessage run_code(ScriptSource code) {
try {
object result = code.Execute(_scope);
return Task.FromResult(new ScriptResultMessage { Res = ScriptResult.Complete, Message = (result?.ToString()) ?? "" });
return new ScriptResultMessage { Res = ScriptResult.Complete, Message = (result?.ToString()) ?? "" };
}
catch (Exception e) {
//log($"Exception: {e.Message}");
return Task.FromResult(new ScriptResultMessage { Res = ScriptResult.RunError, Message = e.Message });
return (new ScriptResultMessage { Res = ScriptResult.RunError, Message = e.Message });
}
}
public override Task<ScriptResultMessage> run(ScriptLines request) {
// append lines to _pending
foreach (var line in request.Lines) {
_pending.AppendLine(line);
}
// CreateScriptSource from pending
var code = _engine.CreateScriptSourceFromString(_pending.ToString().TrimStart(' ', '\n', '\r'), SourceCodeKind.InteractiveCode);
var parse_result = code.GetCodeProperties();
if (parse_result == ScriptCodeParseResult.IncompleteStatement && code.GetCode().EndsWith(Environment.NewLine+Environment.NewLine)) {
parse_result = Microsoft.Scripting.ScriptCodeParseResult.Complete;
}
// todo: If code is 'incomplete' but we have 2 blank lines run it!
switch (parse_result) {
// If 'complete' run code
case ScriptCodeParseResult.Complete:
_pending.Clear();
return run_code(code);
// if 'invalid' return error
case ScriptCodeParseResult.Invalid:
Console.WriteLine("Invalid");
_pending.Clear();
return Task.FromResult(new ScriptResultMessage { Res = ScriptResult.Invalid, Message = code.GetCode() });
// otherwise continue buffering
default:
Console.WriteLine("Incomplete");
return Task.FromResult(new ScriptResultMessage { Res = ScriptResult.Incomplete });
var tcs = new TaskCompletionSource<ScriptResultMessage>();
void ret(ScriptResultMessage r) {
tcs.TrySetResult(r);
}
_dispatcher.BeginInvoke(() => {
// append lines to _pending
foreach (var line in request.Lines) {
_pending.AppendLine(line);
}
// CreateScriptSource from pending
var code = _engine.CreateScriptSourceFromString(_pending.ToString().TrimStart(' ', '\n', '\r'), SourceCodeKind.InteractiveCode);
var parse_result = code.GetCodeProperties();
if (parse_result == ScriptCodeParseResult.IncompleteStatement && code.GetCode().EndsWith(Environment.NewLine+Environment.NewLine)) {
parse_result = Microsoft.Scripting.ScriptCodeParseResult.Complete;
}
// todo: If code is 'incomplete' but we have 2 blank lines run it!
switch (parse_result) {
// If 'complete' run code
case ScriptCodeParseResult.Complete:
_pending.Clear();
ret(run_code(code));
break;
// if 'invalid' return error
case ScriptCodeParseResult.Invalid:
Console.WriteLine("Invalid");
_pending.Clear();
ret(new ScriptResultMessage { Res = ScriptResult.Invalid, Message = code.GetCode() });
break;
// otherwise continue buffering
default:
Console.WriteLine("Incomplete");
ret(new ScriptResultMessage { Res = ScriptResult.Incomplete });
break;
}
});
return tcs.Task;
}
}
}
@@ -199,10 +199,10 @@ namespace mbts.ui {
public static BTSColor straddle_theovsmarket_positive { get { return BTSCoreColors.green_light; } }
// Order Types
public static BTSColor tick_order { get { return new BTSColor(31, 4, 57);}}
public static BTSColor lean_order { get { return new BTSColor(110, 77, 143);}}
public static BTSColor join_order { get { return new BTSColor(15, 1, 29);}}
public static BTSColor leader_order { get { return new BTSColor(40, 9, 72);}}
public static BTSColor tick_order { get { return BTSColors.purple.rgb_color();}}
public static BTSColor lean_order { get { return BTSColors.purple.rgb_color();}}
public static BTSColor join_order { get { return BTSColors.purple.rgb_color();}}
public static BTSColor top_order { get { return BTSColors.purple.rgb_color();}}
}
public static class AW2 {
@@ -48,10 +48,10 @@ namespace System.Collections.Generic {
return rv;
}
public static IEnumerable<T> ToIEnumerable<T>(this T item) {
yield return item;
}
// use "new [] { item }" - avoid putting this on the intellisense for every item everywhere
//public static IEnumerable<T> ToIEnumerable<T>(this T item) {
// yield return item;
//}
public static IEnumerable<T> TakeWhile<T, TAccumulate>(this IEnumerable<T> source, TAccumulate seed, Func<TAccumulate, T, TAccumulate> accumulate, Func<TAccumulate, T, bool> predicate) {
TAccumulate a = seed;
@@ -345,6 +345,17 @@ namespace System.Reactive.Linq
public static async Task<(TResult result, bool success)> FirstAsyncTask<T, TResult>(this IObservable<T> source, Func<T, bool> predicate, Func<T, TResult> selector, TimeSpan timeout) where T : class =>
await source.Where(predicate).Select(val => (selector(val), true)).Merge(Observable.Return((default(TResult), false)).Delay(timeout)).FirstAsync();
// This is placed here to intentionally make ObserveOnDispatcher ambiguous with the one provided by Rx
public static IObservable<T> ObserveOnDispatcher<T>(this IObservable<T> source) {
throw new InvalidOperationException("This method can starve the UI of input messages. Please use OnDispatcher() instead");
}
public static IObservable<T> OnDispatcher<T>(this IObservable<T> source) {
return DispatcherObservable.ObserveOnDispatcher(source, DispatcherPriority.Background);
}
}
}
@@ -54,5 +54,33 @@
rv |= try_convert(args[1], out arg2);
return rv;
}
public static bool try_convert_args<T1, T2, T3>(object[] args, out T1 arg1, out T2 arg2, out T3 arg3) {
if (args == null || args.Length != 3) {
arg1 = default;
arg2 = default;
arg3 = default;
return false;
}
bool rv = try_convert(args[0], out arg1);
rv |= try_convert(args[1], out arg2);
rv |= try_convert(args[2], out arg3);
return rv;
}
public static bool try_convert_args<T1, T2, T3, T4>(object[] args, out T1 arg1, out T2 arg2, out T3 arg3, out T4 arg4) {
if (args == null || args.Length != 4) {
arg1 = default;
arg2 = default;
arg3 = default;
arg4 = default;
return false;
}
bool rv = try_convert(args[0], out arg1);
rv |= try_convert(args[1], out arg2);
rv |= try_convert(args[2], out arg3);
rv |= try_convert(args[3], out arg4);
return rv;
}
}
}
@@ -17,8 +17,10 @@ namespace mbts {
public static double tick_unit_frac(this TickFactors tick) =>
tick.tick_unit / tick.tick_base;
public static double reduced_base(this TickFactors tick) => factor_out(tick.tick_base, 10);
public static string format(this TickFactors tick, double value) =>
format(tick, value, double.NaN, true, _nan_text);
format(tick, value, double.NaN, false, _nan_text);
public static string format(this TickFactors tick, double value, bool show_commas) =>
format(tick, value, double.NaN, show_commas, _nan_text);
@@ -176,9 +178,10 @@ namespace mbts {
if (DoubleHelper.IsNaNOrInfinity(input)) return input;
return input <= regional_tick.low_tick_inclusive_end ? regional_tick.low_tick.round(input) : regional_tick.high_tick.round(input);
}
public static int display_digits(this TickFactors tick) => display_digits(tick.tick_base);
#endregion
public static int n_display_digits(double tick_base) {
public static int display_digits(double tick_base) {
double log_tick_base = Math.Log10(tick_base);
int floor = (int)Math.Floor(log_tick_base);
double dec = log_tick_base - floor;
@@ -216,7 +219,7 @@ namespace mbts {
if (numerator_part >= tick_base) numerator_part /= Math.Pow(10, Math.Ceiling(Math.Log10(numerator_part) - Math.Log10(tick_base)));
else {
//e.g [tick_base 640] 1989-8 == 1989-80, or 1989-32 == 1989-320
int ndd_diff = n_display_digits(tick_base) - numerator_text.Length;
int ndd_diff = display_digits(tick_base) - numerator_text.Length;
if (ndd_diff > 0) numerator_part *= Math.Pow(10, ndd_diff);
if (numerator_part >= tick_base) numerator_part /= 10;
}
@@ -255,16 +258,21 @@ namespace mbts {
double numerator_dec = value - integer_part;
double unit_frac = tick.tick_unit / tick.tick_base;
int n_units = (int)Math.Round(numerator_dec / unit_frac);
if (n_units == tick.tick_base) return (integer_part + 1).ToString() + tick.separator() + new string('0', n_display_digits(tick.tick_base));
if (n_units == tick.tick_base) return (integer_part + 1).ToString() + tick.separator() + new string('0', display_digits(tick.tick_base));
double decimal_part = n_units * tick.tick_unit;
if (decimal_part == tick.tick_base) {
++integer_part;
decimal_part = 0;
} else {
// some ticks are not formatted with 100% precision, using less than the required display_digits
// for such cases, we need to truncate the decimal part rather than round it.
decimal_part = Math.Floor(decimal_part);
}
string integer_part_text = show_commas ? integer_part.ToString("#,0") : integer_part.ToString();
return integer_part_text + tick.separator() + decimal_part.ToString(new string('0', n_display_digits(tick.tick_base)));
return integer_part_text + tick.separator() + decimal_part.ToString(new string('0', display_digits(tick.tick_base)));
}
} catch (OverflowException ex) {
}
catch (OverflowException ex) {
Logger.warn(bts.log.Subsystem.Ui, $"Arithmetic overflow exception in format_tick (tick:{tick}, value:{value}):{ex}");
return "Overflow";
}
@@ -272,7 +280,7 @@ namespace mbts {
private static double _round_tick(double tick_unit_frac, double input) {
return input - Math.IEEERemainder(input, tick_unit_frac);
}
}
#endregion
}
}
@@ -8,10 +8,10 @@ namespace mbts.ui.native
{
public static class BTSUINativeMethods
{
[DllImport("spark_lib")]
[DllImport("libspark")]
public static extern void enable_limits();
[DllImport("spark_lib", CharSet = CharSet.Unicode)]
[DllImport("libspark", CharSet = CharSet.Unicode)]
public static extern void set_window_app_id(IntPtr hwnd, string stack_id);
}
}
@@ -69,16 +69,18 @@ namespace mbts.ui.settings
if (window == null || window.Owner == null) return null;
MemoryStream out_stream = new MemoryStream();
window.GetState(out_stream);
var pos = _interop.GetPosition(window);
var state = new WindowState() {
owner = window.Owner.key(),
app_data = out_stream.ToArray(),
position = _interop.GetPosition(window),
title = window.Title
position = pos,
title = string.IsNullOrEmpty(pos.custom_title) ? window.Title : pos.custom_title
};
return state;
}
catch (Exception e) {
Logger.log(bts.log.Level.Error, bts.log.Subsystem.Ui, "Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
Logger.log(Level.Error, Subsystem.Ui, "Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
}
return null;
}
@@ -12,6 +12,9 @@ using bts.api.persistence;
namespace mbts.ui.settings
{
//
// This class is used for tools like SettingsManager.exe
//
[Export(typeof(IUserPersistFactory))]
class UISettingsFactory : IUserPersistFactory
{
@@ -34,7 +37,7 @@ namespace mbts.ui.settings
public IUserPersist get_persist_for(string name) {
var user = _users.Result.FirstOrDefault(u => u.Login == name);
return new UserUISettings(user.Id, _persist);
return new UserUISettings(user.Id, _persist, null);
}
}
}
@@ -39,6 +39,9 @@ namespace mbts.ui.shell {
[ProtoMember(102), DefaultValue(true)]
public bool always_on_top = true;
//[ProtoMember(103)] //used
[ProtoMember(104)]
public string custom_title;
}
[ProtoContract]
@@ -58,7 +61,7 @@ namespace mbts.ui.shell {
public WindowPosition position;
[ProtoMember(9)]
public string title;
public string title; // for debugging
}
[ProtoContract]
@@ -23,6 +23,9 @@ namespace mbts.ui
Task<IShellWindow> create(Stream load_stream);
}
// ILazyModule may also impement IShellMenu to get opening/closing notification
public interface ILazyMenuModule : IShellModule { }
public interface IPersistShellModule : IShellModule {
// Called at startup before the UI is shown
// This is an opportunity for you to kick off background services
@@ -17,5 +17,5 @@ namespace mbts.ui
WindowPosition GetPosition(IShellWindow window);
bool IsVisible(IShellWindow window);
void PinWindow(IShellWindow window);
}
}
}
@@ -10,6 +10,7 @@ using bts.api.core;
using bts.api.persistence;
using System.Text.RegularExpressions;
using System.Reactive.Linq;
using bts.spark;
namespace mbts.persist
{
@@ -18,11 +19,13 @@ namespace mbts.persist
protected abstract string user();
string uns(string ns) { return $"{user()}#{ns}"; }
PersistenceService _persist;
ErrorReporter _errors;
private const string _settings_change_base = "/ui/settingschange/";
public UISettingsBase(PersistenceService persist) {
public UISettingsBase(PersistenceService persist, ErrorReporter errors) {
_persist = persist;
_errors = errors;
}
@@ -50,7 +53,7 @@ namespace mbts.persist
Ns = uns(ns),
Key = { key }
});
check_error(ret.value.Result, ns, key, out bool empty);
check_error(ret, ns, key, out bool empty);
if (empty) return default(T);
return deserialize<T>(ret.value.Record.Single().Value);
}
@@ -67,20 +70,28 @@ namespace mbts.persist
return ProtoBuf.Serializer.DeserializeWithLengthPrefix<T>(stream, ProtoBuf.PrefixStyle.Fixed32);
}
private void check_error(RetrieveResponse.Types.Result result, string ns, string key, out bool empty) {
switch (result) {
case RetrieveResponse.Types.Result.EmptyNs:
case RetrieveResponse.Types.Result.NoSuchNs:
case RetrieveResponse.Types.Result.NoSuchKey:
empty = true;
return;
case RetrieveResponse.Types.Result.EmptyKey:
case RetrieveResponse.Types.Result.DuplicateKey:
throw new ArgumentException($"Invalid Key {result} '{key}'");
default:
case RetrieveResponse.Types.Result.Ok:
empty = false;
return;
private void check_error(Result<RetrieveResponse> result, string ns, string key, out bool empty) {
if (result.success) {
switch (result.value.Result) {
case RetrieveResponse.Types.Result.EmptyNs:
case RetrieveResponse.Types.Result.NoSuchNs:
case RetrieveResponse.Types.Result.NoSuchKey:
empty = true;
return;
case RetrieveResponse.Types.Result.EmptyKey:
case RetrieveResponse.Types.Result.DuplicateKey:
_errors?.report_error($"Invalid Key {result} '{key}'", "Requested Invalid Settings Key", System.Windows.MessageBoxImage.Information);
empty = true;
return;
default:
case RetrieveResponse.Types.Result.Ok:
empty = false;
return;
}
}
else {
_errors?.report_error($"There was an error communicating with the settings server: '{result.error_message}'", "Failed to retrieve settings from the server");
empty = true;
}
}
@@ -90,7 +101,7 @@ namespace mbts.persist
new RetrieveAllRequest() {
Ns = uns(ns)
});
check_error(results.value.Result, ns, "", out bool empty);
check_error(results, ns, "", out bool empty);
if (empty) return Enumerable.Empty<PersistResult<T>>();
return results.value.Record
.Where(r => re.IsMatch(r.Key))
@@ -131,7 +142,7 @@ namespace mbts.persist
protected override string user() { return "all_users"; }
[ImportingConstructor]
public UISettings([Import("settings")]PersistenceService p) : base(p) { }
public UISettings([Import("settings")]PersistenceService p, ErrorReporter err) : base(p,err) { }
}
[Export(typeof(IUserPersist))]
@@ -140,8 +151,8 @@ namespace mbts.persist
string _user;
[ImportingConstructor]
public UserUISettings([Import("userid")]uint user, [Import("settings")]PersistenceService persist)
: base(persist) {
public UserUISettings([Import("userid")]uint user, [Import("settings")]PersistenceService persist, ErrorReporter err)
: base(persist,err) {
_user = $"User {user}";
}
@@ -1,8 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace mbts.utils {
public static class EqualityComparer {
const int _hash_seed = 63607, _hash_multiplier = 6451;
public static IEqualityComparer<T> Create<T>(Func<T, T, bool> equals) =>
new AnonymousEqualityComparer<T>(equals);
@@ -23,12 +26,40 @@ namespace mbts.utils {
public static IEqualityComparer<T?> CreateNullable<T>(Func<T, T, bool> equals) where T : struct =>
new AnonymousEqualityComparer<T?>((a, b) => a.HasValue ? b.HasValue && equals(a.Value, b.Value) : !b.HasValue);
public static IEqualityComparer to_non_generic<T>(this IEqualityComparer<T> comparer) =>
new AnonymousEqualityComparer<T>(comparer.Equals, comparer.GetHashCode);
public static bool NullCheckEquals<T>(T x, T y, Func<T, T, bool> non_null_equals) where T : class, IEquatable<T> =>
x == null ? y == null :
y != null &&
non_null_equals(x, y);
private class AnonymousEqualityComparer<T> : IEqualityComparer<T> {
public static int aggregate_hash(int item1_hash, int item2_hash, int item3_hash) {
int rv = _hash_seed;
unchecked {
_accumulate_hash(ref rv, ref item1_hash);
_accumulate_hash(ref rv, ref item2_hash);
_accumulate_hash(ref rv, ref item3_hash);
}
return rv;
}
// Note: be wary of the performance cost this method may have, as frequently creating large arrays can be expensive.
// If you wish to avoid this cost, consider making an overload like the method above, with the appropriate number of item-hash arguments
public static int aggregate_hash(params int[] item_hashes) {
int hash = _hash_seed;
unchecked {
foreach (int item_hash in item_hashes)
hash = hash * _hash_multiplier + item_hash;
}
return hash;
}
// WARNING: be sure to call this within in an unchecked scope
static void _accumulate_hash(ref int aggregate_hash, ref int item_hash) =>
aggregate_hash = aggregate_hash * _hash_multiplier + item_hash;
private class AnonymousEqualityComparer<T> : IEqualityComparer<T>, IEqualityComparer {
readonly Func<T, T, bool> _equals;
readonly Func<T, int> _get_hash_code;
@@ -40,6 +71,9 @@ namespace mbts.utils {
public bool Equals(T x, T y) => _equals(x, y);
public int GetHashCode(T obj) => _get_hash_code(obj);
bool IEqualityComparer.Equals(object x, object y) => x is T xt && y is T yt && _equals(xt, yt);
int IEqualityComparer.GetHashCode(object obj) => throw new NotImplementedException();
}
}
}
+20 -1
View File
@@ -1,4 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace mbts.utils {
public static class CompareUtils {
@@ -19,5 +21,22 @@ namespace mbts.utils {
b.HasValue ? compare(a.Value, b.Value) : 1 :
b.HasValue ? -1 : 0;
#endregion
#region Comparer
public static IComparer to_non_generic<T>(this IComparer<T> comparer) =>
new NonGenericComparerWrapper<T>(comparer);
public static IComparer<TOutter> wrap<TInner, TOutter>(this IComparer<TInner> inner_comparer, Func<TOutter, TInner> selector) =>
Comparer<TOutter>.Create((a, b) => inner_comparer.Compare(a == null ? default : selector(a), b == null ? default : selector(b)));
class NonGenericComparerWrapper<T> : IComparer {
readonly IComparer<T> _comparer;
public NonGenericComparerWrapper(IComparer<T> comparer) => _comparer = comparer;
public int Compare(object x, object y) =>
_comparer.Compare(ObjectHelper.try_convert<T>(x), ObjectHelper.try_convert<T>(y));
}
#endregion
}
}
}
@@ -0,0 +1,6 @@
namespace mbts.spark {
interface IValueFactoryVM<T> {
T create();
void load_from(T value);
}
}
@@ -2,9 +2,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
namespace mbts.utils {
interface IObservableDemuxer<TKey, in TInput, out TOutput> : IObserver<TInput> {
@@ -89,21 +89,17 @@ namespace mbts.utils {
_group_key = group_key;
}
public void OnCompleted() { }
public void OnError(Exception error) { }
public IObservable<ITableUpdateTransform<T, TKey>> subscribe(TGroupKey group_key) =>
Observable.Create<ITableUpdateTransform<T, TKey>>(o => {
var data = _map.GetOrAdd(group_key, gk => new Data());
lock (data) {
var current = data.current;
if (current.state.Count > 0) {
var state_clone = current.state.ToDictionary();
o.OnNext(new TableUpdateTransform<T, TKey>(state_clone, state_clone.Values.ToList(), new List<T>(0)));
}
using (data.read()) {
o.OnNext(data.current.as_snapshot());
return data.subject.Subscribe(o);
}
return data.subject.Subscribe(o);
});
public void OnCompleted() { }
public void OnError(Exception error) { }
public void OnNext(TableUpdate<T, TKey> table) {
var changes = new Dictionary<TGroupKey, ChangeSet>();
foreach (T value in table.added)
@@ -116,16 +112,29 @@ namespace mbts.utils {
}
Data _update(Data data, ChangeSet changes) {
lock (data) {
data.current = data.current.update(changes.removed, changes.added, _key);
data.subject.OnNext(data.current);
}
using (data.write())
data.subject.OnNext(data.current = data.current.update(changes.removed, changes.added, _key));
return data;
}
class Data {
int _n_read_ops, _n_write_ops;
public readonly Subject<TableUpdateTransform<T, TKey>> subject = new Subject<TableUpdateTransform<T, TKey>>();
public TableUpdateTransform<T, TKey> current = new TableUpdateTransform<T, TKey>();
public IDisposable read() {
while (_n_write_ops > 0)
Thread.Yield();
Interlocked.Increment(ref _n_read_ops);
return Disposable.Create(() => Interlocked.Decrement(ref _n_read_ops));
}
public IDisposable write() {
while (_n_read_ops > 0)
Thread.Yield();
Interlocked.Increment(ref _n_write_ops);
return Disposable.Create(() => Interlocked.Decrement(ref _n_write_ops));
}
}
// [John]I prefer this class over anonymous tuples bc it is easy to mix up Item1/Item2
@@ -7,12 +7,30 @@ using System.Linq;
using System.Reactive.Linq;
namespace mbts.utils {
interface IIncrementalUpdate<T, TKey> where TKey : IEquatable<TKey> {
IReadOnlyDictionary<TKey, T> added { get; }
IReadOnlyDictionary<TKey, T> removed { get; }
}
interface ITableUpdateTransform<T, TKey> where TKey : IEquatable<TKey> {
IReadOnlyDictionary<TKey, T> state { get; }
IReadOnlyList<T> added { get; }
IReadOnlyList<T> removed { get; }
}
class IncrementalUpdate<T, TKey> : IIncrementalUpdate<T, TKey> where TKey : IEquatable<TKey> {
public IncrementalUpdate()
: this(new Dictionary<TKey, T>(0), new Dictionary<TKey, T>(0)) { }
public IncrementalUpdate(IReadOnlyDictionary<TKey, T> added, IReadOnlyDictionary<TKey, T> removed) {
this.added = added;
this.removed = removed;
}
public IReadOnlyDictionary<TKey, T> added { get; }
public IReadOnlyDictionary<TKey, T> removed { get; }
}
class TableUpdateTransform<T, TKey> : ITableUpdateTransform<T, TKey> where TKey : IEquatable<TKey> {
readonly Dictionary<TKey, T> _state;
readonly IReadOnlyList<T> _added, _removed;
@@ -44,6 +62,18 @@ namespace mbts.utils {
table._state.Add(key(item), item);
return table;
}
public TableUpdateTransform<T, TKey> update(IIncrementalUpdate<T, TKey> update, Func<T, TKey> key) {
var table = new TableUpdateTransform<T, TKey>(new Dictionary<TKey, T>(_state), added, removed);
foreach (var k in update.removed.Keys)
table._state.Remove(k);
foreach (var kv in update.added)
table._state.Add(kv.Key, kv.Value);
return table;
}
internal TableUpdateTransform<T, TKey> as_snapshot() =>
new TableUpdateTransform<T, TKey>(_state, _state.Values.ToList(), new T[0]);
}
[Browsable(false)]
@@ -58,6 +88,15 @@ namespace mbts.utils {
.Select(u => new FilteredTableUpdateTransform<T, TKey>(u, predicate))
.Where(u => u.added.Count > 0 || u.removed.Count > 0);
/// <summary>
/// Makes a 1:1 conversion to an IncrementalUpdate sequence of TResult (TKey remains the same).
/// </summary>
public static IObservable<IIncrementalUpdate<TResult, TKey>> convert<T, TKey, TResult>(this IObservable<IIncrementalUpdate<T, TKey>> source, Func<T, TResult> selector)
where TKey : IEquatable<TKey> =>
source.Select(update => new IncrementalUpdate<TResult, TKey>(
update.added.ToDictionary(kv => kv.Key, kv => selector(kv.Value)),
update.removed.ToDictionary(kv => kv.Key, kv => selector(kv.Value))));
/// <summary>
/// Converts sequence to show incremental changes with respect to the [TResult] value returned by result_selector.
/// </summary>
@@ -69,6 +108,33 @@ namespace mbts.utils {
source.Scan(new PivotTableUpdateTransform<T, TKey, TResult, TResultKey>(selector, result_key, key),
(pivot, table) => pivot.update(table));
/// <summary>
/// Converts sequence to show incremental changes with respect to the [TResult] value returned by result_selector.
/// </summary>
public static IObservable<IIncrementalUpdate<TResult, TResultKey>> with_respect_to<T, TKey, TResult, TResultKey>(
this IObservable<IIncrementalUpdate<T, TKey>> source, Func<T, TResult> selector, Func<TResult, TResultKey> result_key, Func<T, TKey> key)
where TKey : IEquatable<TKey>
where TResultKey : IEquatable<TResultKey> =>
//
source.Scan(new PivotIncrementalUpdate<T, TKey, TResult, TResultKey>(selector, result_key, key),
(pivot, table) => pivot.update(table));
/// <summary>
/// Uses a Scan operator to accumulate state in the resulting ITableUpdateTransform sequence.
/// </summary>
public static IObservable<ITableUpdateTransform<T, TKey>> table<T, TKey>(this IObservable<IIncrementalUpdate<T, TKey>> source, Func<T, TKey> key) where TKey : IEquatable<TKey> =>
source.Scan(
new TableUpdateTransform<T, TKey>(),
(table, update) => table.update(update, key));
/// <summary>
/// Filters the incremental added/removed lists by a predicate. Observers are only notified when the filtered added/removed lists have elements.
/// </summary>
public static IObservable<IIncrementalUpdate<T, TKey>> filter<T, TKey>(this IObservable<IIncrementalUpdate<T, TKey>> source, Func<T, bool> predicate)
where TKey : IEquatable<TKey> => source
.Select(u => new IncrementalUpdate<T, TKey>(u.added.Where(kv => predicate(kv.Value)).ToDictionary(), u.removed.Where(kv => predicate(kv.Value)).ToDictionary()))
.Where(u => u.added.Count > 0 || u.removed.Count > 0);
class FilteredTableUpdateTransform<T, TKey> : ITableUpdateTransform<T, TKey> where TKey : IEquatable<TKey> {
internal FilteredTableUpdateTransform(ITableUpdateTransform<T, TKey> source, Func<T, bool> predicate) {
state = source.state;
@@ -81,6 +147,57 @@ namespace mbts.utils {
public IReadOnlyList<T> removed { get; }
}
class PivotIncrementalUpdate<T, TKey, TResult, TResultKey> : IIncrementalUpdate<TResult, TResultKey> where TKey : IEquatable<TKey> where TResultKey : IEquatable<TResultKey> {
readonly Dictionary<TResultKey, TResult> _state;
readonly SetMap<TResultKey, TKey> _key_map;
readonly Func<T, TResult> _selector;
readonly Func<TResult, TResultKey> _result_key;
readonly Func<T, TKey> _key;
internal PivotIncrementalUpdate(Func<T, TResult> selector, Func<TResult, TResultKey> result_key, Func<T, TKey> key) :
this(new Dictionary<TResultKey, TResult>(), new Dictionary<TResultKey, TResult>(0), new Dictionary<TResultKey, TResult>(0), new SetMap<TResultKey, TKey>(), selector, result_key, key) { }
PivotIncrementalUpdate(Dictionary<TResultKey, TResult> state, IReadOnlyDictionary<TResultKey, TResult> added, IReadOnlyDictionary<TResultKey, TResult> removed,
SetMap<TResultKey, TKey> key_map, Func<T, TResult> selector, Func<TResult, TResultKey> result_key, Func<T, TKey> key) {
//
_state = state;
this.added = added;
this.removed = removed;
_key_map = key_map;
_selector = selector;
_result_key = result_key;
_key = key;
}
public IReadOnlyDictionary<TResultKey, TResult> state => _state;
public IReadOnlyDictionary<TResultKey, TResult> added { get; }
public IReadOnlyDictionary<TResultKey, TResult> removed { get; }
public PivotIncrementalUpdate<T, TKey, TResult, TResultKey> update(IIncrementalUpdate<T, TKey> table) {
Dictionary<TResultKey, TResult> removed = new Dictionary<TResultKey, TResult>(), added = new Dictionary<TResultKey, TResult>();
var state_clone = new Dictionary<TResultKey, TResult>(_state);
foreach (var kv in table.removed) {
var result = _selector(kv.Value);
var rkey = _result_key(result);
if (_key_map.removes_key(rkey, kv.Key))
removed.Add(rkey, result);
}
foreach (var kv in table.added) {
var result = _selector(kv.Value);
var rkey = _result_key(result);
if (_key_map.adds_key(rkey, kv.Key)) {
if (!removed.Remove(rkey))
added.Add(rkey, result);
}
}
foreach (var k in removed.Keys)
state_clone.Remove(k);
foreach (var kv in added)
state_clone.Add(kv.Key, kv.Value);
return new PivotIncrementalUpdate<T, TKey, TResult, TResultKey>(state_clone, added, removed, _key_map, _selector, _result_key, _key);
}
}
class PivotTableUpdateTransform<T, TKey, TResult, TResultKey> : ITableUpdateTransform<TResult, TResultKey> where TKey : IEquatable<TKey> where TResultKey : IEquatable<TResultKey> {
readonly Dictionary<TResultKey, TResult> _state;
readonly SetMap<TResultKey, TKey> _key_map;
@@ -471,7 +471,7 @@ namespace mbts.ui.shell
protected Property<T> backing<T, TData>(string name, IObservable<TData> source, Func<TData, T> select, T default_value = default) {
if (_observable_properties == null) _observable_properties = new List<IDisposable>();
var prop = new ObservablePropertySink<T>(this, name, default_value);
_observable_properties.Add(source.ObserveOnDispatcher().Subscribe(d => prop.set(select(d))));
_observable_properties.Add(source.OnDispatcher().Subscribe(d => prop.set(select(d))));
return prop;
}
@@ -526,8 +526,8 @@ namespace mbts.ui.shell
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, Func<T, T, bool> items_match, IReadOnlyList<PropertyValidation<T>> validation_steps) {
if (_observable_properties == null) _observable_properties = new List<IDisposable>();
var prop = new SelectorProperty<T>(this, name, select_default, items_match, validation_steps);
_observable_properties.Add(items.ObserveOnDispatcher().Subscribe(prop));
_observable_properties.Add(desired_item.ObserveOnDispatcher().Subscribe(prop));
_observable_properties.Add(items.OnDispatcher().Subscribe(prop));
_observable_properties.Add(desired_item.OnDispatcher().Subscribe(prop));
return prop;
}
+191 -112
View File
@@ -1,141 +1,220 @@
using mbts.spark;
using mbts.ui.extensions;
using bts.api;
using mbts.spark;
using mbts.ui.utility.text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Data;
using System.Windows.Media;
using static mbts.config.Configuration;
using FontFamily = System.Windows.Media.FontFamily;
namespace mbts.ui.controls {
public class Badge: Control {
readonly static double _dpi = get<bts.config.System>().dpi_ratio;
public class Badge : Control {
public const double LINE_SPACING = 1;
// Storing the defaults here for readability
// Warning: these properties are used to calculate the ladder's row_height. Please change the values below if you want to change properties that affect the layout.
// ** - value is not the actual PropertyMetadata.DefaultValue
public static readonly Thickness
DefaultMargin = new Thickness(4, 2, 4, 2), // **
DefaultPadding = new Thickness(1),
DefaultBorderThickness = new Thickness(2);
public static readonly double
DefaultCornerRadius = 1,
DefaultFontSize = 10,
DefaultWidth = 63; // **
public static readonly FontFamily DefaultFontFamily = new FontFamily("Consolas");
readonly GlyphContentRect _gcr = new GlyphContentRect();
Brush _background, _foreground, _border_brush;
Typeface _face;
FormattedText _center_text;
FormattedText _top_text;
FormattedText _bottom_text;
static Badge() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(typeof(Badge)));
WidthProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(15.0));
HeightProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(10.0));
FontSizeProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender /*| FrameworkPropertyMetadataOptions.AffectsMeasure*/, update_text));
ForegroundProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(Brushes.White, update_text));
BackgroundProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender));
BorderThicknessProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(new Thickness(1)));
BorderBrushProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(Brushes.Black));
FontFamilyProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(new FontFamily("Consolas"), FrameworkPropertyMetadataOptions.AffectsRender /*| FrameworkPropertyMetadataOptions.AffectsMeasure*/, update_text));
//TextProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, update_text));
//TopTextProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, update_text));
//BottomTextProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, update_text));
// MinWidth
WPFHelper.override_metadata<double, Badge>(
MinWidthProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.min_width = v,
0);
// Width
WPFHelper.override_metadata<double, Badge>(
WidthProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.width = v,
double.NaN);
// Max Width
WPFHelper.override_metadata<double, Badge>(
MaxWidthProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.max_width = v,
double.MaxValue);
// Min Height
WPFHelper.override_metadata<double, Badge>(
MinHeightProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.min_height = v,
0);
// Height
WPFHelper.override_metadata<double, Badge>(
HeightProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.height = v,
double.NaN);
// Max Height
WPFHelper.override_metadata<double, Badge>(
MaxHeightProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.max_height = v,
double.MaxValue);
// FontSize
WPFHelper.override_metadata<double, Badge>(
FontSizeProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.font_size = v,
DefaultFontSize);
// Foreground
WPFHelper.override_metadata<Brush, Badge>(
ForegroundProperty,
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._foreground = v,
Brushes.White);
// Background
WPFHelper.override_metadata<Brush, Badge>(
BackgroundProperty,
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._background = v,
Brushes.Transparent);
// BorderThickness
WPFHelper.override_metadata<Thickness, Badge>(
BorderThicknessProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.border_thickness = v,
DefaultBorderThickness);
// BorderBrush
WPFHelper.override_metadata<Brush, Badge>(
BorderBrushProperty,
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._border_brush = v,
Brushes.Black);
// FontFamily
WPFHelper.override_metadata<FontFamily, Badge>(
FontFamilyProperty,
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.glyph_context = b._gcr.glyph_context.with(v),
DefaultFontFamily);
// HoriontalAlignment
WPFHelper.override_metadata<HorizontalAlignment, Badge>(
HorizontalAlignmentProperty,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsRender,
(o, v) => o._gcr.horizontal_alignment = v,
HorizontalAlignment.Stretch);
// HoriontalContentAlignment
WPFHelper.override_metadata<HorizontalAlignment, Badge>(
HorizontalContentAlignmentProperty,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsRender,
(o, v) => o._gcr.horizontal_content_alignment = v,
HorizontalAlignment.Center);
// VerticalAlignment
WPFHelper.override_metadata<VerticalAlignment, Badge>(
VerticalAlignmentProperty,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsRender,
(o, v) => o._gcr.vertical_alignment = v,
VerticalAlignment.Center);
// VerticalContentAlignment
WPFHelper.override_metadata<VerticalAlignment, Badge>(
VerticalContentAlignmentProperty,
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsRender,
(o, v) => o._gcr.vertical_content_alignment = v,
VerticalAlignment.Center);
// Padding
WPFHelper.override_metadata<Thickness, Badge>(
PaddingProperty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(p, v) => p._gcr.padding = v,
DefaultPadding);
}
public string Text {
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(Badge), new PropertyMetadata(string.Empty,
format_text((b,v) => b._center_text= v)));
public string TopText {
get { return (string)GetValue(TopTextProperty); }
set { SetValue(TopTextProperty, value); }
}
public static readonly DependencyProperty TopTextProperty =
DependencyProperty.Register(nameof(TopText), typeof(string), typeof(Badge), new PropertyMetadata(string.Empty,
format_text((b, v) => b._top_text = v)));
public string BottomText {
get { return (string)GetValue(BottomTextProperty); }
set { SetValue(BottomTextProperty, value); }
public Badge() {
_gcr.min_width = MinWidth;
_gcr.width = Width;
_gcr.max_width = MaxWidth;
_gcr.min_height = MinHeight;
_gcr.height = Height;
_gcr.max_height = Height;
_gcr.padding = Padding;
_gcr.border_thickness = BorderThickness;
_gcr.horizontal_alignment = HorizontalAlignment;
_gcr.horizontal_content_alignment = HorizontalContentAlignment;
_gcr.vertical_alignment = VerticalAlignment;
_gcr.vertical_content_alignment = VerticalContentAlignment;
_background = Background;
_foreground = Foreground;
_border_brush = BorderBrush;
_gcr.font_size = FontSize;
_gcr.text_lines = TextLines;
_gcr.glyph_context = GlyphContext.create(FontFamily, FontWeight);
}
public static readonly DependencyProperty BottomTextProperty =
DependencyProperty.Register(nameof(BottomText), typeof(string), typeof(Badge), new PropertyMetadata(string.Empty,
format_text((b,v)=>b._bottom_text=v)));
public static readonly DependencyProperty TextLinesProperty =
WPFHelper.create_dp<IReadOnlyList<string>, Badge>(
nameof(TextLines),
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender,
(b, v) => b._gcr.text_lines = v,
new string[0]);
public IReadOnlyList<string> TextLines {
get => GetValue(TextLinesProperty) as IReadOnlyList<string>;
set => SetValue(TextLinesProperty, value);
}
public double CornerRadius {
get { return (double)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register("CornerRadius", typeof(double), typeof(Badge), new PropertyMetadata(3.0));
WPFHelper.create_dp<double, Badge>(nameof(CornerRadius), DefaultCornerRadius);
static PropertyChangedCallback format_text(Action<Badge,FormattedText> f) {
return (d, e) => {
Badge b = (Badge)d;
ensure_fontface(b);
string text = e.NewValue as string;
if (string.IsNullOrEmpty(text)) f(b, null);
f(b, new FormattedText(text,
System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
b._face, b.FontSize, b.Foreground, _dpi));
b.InvalidateVisual();
};
protected override Size ArrangeOverride(Size finalSize) => finalSize;
protected override Size MeasureOverride(Size availableSize) {
_gcr.available_size = availableSize;
return _gcr.layout_size;
}
static FormattedText format_text(Badge badge, DependencyProperty source) {
FormattedText result = null;
var value = badge.GetValue(source);
format_text((b, v) => result = v)(badge, new DependencyPropertyChangedEventArgs(source, value, value));
return result;
}
static void update_text(DependencyObject d, DependencyPropertyChangedEventArgs args) {
// Force formatted text regeneration when font/color changes
Badge badge = (Badge)d;
badge._face = null;
ensure_fontface(badge);
badge._center_text = format_text(badge, TextProperty);
badge._top_text = format_text(badge, TopTextProperty);
badge._bottom_text = format_text(badge, BottomTextProperty);
}
private static void ensure_fontface(Badge b) {
b._face = b._face ?? new Typeface(b.FontFamily, b.FontStyle, b.FontWeight, b.FontStretch);
}
//protected override Size MeasureOverride(Size availableSize) {
// Double calc_height = 0.0;
// Double calc_width = 0.0;
// Size size = new Size(0, 0);
// Size center_size = new Size(_center_text.Width, _center_text.Height);
// Size top_size = new Size(_top_text.Width, _top_text.Height);
// Size bottom_size = new Size(_bottom_text.Width, _bottom_text.Height);
// calc_width = Math.Max(center_size.Width, top_size.Width);
// calc_width = Math.Max(calc_width, bottom_size.Width);
// calc_height += center_size.Height;
// calc_height += top_size.Height;
// calc_height += bottom_size.Height;
// size.Width = calc_width;
// size.Height = calc_height;
// return size;
//}
protected override void OnRender(DrawingContext dc) {
base.OnRender(dc);
double border_thickness = BorderThickness.Top;
var pen = new Pen(BorderBrush, border_thickness);
dc.DrawRoundedRectangle(Background, pen, new Rect(0, 0 + border_thickness, ActualWidth, ActualHeight - 2 * border_thickness), CornerRadius, CornerRadius);
if (_center_text != null)
dc.DrawText(_center_text, new Point((ActualWidth-_center_text.Width)/2, (ActualHeight-_center_text.Height)/2));
if (_top_text != null)
dc.DrawText(_top_text, new Point(border_thickness, border_thickness));
if (_bottom_text != null)
dc.DrawText(_bottom_text, new Point(border_thickness, ActualHeight - _bottom_text.Height - border_thickness));
dc.DrawRoundedRectangle(Background, pen, new Rect(0, border_thickness, _gcr.layout_size.Width, _gcr.layout_size.Height), CornerRadius, CornerRadius);
var gr = _gcr.glyph_run;
if (gr != null)
dc.DrawGlyphRun(_foreground, gr);
}
}
}
class BadgeTextLinesConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
if (ObjectHelper.try_convert_args(values, out Side side, out uint quantity, out uint filled, out mbts.spark.LadderSettings.BadgeStyle style))
return BadgeRender.format(side, quantity, filled, style);
else return new string[0];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
}
@@ -73,7 +73,7 @@ namespace mbts.ui.controls {
Disposable.dispose(ref timer._trigger_sub);
IObservable<object> newval = args.NewValue as IObservable<object>;
if (newval != null) {
timer._trigger_sub = newval.ObserveOnDispatcher().Subscribe(timer.start);
timer._trigger_sub = newval.OnDispatcher().Subscribe(timer.start);
}
}
@@ -1,4 +1,5 @@
using mbts.spark.filters;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
@@ -18,10 +19,12 @@ namespace mbts.ui.controls {
SecurityKey = 90,
Side = 47,
Size = 48,
Symbol = 48,
Time = 100,
TimeInForce = 47,
TradeKey = 76,
User = 53;
User = 53,
Aggressor = 80;
}
public static class DefaultColumnHeaders {
@@ -37,9 +40,12 @@ namespace mbts.ui.controls {
SecurityKey = "Security Key",
Side = "Side",
Size = "Qty",
Symbol = "Symbol",
TimeInForce = "TIF",
TradeKey = "Trade Key",
User = "User";
User = "User",
Time = "Time",
Aggressor = "Aggressor";
}
internal interface IKeyedColumn<TColKey> {
@@ -60,13 +66,19 @@ namespace mbts.ui.controls {
WPFHelper.create_dp<ListSortDirection?, ColumnBase>(
nameof(SortDirection), (o, v) => o.OnSortDirectionChanged(v));
public ListSortDirection? SortDirection { get => GetValue(SortDirectionProperty) as ListSortDirection?; set => SetValue(SortDirectionProperty, value); }
public ListSortDirection? SortDirection {
get => GetValue(SortDirectionProperty) as ListSortDirection?;
set => SetValue(SortDirectionProperty, value);
}
public event Action<ColumnBase, ListSortDirection?> sort_direction_changed;
public string sort_member_path { get; set; }
public double original_width { get; set; }
public bool is_collapsed { get; set; }
protected virtual void OnSortDirectionChanged(ListSortDirection? new_value) { }
protected virtual void OnSortDirectionChanged(ListSortDirection? new_value) =>
sort_direction_changed?.Invoke(this, new_value);
}
internal abstract class ColumnBase<TColKey> : ColumnBase, IKeyedColumn<TColKey> {
@@ -0,0 +1,36 @@
using mbts.spark;
using mbts.spark.filters;
using mbts.ui.utility.text;
using System;
using System.Windows;
using System.Windows.Media;
namespace mbts.ui.controls {
public partial class DIYGrid {
internal class TimeAndSalesColumn : RenderTextColumn<TimeAndSalesVM.Row> {
readonly Func<TimeAndSalesVM.Row, string> _get_text;
readonly Func<TimeAndSalesVM.Row, Brush> _get_brush;
public TimeAndSalesColumn(Func<TimeAndSalesVM.Row, string> get_text, Func<TimeAndSalesVM.Row, Brush> get_brush, IDataGridColumnFilter filter = null, string filter_template = null, GlyphContext glyph_context = null) : base(get_text, filter, filter_template, glyph_context) {
_get_text = get_text;
_get_brush = get_brush;
}
public override void draw(TimeAndSalesVM.Row row, DrawingContext dc, ColumnGeometry position, double row_height) {
CellRender.draw_text(dc
, glyph_context
, font_size
, _get_text(row)
, position
, row_height
, foreground
, _get_brush(row)
, horizontal_alignment
, vertical_alignment
, padding);
}
}
}
}
@@ -49,7 +49,7 @@ namespace mbts.spark.filters {
) => new DynamicItemsFilter<TRow, TValue, TState>(items, get_row_val, get_state, load_state, prop_name, equality_comparer, comparer);
public static IDataGridColumnPersistentFilter<TRow, TState> Create<TRow, TValue, TState>(
IObservable<(IReadOnlyList<TValue> added, IReadOnlyList<TValue> removed)> items,
IObservable<(IEnumerable<TValue> added, IEnumerable<TValue> removed)> items,
Func<TRow, TValue> get_row_val,
Func<ISet<TValue>, TState> get_state,
Func<TState, IObservable<IEnumerable<TValue>>> load_state,
@@ -115,14 +115,14 @@ namespace mbts.spark.filters {
IComparer<TValue> comparer = null
) : this(get_row_val, get_state, load_state, prop_name, equality_comparer, comparer) {
//
_items_sub = items.ObserveOnDispatcher().Subscribe(new_items => {
_items_sub = items.OnDispatcher().Subscribe(new_items => {
_items_set.SetItems(new_items.Concat(_selected_items_set));
_unsorted_items.sync_with(_items_set);
});
}
public DynamicItemsFilter(
IObservable<(IReadOnlyList<TValue> added, IReadOnlyList<TValue> removed)> items,
IObservable<(IEnumerable<TValue> added, IEnumerable<TValue> removed)> items,
Func<TRow, TValue> get_row_val,
Func<ISet<TValue>, TState> get_state,
Func<TState, IObservable<IEnumerable<TValue>>> load_state,
@@ -131,7 +131,7 @@ namespace mbts.spark.filters {
IComparer<TValue> comparer = null
) : this(get_row_val, get_state, load_state, prop_name, equality_comparer, comparer) {
//
_items_sub = items.ObserveOnDispatcher().Subscribe(update => {
_items_sub = items.OnDispatcher().Subscribe(update => {
foreach (var v in update.added)
_items_set.Add(v);
foreach (var v in update.removed.Except(_selected_items_set))
@@ -182,7 +182,7 @@ namespace mbts.spark.filters {
protected override void _load_state_internal(TState state) {
Disposable.dispose(ref _desired_items_sub);
_desired_items_sub = _load_state(state).ObserveOnDispatcher().Subscribe(_on_desired_item);
_desired_items_sub = _load_state(state).OnDispatcher().Subscribe(_on_desired_item);
}
protected override void _apply() {
@@ -54,8 +54,9 @@ namespace mbts.ui {
public static TimeTextTuple FromTicks(long ticks) =>
FromDateTime(new DateTime(ticks));
public static TimeTextTuple FromDateTime(DateTime date_time) =>
new TimeTextTuple(DateTimeFormatInfo.CurrentInfo.AbbreviatedMonthNames[date_time.Month - 1], date_time.Day.ToString(), date_time.Year.ToString(), date_time.ToString("t"));
public static TimeTextTuple FromDateTime(DateTime date_time) => FromDateTime(date_time, "t");
public static TimeTextTuple FromDateTime(DateTime date_time, string time_format) =>
new TimeTextTuple(DateTimeFormatInfo.CurrentInfo.AbbreviatedMonthNames[date_time.Month - 1], date_time.Day.ToString(), date_time.Year.ToString(), date_time.ToString(time_format));
public TimeTextTuple(string month_text, string day_text, string year_text, string time_text) {
_month_text = month_text;
@@ -107,14 +108,23 @@ namespace mbts.ui {
static DateTime? _parse(string month_text, string day_text, string year_text, string time_text) {
DateTime? t = _parse(time_text);
TimeSpan? t = _parse_time(time_text);
if (t.HasValue) {
DateTime? d = _parse($"{month_text}/{day_text}/{year_text}");
if (d.HasValue) return d.Value.Date.AddDays(t.Value.TimeOfDay.TotalDays);
if (d.HasValue) return d.Value.Date.Add(t.Value);
}
return null;
}
static TimeSpan? _parse_time(string text) {
DateTime? dt = _parse(text);
if (dt.HasValue) {
return dt.Value.TimeOfDay;
} else if (TimeSpan.TryParse(text, out TimeSpan result) && result < TimeSpan.FromDays(1)) {
return result;
} else return null;
}
static DateTime? _parse(string text) {
if (!string.IsNullOrEmpty(text) && DateTime.TryParse(text, out DateTime result))
return result;
Loaded 100 of 496 files, more files were not shown because too many files have changed in this diff. Show more