Compare commits

...
298 Commits
Author SHA1 Message Date
Taha Afzal 78368944c8 Merge branch 'prod' of bitbucket.org:bluetradesys/spark into prod 2018-08-16 15:36:14 -04:00
Taha Afzal 3812783b29 added logs in TriggerOrder to caputure initial md state 2018-08-16 15:36:02 -04:00
Jake Norbut b4908ac155 the great cpp fixup 2018-08-16 14:02:01 -05:00
Jake Norbut d0e0b2088c cpp maintenance 2018-08-16 10:33:53 -05:00
Jake Norbut 11211f4633 PnL - Pennies are back 2018-08-15 14:45:32 -05:00
Pedro Pinto dd6d86b9b1 Merged in spread_leg_last_px (pull request #58)
Implemented Jake's note on spread leg last px.

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-08-15 17:43:34 +00:00
Pedro Pinto b12fb8c175 Implemented Jake's note on spread leg last px. 2018-08-15 17:29:06 +00:00
Taha Afzal 8d5402bd4d Merge branch 'prod' of bitbucket.org:bluetradesys/spark into prod 2018-08-15 13:12:36 -04:00
Taha Afzal 7433484484 Fix for the condition when the quoter filled price != order price 2018-08-15 13:12:31 -04:00
Eric Harding 7db6591d22 fix _DISABLE_EXTENDED_ALIGNED_STORAGE issue with 15.8.0 msvc compiler 2018-08-15 11:40:35 -04:00
Jake Norbut 289f9259bb I think this will come in handy one day for testing 2018-08-14 15:58:03 -05:00
Eric Harding 197b6e85bd Quiet websocketpp.transport errors 2018-08-14 16:44:29 -04:00
Eric Harding afea120359 Merged in TableUpdateGoFaster (pull request #56)
Change TableUpdate dictionary implementation

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-08-14 17:19:03 +00:00
Eric Harding dfaeb63ded Change TableUpdate dictionary implementation 2018-08-14 09:40:58 -04:00
Michael Harding ffa1d17860 SPARK-1549: Move wildcard to top of the list 2018-08-13 12:59:51 -04:00
Michael Harding eb739b33f9 SIM Exchange Settings 2018-08-10 15:49:15 -04:00
John Colvin dc2b0c8efe Merged in mview-spreader-leg-orders (pull request #53)
Mview spreader leg orders

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-08-10 16:24:01 +00:00
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
John Colvin ee69892006 SPARK-1534 - (fix previous commit) 2018-08-03 15:50:16 -04:00
John Colvin bc279ea1b1 SPARK-1534 mview - show synthetic future-leg working orders in future's row 2018-08-03 15:22:35 -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
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
Jake Norbut 1fc38234e5 SPARK-1434 - underscore separator for SenderLocationID in audit logs 2018-07-03 09:50:44 -05:00
Michael Harding 625d47d98e SparkAdmin: Data tables and move buttons again 2018-07-02 11:47:02 -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
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 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 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
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
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 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
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 4dcaf224ef SPARK-1409 - slow picker fixed? 2018-06-21 09:43:08 -05:00
Kevin Darby 8c492fdad3 fixed SDK 2018-06-20 17:29:21 -04:00
Michael Harding b811fb6a32 SPARK-1390: Add Mkdocs to the dockerfile 2018-06-20 15:32:10 -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
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
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 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
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
343 changed files with 15789 additions and 6493 deletions

No files matched your search

+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
@@ -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);
}
@@ -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);
}
}
}
@@ -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,7 +78,7 @@ 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).OnDispatcher().Subscribe(_on_orders),
_orders_model.on_security_root_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)),
@@ -250,9 +250,6 @@ namespace mbts.spark {
public int position => _position;
public void add_order(OrderRecordImmutable o) {
// This is a market data form - not an orders form - so just show data for root/non-child orders.
if (o.has_parent())
return;
switch (o.side()) {
case Side.Buy:
_position += (int)o.filled();
@@ -274,9 +271,6 @@ namespace mbts.spark {
}
public void remove_order(OrderRecordImmutable o) {
// This is a market data form - not an orders form - so just show data for root/non-child orders.
if (o.has_parent())
return;
switch (o.side()) {
case Side.Buy:
_position -= (int)o.filled();
@@ -55,7 +55,7 @@ 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).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();
}
}
}
}
@@ -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() =>
@@ -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,6 +14,7 @@
<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="RowHeaderWidth">285</sys:Double>
<sys:Double x:Key="RowHeight">16</sys:Double>
@@ -365,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"
@@ -50,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(
@@ -253,11 +246,11 @@ namespace mbts.ui.spark {
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) {
@@ -269,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);
}
@@ -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>
@@ -53,6 +53,8 @@ namespace bts.shell {
public IntMinMaxState n_legs;
[ProtoMember(5)]
public UIntMinMaxState n_working_orders;
[ProtoMember(6)]
public string security_key;
}
}
}
}
@@ -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>();
@@ -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);
@@ -245,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;
@@ -261,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();
@@ -328,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));
}
@@ -338,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
@@ -104,4 +107,4 @@ namespace mbts.spark {
});
}
}
}
}
@@ -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;
@@ -52,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);
@@ -74,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);
@@ -87,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
));
}
@@ -104,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));
@@ -120,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()
@@ -157,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();
@@ -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;
}
}
}
@@ -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;
@@ -20,7 +22,6 @@ namespace bts.shell {
_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.OnDispatcher(), 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));
+5 -7
View File
@@ -90,18 +90,16 @@ namespace mbts.spark {
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)) {
IEnumerable<FillImmutable> fills = fill_keys.Select(fk => _fills_cache[fk]);
SecurityMD? sec_md = _get_security_md(o.security_key());
foreach (FillImmutable fill in fills) {
FillTuple old_ft = new FillTuple(fill, o, sec_md);
removed.Add(old_ft.key, old_ft);
}
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);
}
@@ -110,8 +108,8 @@ namespace mbts.spark {
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]);
SecurityMD? sec_md = _get_security_md(o.security_key());
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);
+2 -3
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();
@@ -24,6 +24,7 @@ namespace mbts.ui.spark {
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; }
@@ -41,6 +42,7 @@ namespace mbts.ui.spark {
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,8 +43,9 @@ 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;
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
@@ -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,18 +34,18 @@ 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.OnDispatcher()
.Subscribe(s => _security = s),
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) {
@@ -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;
}
@@ -53,7 +53,7 @@ namespace mbts.spark
class JoinOrderVM : OrderTypeVMBase<JoinOrderDefinition>, 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;
@@ -79,17 +79,17 @@ namespace mbts.spark
}
public int hedge_ticks { get; set; } = 1;
public JoinOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
: this(model, security_source, security_source.Select(s =>
new InlineJoinOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
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<SecurityImmutable> security_source,
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineJoinOrderProperties> inline_properties) : base(model)
{
_security_source = security_source;
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_source.OnDispatcher()
.Subscribe(s => _security = s),
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)
@@ -157,7 +157,7 @@ namespace mbts.spark
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;
}
}
@@ -167,7 +167,7 @@ namespace mbts.spark
get
{
if (_hedge_order_choices == null)
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _hedge_order_choices;
}
}
@@ -35,7 +35,7 @@ namespace mbts.spark
class LeanOrderVM : OrderTypeVMBase<LeanOrderDefinition>, 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;
@@ -46,17 +46,17 @@ namespace mbts.spark
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<SecurityImmutable> security_source)
: this(model, security_source, security_source.Select(s =>
new InlineLeanOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
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<SecurityImmutable> security_source,
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineLeanOrderProperties> inline_properties) : base(model)
{
_security_source = security_source;
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_source.OnDispatcher()
.Subscribe(s => _security = s),
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)
@@ -104,7 +104,7 @@ namespace mbts.spark
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;
}
}
@@ -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,31 +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 tick(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TickOrderVM(model, security_source);
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityImmutable> security_source) => new LeanOrderVM(model, security_source);
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityImmutable> security_source) => new JoinOrderVM(model, security_source);
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TopOrderVM(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.Tick: return tick(model, security_source);
case CustomOneofCase.Lean: return lean(model, security_source);
case CustomOneofCase.Join: return join(model, security_source);
case CustomOneofCase.Top: return top(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,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));
}
}
}
@@ -41,7 +41,7 @@ namespace mbts.spark
class TickOrderVM : OrderTypeVMBase<TickOrderDefinition>, 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;
@@ -55,17 +55,17 @@ namespace mbts.spark
public uint max_chase_ticks { get; set; } = 10;
public bool detach_on_toward { get; set; } = true;
public TickOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
: this(model, security_source, security_source.Select(s =>
new InlineTickOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
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<SecurityImmutable> security_source,
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineTickOrderProperties> inline_properties) : base(model)
{
_security_source = security_source;
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_source.OnDispatcher()
.Subscribe(s => _security = s),
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)
@@ -131,7 +131,7 @@ namespace mbts.spark
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;
}
}
@@ -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,18 +40,18 @@ 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.OnDispatcher()
.Subscribe(s => _security = s),
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) {
@@ -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;
}
@@ -51,7 +51,7 @@ namespace mbts.spark
class TopOrderVM : OrderTypeVMBase<TopOrderDefinition>, 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;
@@ -77,18 +77,18 @@ namespace mbts.spark
}
public int hedge_ticks { get; set; } = 1;
public TopOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
: this(model, security_source, security_source.Select(s =>
new InlineTopOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList())))
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<SecurityImmutable> security_source,
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineTopOrderProperties> inline_properties) : base(model)
{
_security_source = security_source;
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_source.OnDispatcher()
.Subscribe(s => _security = s),
security_md_source.OnDispatcher()
.Subscribe(smd => _security = smd.security),
inline_properties.OnDispatcher()
.Subscribe(p =>
{
@@ -157,7 +157,7 @@ namespace mbts.spark
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;
}
}
@@ -167,7 +167,7 @@ namespace mbts.spark
get
{
if (_hedge_order_choices == null)
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
return _hedge_order_choices;
}
}
@@ -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));
}
}
}
}
+70
View File
@@ -2,6 +2,7 @@
using bts.api.core;
using bts.api.orderman;
using bts.spark;
using mbts.collections;
using mbts.spark.utilities;
using mbts.utils;
using System;
@@ -42,6 +43,7 @@ namespace mbts.spark {
_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())
@@ -172,6 +174,69 @@ namespace mbts.spark {
.Select(t => t.state.Any(kv => kv.Value.is_active()))
.StartWith(false);
public static IObservable<ITableUpdateTransform<OrderRecordImmutable, uint>> on_security_root_orders(this OrdersModel orders_model, uint security_key) =>
orders_model.on_security_orders(security_key).Scan(new RootOrderData(), (rod, table) => {
// ** using HashSet/List pairs, rather than a Dictionary, because TableUpdateTransform calls for IReadOnlyLists, while Dictionary.Values only implements IReadOnlyCollection.
// (this way, we avoid copying both added/removed Dictionary.Values to new Lists at the end, see comment at bottom of method if this is unclear) **
var added_root_keys = new HashSet<uint>();
var added_roots = new List<OrderRecordImmutable>();
var removed_root_keys = new HashSet<uint>();
var removed_roots = new List<OrderRecordImmutable>();
foreach (var o in table.removed) {
if (rod.last_update.state.ContainsKey(o.Key)) {
// order was a root, needs to be removed
removed_roots.Add(o);
if (table.state.TryGetValue(o.Key, out OrderRecordImmutable newval)) {
// order was changed, if its an actual root, or its parent isn't present, the newval needs to be added
if (!o.has_parent() || !table.state.ContainsKey(o.ParentKey)) {
added_roots.Add(newval);
added_root_keys.Add(o.Key);
}
} else {
// order was indeed removed, not just changed - if it has a parent, remove its corresponding child-entry
if (o.has_parent()) {
rod.parent_to_children.remove(o.ParentKey, o.Key);
}
if (rod.parent_to_children.try_lookup(o.Key, out ISet<uint> children)) {
// order has left some orphans behind, any children that haven't been removed (i.e. still exist in table.state) are now roots
foreach (uint ck in children) {
if (table.state.TryGetValue(ck, out OrderRecordImmutable child)) {
added_roots.Add(child);
added_root_keys.Add(ck);
}
}
}
}
} else if (!table.state.ContainsKey(o.Key)) {
// order was indeed removed, not just changed - if it has a parent, remove its corresponding child-entry
rod.parent_to_children.remove(o.ParentKey, o.Key);
}
}
foreach (var o in table.added) {
if (added_root_keys.Contains(o.Key))
continue;
if (rod.parent_to_children.try_lookup(o.Key, out ISet<uint> children)) {
// children were previously functioning as roots, need to remove them
foreach (uint ck in children) {
if (rod.last_update.state.TryGetValue(ck, out OrderRecordImmutable child))
removed_roots.Add(child);
}
}
if (o.has_parent()) {
rod.parent_to_children.add(o.ParentKey, o.Key);
if (table.state.ContainsKey(o.ParentKey)) {
// not a root order
continue;
}
}
added_roots.Add(o);
}
// ** [continue comment at top of method] this call would otherwise be:
// rod.last_update = rod.last_update.update(removed_dict.Values.ToList(), added_dict.Values.ToList(), o => o.Key) **
rod.last_update = rod.last_update.update(removed_roots, added_roots, o => o.Key);
return rod;
}).Select(r => r.last_update);
public static async Task<OrderRecordImmutable?> order_from_key(this OrdersModel orders_model, uint order_key) {
(var tu, var success) = await orders_model.on_orders.FirstAsyncTask(t => t.state.ContainsKey(order_key), TimeSpan.FromSeconds(3));
if (success && tu.state.TryGetValue(order_key, out OrderRecordImmutable rec)) {
@@ -212,5 +277,10 @@ namespace mbts.spark {
}
return false;
}
class RootOrderData {
public readonly SetMap<uint, uint> parent_to_children = new SetMap<uint, uint>();
public TableUpdateTransform<OrderRecordImmutable, uint> last_update { get; set; } = new TableUpdateTransform<OrderRecordImmutable, uint>();
}
}
}
@@ -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);
}
@@ -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;
}
}
}
@@ -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;
@@ -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;
}
@@ -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);
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace mbts.utils {
@@ -25,6 +26,9 @@ 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 &&
@@ -55,7 +59,7 @@ namespace mbts.utils {
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> {
private class AnonymousEqualityComparer<T> : IEqualityComparer<T>, IEqualityComparer {
readonly Func<T, T, bool> _equals;
readonly Func<T, int> _get_hash_code;
@@ -67,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);
}
}
@@ -19,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 {
@@ -38,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> {
@@ -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);
}
}
}
}
@@ -42,9 +42,12 @@
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center">From</TextBlock>
<TextBlock Grid.Column="2" HorizontalAlignment="Left">
<Hyperlink x:Name="ClearBeginLink" Focusable="False">(Set Anytime)</Hyperlink>
</TextBlock>
<Grid Grid.Column="2">
<TextBlock HorizontalAlignment="Left">
<Hyperlink x:Name="ClearBeginLink" Focusable="False">(Set Anytime)</Hyperlink>
<Hyperlink x:Name="SetLastHourLink" Focusable="False">(Set Last Hour)</Hyperlink>
</TextBlock>
</Grid>
<c:DateTimePicker
x:Name="PART_Begin_DateTimePicker"
Grid.Row="2"
@@ -44,8 +44,10 @@ namespace mbts.ui.controls {
public override void OnApplyTemplate() {
var clear_begin_time = this.assert_template_child<Hyperlink>("ClearBeginLink");
var clear_end_time = this.assert_template_child<Hyperlink>("ClearEndLink");
var set_last_hour = this.assert_template_child<Hyperlink>("SetLastHourLink");
clear_begin_time.Command = new DelegateCommand(() => Begin = default);
clear_end_time.Command = new DelegateCommand(() => End = default);
set_last_hour.Command = new DelegateCommand(() => Begin = TimeTextTuple.FromDateTime(DateTime.Now.AddHours(-1)));
}
class TimeFilterErrorAdorner : NotifyDataErrorAdorner<TimeFilterControl, ITimeFilterVM>, IDisposable {
@@ -190,17 +190,21 @@ namespace mbts.ui.controls
}
}
void item_Unselected(object sender, RoutedEventArgs e) {
if (_selection_changing) return;
var item = ((ListBoxItem)sender).DataContext;
var selected_items = Selection;
if (selected_items.Contains(item)) {
using (_selection_change()) {
selected_items.Remove(item);
_update_is_all_selected();
}
}
}
void item_Unselected(object sender, RoutedEventArgs e) {
if (!_selection_changing && sender is ListBoxItem container) {
// the [ListBoxItem] sender may be "Disconnected" https://stackoverflow.com/questions/14282894/wpf-listbox-virtualization-creates-disconnecteditems/14358688
var item = ItemContainerGenerator.ItemFromContainer(container);
if (item != DependencyProperty.UnsetValue) {
var selected_items = Selection;
if (selected_items.Contains(item)) {
using (_selection_change()) {
selected_items.Remove(item);
_update_is_all_selected();
}
}
}
}
}
void item_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) {
using (_selection_change()) ((ListBoxItem)sender).IsSelected = Selection.Contains(e.NewValue);
@@ -368,4 +372,4 @@ namespace mbts.ui.controls
}
}
}
}
}
@@ -335,7 +335,45 @@
/>
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.TopOrderTemplate>
</usp:OrderModificationTemplateSelector.TopOrderTemplate>
<usp:OrderModificationTemplateSelector.TriggerOrderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Trigger Price Type</TextBlock>
<ComboBox
Grid.Row="1" Grid.Column="2"
ItemsSource="{Binding trigger_price_types}"
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
SelectedItem="{Binding trigger_price_type}"
/>
<TextBlock Grid.Row="3" VerticalAlignment="Center">Order Price</TextBlock>
<usp:PriceEntryControl Grid.Row="3" Grid.Column="2" Value="{Binding order_price}" ContractSpec="{Binding spec}" />
<TextBlock Grid.Row="5">Child</TextBlock>
<uag:OrderDetailsPopupControl
Grid.Row="5" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
ItemsSource="{Binding child_order_types}"
SelectedItem="{Binding child_order}"
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
/>
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.TriggerOrderTemplate>
<usp:OrderModificationTemplateSelector.IcebergOrderTemplate>
<DataTemplate>
<Grid>
@@ -375,7 +413,6 @@
DataContext="{Binding strategy_editor}"
SyntheticName="{Binding name}"
TickFactors="{Binding tick_factors}"
AvailableTickFactors="{Binding available_tick_factors_view}"
IsNew="False"
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
PriceCalc="{Binding price_calc}"
@@ -41,7 +41,7 @@ namespace mbts.ui.spark {
#region ContractSpec
public static readonly DependencyProperty ContractSpecProperty = DependencyProperty.Register("ContractSpec", typeof(ContractSpecificationImmutable), typeof(PriceEntryControl),
new PropertyMetadata(SecurityDataHelper.Defaults.ContractSpecificationImmutable, OnContractSpecChanged));
new PropertyMetadata(SecurityDataHelper.Defaults.ContractSpecificationImmutablePennies, OnContractSpecChanged));
static void OnContractSpecChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((PriceEntryControl)d)._update_text_binding();
public ContractSpecificationImmutable? ContractSpec { get => GetValue(ContractSpecProperty) as ContractSpecificationImmutable?; set => SetValue(ContractSpecProperty, value); }
#endregion
@@ -10,9 +10,11 @@ namespace mbts.ui.controls {
static SparkMenu() =>
DefaultStyleKeyProperty.OverrideMetadata(typeof(SparkMenu), new FrameworkPropertyMetadata(typeof(SparkMenu)));
public SparkMenu() =>
public SparkMenu() {
SetCurrentValue(FocusableProperty, false);
}
public SparkMenu(IEnumerable<SparkCommand> commands, IBus bus) : this() =>
ItemsSource = commands.Select(c => new SparkCommandVM(c, bus)).ToList();
@@ -0,0 +1,75 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:txt="clr-namespace:mbts.ui.utility.text"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Resources/ComboBoxes.xaml"/>
<ResourceDictionary Source="/view/Resources/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="c:TickFactorsPicker">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="c:TickFactorsPicker">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox
MinWidth="48"
IsEditable="True"
DisplayMemberPath="value"
SelectedValuePath="value"
ItemsSource="{Binding TickUnitPresetsView, RelativeSource={RelativeSource TemplatedParent}}"
SelectedValue="{Binding Controller.tick_unit, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Text="{Binding Controller.tick_unit, Converter={StaticResource DoubleTextConverter}, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
>
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ComboBox.DelineatedGroupContainerStyle}"/>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{x:Static c:TickFactorsPicker.TickUnitVisibleTextConverter}">
<Binding Path="Controller.tick_base" RelativeSource="{RelativeSource AncestorType=c:TickFactorsPicker}"/>
<Binding Path="value"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="1" Foreground="{u:BTSColor UI.input_disabled_text}" Text="{Binding value, Converter={x:Static c:TickFactorsPicker.TickUnitHiddenTextConverter}}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox
Grid.Column="2"
MinWidth="60"
IsEditable="True"
DisplayMemberPath="value"
SelectedValuePath="value"
ItemsSource="{Binding TickBasePresetsView, RelativeSource={RelativeSource TemplatedParent}}"
SelectedValue="{Binding Controller.tick_base, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Text="{Binding Controller.tick_base, Converter={StaticResource DoubleTextConverter}, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
>
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ComboBox.DelineatedGroupContainerStyle}"/>
</ComboBox.GroupStyle>
</ComboBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -0,0 +1,157 @@
using mbts.spark;
using mbts.spark.utilities;
using mbts.ui.converters;
using mbts.ui.shell;
using mbts.ui.utility;
using mbts.user.settings;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace mbts.ui.controls {
/// <summary>
/// Interaction logic for TickPicker.xaml
/// </summary>
class TickFactorsPicker : Control {
readonly ObservableCollection<double> _tick_unit_presets = new ObservableCollection<double>();
static TickFactorsPicker() =>
DefaultStyleKeyProperty.OverrideMetadata(typeof(TickFactorsPicker), new FrameworkPropertyMetadata(typeof(TickFactorsPicker)));
public TickFactorsPicker() {
Controller = new TickFactorsController(this);
TickBasePresetsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(TickBaseVM.reduced_base_group)));
((ListCollectionView)TickBasePresetsView).CustomSort = TickUtils.CompareUtils.base_comparer.wrap<double, TickBaseVM>(vm => vm.value).to_non_generic();
TickUnitPresetsView = CollectionViewSource.GetDefaultView(new ObservableAutoWrapper<TickUnitVM, double>(_tick_unit_presets, v => new TickUnitVM(v)));
TickUnitPresetsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(TickUnitVM.reduced_unit_group)));
((ListCollectionView)TickUnitPresetsView).CustomSort = Comparer<double>.Default.wrap<double, TickUnitVM>(vm => vm.value).to_non_generic();
}
public static IEnumerable<double> TickBasePresets =>
new [] {
8, 16, 32, 64, 100
}.SelectMany(i => Enumerable.Range(0, 4).Select(log10 => i * Math.Pow(10, log10)));
public static IMultiValueConverter TickUnitVisibleTextConverter =>
ValueConverter.create((double tick_base, double tick_unit) => {
int display_digits = TickExtensions.display_digits(tick_base);
return ((int)(tick_unit)).ToString(new string('0', display_digits));
});
public static IValueConverter TickUnitHiddenTextConverter =>
ValueConverter.create((double tick_unit) => new string(tick_unit.ToString().SkipWhile(c => c != '.').ToArray()));
public static readonly DependencyProperty SelectedTickFactorsProperty =
WPFHelper.create_dp<TickFactors, TickFactorsPicker>(
nameof(SelectedTickFactors),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, v) => o.Controller.load_from(v),
new TickFactors());
public TickFactors SelectedTickFactors {
get => GetValue(SelectedTickFactorsProperty) as TickFactors;
set => SetValue(SelectedTickFactorsProperty, value);
}
public TickFactorsController Controller { get; }
public ICollectionView TickBasePresetsView { get; } = CollectionViewSource.GetDefaultView(TickBasePresets.Select(v => new TickBaseVM(v)).ToList());
public ICollectionView TickUnitPresetsView { get; }
static IEnumerable<double> _get_tick_unit_presets(double tick_base) {
double reduced_base = MathUtils.factor_out(tick_base, 10, out int base_log10);
return reduced_base == 1 ?
_get_dbased_tick_unit_presets(base_log10) :
_get_2based_tick_unit_presets((int)Math.Log(reduced_base, 2), base_log10);
}
static IEnumerable<double> _get_2based_tick_unit_presets(int log2, int log10) =>
(log10 == 0 ? new[] { 1.0 } : _get_dbased_tick_unit_presets(log10)).Concat(
Enumerable.Range(1, log2 - 1).SelectMany(pow2 => {
double root = Math.Pow(2, pow2);
return Enumerable.Range(0, log10 + 1).Select(pow10 => root * Math.Pow(10, pow10));
}));
static IEnumerable<double> _get_dbased_tick_unit_presets(int log10) =>
Enumerable.Range(0, log10).SelectMany(_coins);
static IEnumerable<double> _coins(int pow10) {
double d = Math.Pow(10, pow10);
yield return d;
yield return 1.25 * d;
yield return 2.5 * d;
yield return 5.0 * d;
}
public class TickFactorsController : ViewModelBase, IValueFactoryVM<TickFactors> {
readonly TickFactorsPicker _view;
readonly Property<double> _tick_base, _tick_unit;
public TickFactorsController(TickFactorsPicker view) {
var init = (_view = view).SelectedTickFactors;
_tick_base = backing(nameof(tick_base), init.tick_base);
_tick_unit = backing(nameof(tick_unit), init.tick_unit);
}
public double tick_base {
get => _tick_base.Value;
set {
_tick_base.set(value);
WPFHelper.set_current_if_changed(_view, SelectedTickFactorsProperty, new TickFactors(value, tick_unit));
_view._tick_unit_presets.sync_with(_get_tick_unit_presets(value));
}
}
public double tick_unit {
get => _tick_unit.Value;
set {
_tick_unit.set(value);
WPFHelper.set_current_if_changed(_view, SelectedTickFactorsProperty, new TickFactors(tick_base, value));
}
}
public TickFactors create() => new TickFactors(tick_base, tick_unit);
public void load_from(TickFactors value) {
if (value != null) {
_tick_base.set(value.tick_base);
_tick_unit.set(value.tick_unit);
}
}
}
public class TickBaseVM : IEquatable<TickBaseVM> {
public TickBaseVM(double value) =>
this.value = value;
public double value { get; }
public double reduced_base_group => MathUtils.factor_out(value, 10);
public bool Equals(TickBaseVM other) => other != null && value == other.value;
public override bool Equals(object obj) => Equals(obj as TickBaseVM);
public override int GetHashCode() => value.GetHashCode();
}
public class TickUnitVM : IEquatable<TickUnitVM> {
public TickUnitVM(double value) =>
this.value = value;
public double value { get; }
public double reduced_unit_group {
get {
if (!MathUtils.is_int(value))
return 1;
else return MathUtils.factor_out(value, 10, 5);
}
}
public bool Equals(TickUnitVM other) => other != null && value == other.value;
public override bool Equals(object obj) => Equals(obj as TickUnitVM);
public override int GetHashCode() => value.GetHashCode();
}
}
}
@@ -2,8 +2,15 @@
using System.Globalization;
using System.Windows.Data;
namespace mbts.ui.converters
{
namespace mbts.ui.converters {
static class ValueConverter {
public static IValueConverter create<TIn, TOut>(Func<TIn, TOut> convert) =>
new AnonymousValueConverter<TIn, TOut>(convert);
public static IMultiValueConverter create<TIn1, TIn2, TOut>(Func<TIn1, TIn2, TOut> convert) =>
new AnonymousMultiValueConverter<TIn1, TIn2, TOut>(convert);
}
public class AnonymousValueConverter : IValueConverter {
public AnonymousValueConverter(Func<object, object> convert) : this(convert, null) { }
public AnonymousValueConverter(Func<object, object> convert, Func<object, object> convert_back) {
@@ -13,7 +13,6 @@ namespace mbts.ui.converters
Minimum = Double.NaN;
Maximum = Double.NaN;
Multiplier = Double.NaN;
Format = FORMAT_WITH_COMMAS;
HideNaN = true;
}
@@ -14,9 +14,20 @@ namespace mbts.ui.converters {
static NumericSignBrushConverter() =>
_error_brush.Freeze();
public Color PositiveColor { get => _positive_brush.Color; set => (_positive_brush = new SolidColorBrush(value)).Freeze(); }
public Color NeutralColor { get => _neutral_brush.Color; set => (_neutral_brush = new SolidColorBrush(value)).Freeze(); }
public Color NegativeColor { get => _negative_brush.Color; set => (_negative_brush = new SolidColorBrush(value)).Freeze(); }
public Color PositiveColor { get => _positive_brush.Color; set => (_positive_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
public Color NeutralColor { get => _neutral_brush.Color; set => (_neutral_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
public Color NegativeColor { get => _negative_brush.Color; set => (_negative_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
private double _opacity = 1.0;
public Double Opacity {
get { return _opacity; }
set {
_opacity = value;
(_positive_brush = new SolidColorBrush(_positive_brush.Color) { Opacity = _opacity }).Freeze();
(_negative_brush = new SolidColorBrush(_negative_brush.Color) { Opacity = _opacity }).Freeze();
(_neutral_brush = new SolidColorBrush(_neutral_brush.Color) { Opacity = _opacity }).Freeze();
}
}
public IComparable Zero { get; set; } = 0.0;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
@@ -26,23 +26,16 @@ namespace mbts.ui.spark.converters {
}
class TickDisplayTwoWayConverter : IValueConverter {
public ContractSpecificationImmutable? ContractSpec { get; set; } =
new ContractSpecification {
CashPerPoint = 1.0,
LowTick = 0.01,
HighTick = 0.01,
PriceBreak = double.MaxValue,
PriceMultiplier = 1.0
};
public ContractSpecificationImmutable? ContractSpec { get; set; } = SecurityDataHelper.Defaults.ContractSpecificationImmutablePennies;
public double DisplayMultiplier { get; set; } = double.NaN;
public bool ShowCommas { get; set; } = false;
public string NaNText { get; set; } = string.Empty;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
ContractSpec.Value.format((double)value, DisplayMultiplier, ShowCommas, NaNText);
ContractSpec.format((double)value, DisplayMultiplier, ShowCommas, NaNText);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
ContractSpec.Value.parse(value as string, DisplayMultiplier, NaNText);
ContractSpec.parse(value as string, DisplayMultiplier, NaNText);
}
}
@@ -7,7 +7,7 @@ namespace mbts.ui.converters {
class TickFactorsTextConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is TickFactors tf) {
int n_digits = TickExtensions.n_display_digits(tf.tick_base);
int n_digits = tf.display_digits();
if (n_digits > 1) {
string n_digits_format = new string('0', n_digits);
string format = "{0:" + n_digits_format + "}/{1}";
@@ -61,16 +61,16 @@ namespace mbts.ui
_source_window = window as FloatingWindow;
if (_source != null) {
_mouse_start = e.GetPosition(window);
if (!is_button_descendant((DependencyObject)e.OriginalSource)) {
if (!is_clickable_descendant((DependencyObject)e.OriginalSource)) {
Mouse.Capture(_source);
}
}
}
static bool is_button_descendant(DependencyObject target) {
static bool is_clickable_descendant(DependencyObject target) {
while (target != null && !(target is TabDocument)) {
target = VisualTreeHelper.GetParent(target);
if (target is Button) return true;
if (target is Button || target is TextBox) return true;
}
return false;
}
@@ -24,38 +24,7 @@ namespace mbts.ui
}
}
public static async Task FromEvent(this object target, string event_name) {
var evt = target.GetType().GetEvent(event_name);
var delegate_type = evt.EventHandlerType;
var tcs = new TaskCompletionSource<object>();
Action result = () => { tcs.TrySetResult(null); };
var d = Delegate.CreateDelegate(evt.EventHandlerType, result, "Invoke");
try {
evt.AddEventHandler(target, d);
await tcs.Task;
}
finally {
evt.RemoveEventHandler(target, d);
}
}
public static async Task<T> FromEvent<T>(this object target, string event_name) {
var evt = target.GetType().GetEvent(event_name);
var delegate_type = evt.EventHandlerType;
var tcs = new TaskCompletionSource<T>();
Action<T> result = (arg) => { tcs.TrySetResult(arg); };
var d = Delegate.CreateDelegate(evt.EventHandlerType, result, "Invoke");
try {
evt.AddEventHandler(target, d);
await tcs.Task;
return tcs.Task.Result;
}
finally {
evt.RemoveEventHandler(target, d);
}
}
public static async Task<Tuple<T1,T2>> FromEvent<T1,T2>(this object target, string event_name) {
public static async Task<Tuple<T1,T2>> FromEvent<T1,T2>(this DependencyObject target, string event_name) {
var evt = target.GetType().GetEvent(event_name);
var delegate_type = evt.EventHandlerType;
var tcs = new TaskCompletionSource<Tuple<T1,T2>>();
@@ -14,6 +14,9 @@ namespace mbts.ui
{
return random.NextDouble() * (maxValue - minValue) + minValue;
}
public static bool NextBool(this Random random, int truePercentage = 50) {
return random.NextDouble() < (truePercentage / 100.0);
}
}
@@ -32,7 +32,7 @@ namespace mbts.ui {
#region TraverseOnEnter
public static readonly DependencyProperty TraverseOnEnterProperty = DependencyProperty.RegisterAttached("TraverseOnEnter", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, OnTraverseOnEnterChanged));
public static readonly DependencyProperty TraverseOnEnterProperty = DependencyProperty.RegisterAttached("TraverseOnEnter", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, OnTraverseOnEnterChanged));
private static void OnTraverseOnEnterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var text_box = (TextBox)d;
if ((bool)e.NewValue) text_box.PreviewKeyDown += commit_on_enter_PreviewKeyDownHandler;
@@ -67,6 +67,65 @@ namespace mbts.ui {
public static void SetCommitOnEnter(DependencyObject obj, bool value) => obj.SetValue(CommitOnEnterProperty, value);
#endregion
#region CancelOnEnter
public static readonly DependencyProperty CancelOnEscapeProperty =
DependencyProperty.RegisterAttached("CancelOnEscape", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, cancel_on_escape_changed));
private static void cancel_on_escape_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var tb = (TextBox)d;
if ((bool)e.NewValue) {
tb.KeyDown += tbx_keydown;
}
else {
tb.KeyDown += tbx_keydown;
}
}
static void tbx_keydown(object sender, KeyEventArgs e) {
if (e.Key == Key.Escape) {
abort_edit(sender as DependencyObject);
}
}
// Copy the binding value back and forth to generate a set to the source property with the original value
private static void abort_edit(DependencyObject sender) {
if (sender != null) {
var binding = BindingOperations.GetBindingExpression(sender, TextBox.TextProperty);
binding?.UpdateTarget();
binding?.UpdateSource();
}
}
public static bool GetCancelOnEscape(DependencyObject obj) => (bool)obj.GetValue(CancelOnEscapeProperty);
public static void SetCancelOnEscape(DependencyObject obj, bool value) => obj.SetValue(CancelOnEscapeProperty, value);
#endregion
#region TryCancelOnFocusLost
public static readonly DependencyProperty TryCancelOnFocusLostProperty =
DependencyProperty.RegisterAttached("TryCancelOnFocusLost", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, cancel_on_focus_lost_changed));
private static void cancel_on_focus_lost_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var tb = (TextBox)d;
if ((bool)e.NewValue) {
tb.LostFocus += tb_lostfocus;
}
else {
tb.LostFocus += tb_lostfocus;
}
}
static void tb_lostfocus(object sender, EventArgs e) {
abort_edit(sender as DependencyObject);
}
public static bool GetTryCancelOnFocusLost(DependencyObject obj) => (bool)obj.GetValue(TryCancelOnFocusLostProperty);
public static void SetTryCancelOnFocusLost(DependencyObject obj, bool value) => obj.SetValue(TryCancelOnFocusLostProperty, value);
#endregion
#region ReleaseOnEscape
public static readonly DependencyProperty ReleaseOnEscapeProperty = DependencyProperty.RegisterAttached(
"ReleaseOnEscape", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, OnReleaseOnEscapeChanged));
@@ -79,7 +138,8 @@ namespace mbts.ui {
tb.ReleaseMouseCapture();
Keyboard.ClearFocus();
}));
} else {
}
else {
tb.ClearKeyBindings(Key.Escape, ModifierKeys.None);
tb.ClearCommandBindings(ReleaseCaptureCommand);
}
@@ -180,17 +240,18 @@ namespace mbts.ui {
"IncrementDecrementOnMouseClick", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, IncrementDecrementOnMouseClickPropertyChanged)
);
public static bool GetIncrementDecrementOnMouseClick(TextBox element) { return (bool)element.GetValue(IncrementDecrementOnMouseClickProperty); }
public static bool GetIncrementDecrementOnMouseClick(TextBox element) { return (bool)element.GetValue(IncrementDecrementOnMouseClickProperty); }
public static void SetIncrementDecrementOnMouseClick(TextBox element, bool value) { element.SetValue(IncrementDecrementOnMouseClickProperty, value); }
private static void IncrementDecrementOnMouseClickPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var text_box = d as TextBox;
if(text_box != null) {
if (text_box != null) {
if ((bool)e.NewValue) {
text_box.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownDecrementValue;
text_box.PreviewMouseRightButtonDown += OnMouseRightButtonDownIncrementValue;
} else {
}
else {
text_box.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownDecrementValue;
text_box.PreviewMouseRightButtonDown -= OnMouseRightButtonDownIncrementValue;
}
@@ -201,7 +262,8 @@ namespace mbts.ui {
var text_box = sender as TextBox;
if (text_box == null || String.IsNullOrWhiteSpace(text_box.Text)) {
return;
} else {
}
else {
if (uint.TryParse(text_box.Text, out uint val)) {
if (val < uint.MaxValue) {
val++;
@@ -226,5 +288,6 @@ namespace mbts.ui {
}
}
#endregion
}
}
@@ -14,18 +14,19 @@ namespace mbts.ui.shell
public partial class FloatingWindow : Window
{
bool _closed = false;
private IUndoActions _undo;
readonly IBookmarks _bookmark;
private IWindowManager _wm;
private ISavedWindowFactory _window_factory;
private ISettingsCollector _window_settings;
private Stack<Action> _recently_closed_windows = new Stack<Action>();
// if is_loaded is true, will NOT set width/height
internal FloatingWindow(TabDocument document, IWindowManager wm, IUndoActions undo, ISettingsCollector settings, ISavedWindowFactory factory, bool is_loaded=false) {
internal FloatingWindow(TabDocument document,
IWindowManager wm, IBookmarks bookmarks, ISettingsCollector settings, ISavedWindowFactory factory, bool is_loaded=false) {
_wm = wm;
_undo = undo;
_window_settings = settings;
_window_factory = factory;
_bookmark = bookmarks;
InitializeComponent();
if (string.IsNullOrEmpty(document.ViewModel.host_id)) {
@@ -67,6 +68,7 @@ namespace mbts.ui.shell
private ItemCollection Items { get { return _tabs.Items; } }
public IEnumerable<TabDocument> Documents => _tabs.Items.OfType<TabDocument>();
public bool IsEmpty { get { return _tabs.Items.Count == 0; } }
public bool SingleChild { get { return _tabs.Items.Count == 1; } }
public string host_id { get; private set; }
@@ -88,10 +90,12 @@ namespace mbts.ui.shell
if (sender != this) {
var undoaction = create_undo_action(document);
if (undoaction != null) {
IDisposable undoitem = _undo.push("Restore Tab '{0}'".fmt(document.Title), undoaction);
_recently_closed_windows.Push(() => { undoaction(); undoitem.Dispose(); });
_recently_closed_windows.Push(() => { undoaction(); });
}
}
if (document.HasCustomTitle) {
_bookmark.save_bookmark(document.Model);
}
}
public void remove_document(TabDocument document) {
@@ -144,22 +148,9 @@ namespace mbts.ui.shell
protected override void OnClosed(EventArgs e) {
_closed = true;
TabDocument[] items = _tabs.Items.OfType<TabDocument>().ToArray();
if (items.Length > 0) {
// If we're closing and still have Tabs open we save the whole "window" worth of state in the undo stack
// If the application is shutting down this might be unnecessary work but it's not too costly
var undo_actions = items.Select(t => create_undo_action(t.ViewModel)).Where(item => item != null).ToArray();
if (undo_actions.Length > 0) {
string name = "Restore Window: '{0}'".fmt(string.Join("', '", items.Select(t => t.ViewModel.Title)));
_undo.push(name, () => {
foreach (var u in undo_actions) { u(); }
});
}
}
base.OnClosed(e);
foreach (TabDocument document in items) {
foreach (TabDocument document in _tabs.Items.OfType<TabDocument>().ToList()) {
if (document != null && document.ViewModel.CloseCommand.CanExecute(this)) {
document.ViewModel.CloseCommand.Execute(this);
}
+40 -12
View File
@@ -3,7 +3,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Style="{StaticResource {x:Type TabItem}}" d:DesignWidth="125"
xmlns:ui="clr-namespace:mbts.ui"
xmlns:convert="clr-namespace:mbts.ui.converters"
mc:Ignorable="d" d:DesignWidth="125"
Header="{Binding}" Content="{Binding AppContent}"
Background="#e6e6e6">
@@ -18,6 +20,19 @@
<Storyboard x:Key="_slide_back">
<DoubleAnimation Storyboard.TargetName="translate_transform" Storyboard.TargetProperty="X" To="100" Duration="0:0:0.25" FillBehavior="Stop" />
</Storyboard>
<DataTemplate x:Key="_edit_template">
<TextBox Text="{Binding TitleOverride}"
ui:TextBoxBehavior.CommitOnEnter="True"
ui:TextBoxBehavior.FocusOnLoaded="True"
ui:TextBoxBehavior.SelectAllOnFocus="True"
ui:TextBoxBehavior.CancelOnEscape="True"
ui:TextBoxBehavior.TryCancelOnFocusLost="True"
Width="150" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0" />
</DataTemplate>
<DataTemplate x:Key="_template">
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0"></TextBlock>
</DataTemplate>
<convert:BooleanToObjectConverter x:Key="MenuHeaderConverter" ValueForTrue="Rename Window" ValueForFalse="Add to My Windows" />
</ResourceDictionary>
</TabItem.Resources>
<TabItem.LayoutTransform>
@@ -28,25 +43,32 @@
<TabItem.RenderTransform>
<TranslateTransform x:Name="translate_transform" X="0" />
</TabItem.RenderTransform>
<TabItem.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0"> </TextBlock>
</DataTemplate>
</TabItem.HeaderTemplate>
<TabItem.Template>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border x:Name="bd" BorderBrush="Black" BorderThickness="0,0,0.5,0" Background="#d6d6d6" Width="{DynamicResource TabWidth}" Height="{DynamicResource TabHeight}" MinWidth="40" Margin="0" SnapsToDevicePixels="True" DockPanel.Dock="Left">
<Border.ToolTip>
<TextBlock Text="{Binding Title}" />
</Border.ToolTip>
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding HasCustomTitle, Converter={StaticResource MenuHeaderConverter}}" Command="{Binding rename_command}">
<MenuItem.Icon>
<Image Source="{StaticResource Bookmarks}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Remove from My Windows" Command="{Binding unrename_command}" IsEnabled="{Binding HasCustomTitle}" />
</ContextMenu>
</Border.ContextMenu>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image SnapsToDevicePixels="True" MaxHeight="20" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Margin="5,0,5,0" Source="{Binding Icon, Mode=OneTime}" RenderOptions.BitmapScalingMode="HighQuality" />
<Image SnapsToDevicePixels="True" MaxHeight="20" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Margin="5,0,5,0"
Source="{Binding Icon, Mode=OneTime}" RenderOptions.BitmapScalingMode="HighQuality" />
<!--<Image MaxHeight="10" HorizontalAlignment="Left" VerticalAlignment="Bottom" Stretch="Uniform" Margin="5"
Source="{StaticResource Bookmarks}" Visibility="{Binding HasCustomTitle, Converter={StaticResource BoolToVisibility}}" />-->
<ContentPresenter Grid.Column="1" x:Name="content" ContentSource="Header" TextBlock.Foreground="#666"
RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="Left"
@@ -62,12 +84,18 @@
<Setter Property="Panel.ZIndex" Value="1" />
<Setter Property="Background" TargetName="bd" Value="#F9F9F9" />
<Setter Property="TextBlock.Foreground" TargetName="content" Value="black" />
<!--<Setter Property="BorderThickness" TargetName="bd" Value="0,4,0.5,0" />-->
<!--<Setter Property="Margin" Value="-2,-2,-2,-1" />
<Setter Property="Margin" TargetName="content" Value="0,0,0,1" />
<Setter Property="Margin" TargetName="bd" Value="0,0,0,0" />-->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</TabItem.Template>
<TabItem.Style>
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="HeaderTemplate" Value="{StaticResource _template}" />
<Style.Triggers>
<DataTrigger Binding="{Binding EditMode}" Value="True">
<Setter Property="HeaderTemplate" Value="{StaticResource _edit_template}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabItem.Style>
</TabItem>
@@ -18,8 +18,15 @@ namespace mbts.ui.shell {
public ImageSource IconSource { get { return _module.icon; } }
public UIElement MenuContent => new SparkMenu(_module.actions, _bus);
public void opening() { }
public void opening() {
(_module as IShellMenu)?.opening();
if (_module is ILazyMenuModule) {
RaisePropertyChanged(nameof(MenuContent));
}
}
public void closing() { }
public void closing() {
(_module as IShellMenu)?.closing();
}
}
}
@@ -24,10 +24,11 @@ namespace mbts.ui.shell
Dispatcher _dispatcher;
HwndContentHost _hwnd_host;
public ShellWindowVM(IShellWindow model, IWindowManager wm, IShellThreadFactory threads, string host = "") {
Logger.assert(model != null);
_model = model;
_model.TitleChanged += () => RaisePropertyChanged("Title");
_model.TitleChanged += () => { if (!HasCustomTitle) RaisePropertyChanged("Title"); };
_model.IconChanged += () => { _icon = null; RaisePropertyChanged("Icon"); };
_wm = wm;
host_id = host;
@@ -40,13 +41,55 @@ namespace mbts.ui.shell
if (HasTabAffinity) {
this.host_id = ((ITabAffineWindow)model).tab_group;
}
_edit_mode = backing(nameof(EditMode), false);
}
public string host_id { get; set; }
public Dispatcher Dispatcher { get { return _dispatcher; } }
public string Title { get { return _model.Title; } }
public string Title { get { return HasCustomTitle? TitleOverride : _model.Title; } }
string _title_override;
public string TitleOverride {
get => _title_override;
set {
if (_title_override != value) {
if (value == _model.Title) {
_title_override = null;
}
else {
_title_override = value;
}
RaisePropertyChanged(nameof(TitleOverride), nameof(Title), nameof(HasCustomTitle));
}
_edit_mode.set(false);
}
}
public bool HasCustomTitle => !string.IsNullOrWhiteSpace(TitleOverride);
Property<bool> _edit_mode;
public bool EditMode => _edit_mode.Value;
public ICommand rename_command => new DelegateCommand(begin_rename);
public ICommand unrename_command => new DelegateCommand(remove_custom);
private void begin_rename() {
if (string.IsNullOrEmpty(TitleOverride)) {
_title_override = Title;
}
_edit_mode.set(true);
}
private void remove_custom() {
TitleOverride = string.Empty;
//begin_close(this);
}
public string OwnerName { get { return _model.Owner == null ? "" : _model.Owner.name; } }
public ICommand CloseCommand { get { return new DelegateCommand(begin_close); } }
public double InitialWidth { get; private set; }
public double InitialHeight { get; private set; }
@@ -155,18 +198,14 @@ namespace mbts.ui.shell
}
}
public byte[] GetSaveState() {
MemoryStream stream = new MemoryStream();
_model.GetState(stream);
return stream.ToArray();
}
public IShellWindow Model { get { return _model; } }
public bool IsSlow { get { return _model is ISlowWindow; } }
public bool IsShared { get { return _model is ISharedWindow; } }
public bool HasTabAffinity { get { return _model is ITabAffineWindow; } }
public void Dispose() {
if (IsShared) {
// Shared threads continue running
+26 -13
View File
@@ -20,25 +20,31 @@ namespace mbts.ui.shell {
[Export(typeof(IWindowManager))]
[Export(typeof(IWindowsInterop))]
[Export]
public class WindowManager : IWindowManager, IWindowsInterop, IDisposable {
Dictionary<IShellWindow, TabDocument> _documents = new Dictionary<IShellWindow, TabDocument>();
Dictionary<string, FloatingWindow> _windows = new Dictionary<string, FloatingWindow>();
IShellThreadFactory _threads;
readonly IShellThreadFactory _threads;
Lazy<ISettingsCollector> _window_settings;
ISavedWindowFactory _window_factory;
IUndoActions _undo;
readonly ISavedWindowFactory _window_factory;
readonly Lazy<IBookmarks> _bookmarks;
IDisposable _dispose;
object _lock = new object();
[ImportingConstructor]
public WindowManager(
IShellThreadFactory threads, Lazy<ISettingsCollector> settings,
ISavedWindowFactory factory, IUndoActions undo, IBus bus) {
ISavedWindowFactory factory, Lazy<IBookmarks> bookmarks, IBus bus) {
_threads = threads;
_window_settings = settings;
_window_factory = factory;
_undo = undo;
_dispose = bus.subscribe(SparkTags.ShowWindow).OnDispatcher().Subscribe(w => add(w).ignore());
_bookmarks = bookmarks;
_dispose =
Disposable.Create(
bus.subscribe(SparkTags.ShowWindow).OnDispatcher().Subscribe(w => add(w).ignore()),
bus.subscribe(SparkTags.ShowWindowAsync).OnDispatcher().Subscribe(w => add(w).ignore()),
bus.subscribe(SparkTags.ActivateWindow).OnDispatcher().Subscribe(MakeVisible)
);
}
#region Window creation
@@ -48,6 +54,7 @@ namespace mbts.ui.shell {
var state = model.start_position();
if (state != null) {
viewmodel.host_id = state.host_id ?? "";
viewmodel.TitleOverride = state.custom_title;
}
var window = CreateWindow(content, state);
@@ -66,10 +73,10 @@ namespace mbts.ui.shell {
}
} else {
if (position != null) {
window = new FloatingWindow(document, this, _undo, _window_settings.Value, _window_factory, true);
window = new FloatingWindow(document, this, _bookmarks.Value, _window_settings.Value, _window_factory, true);
window.set_position(position);
} else
window = new FloatingWindow(document, this, _undo, _window_settings.Value, _window_factory);
window = new FloatingWindow(document, this, _bookmarks.Value, _window_settings.Value, _window_factory);
_windows.Add(window.host_id, window);
window.Closed += (o, e) => _windows.Remove(window.host_id);
}
@@ -81,12 +88,19 @@ namespace mbts.ui.shell {
public IReadOnlyList<IShellWindow> list_windows() =>
_documents.Keys.ToList();
public IReadOnlyList<TabDocument> list_documents() => _documents.Values.ToList();
public async Task add(IShellWindow window) {
if (!Application.Current.Dispatcher.CheckAccess())
await Application.Current.Dispatcher.InvokeAsync(() => add(window));
Logger.info(bts.log.Subsystem.Ui, $"Opening window {window.diagnostic_name()}.");
lock (_lock)
lock (_lock) {
_documents[window] = _create_document(window);
}
}
public async Task add(Task<IShellWindow> window) {
await add(await window);
}
public void remove(IShellWindow window) {
@@ -139,14 +153,13 @@ namespace mbts.ui.shell {
#region IWindowsInterop
public WindowPosition GetPosition(IShellWindow shell_window) {
return GetPosition(shell_window, null);
}
public WindowPosition GetPosition(IShellWindow shell_window, FloatingWindow owning_window) {
FloatingWindow window = owning_window == null ? window_for(shell_window) : owning_window;
FloatingWindow window = window_for(shell_window);
var doc = window.Documents.FirstOrDefault(td => td.ViewModel.Model == shell_window);
if (window != null) {
WindowPosition pos = window.get_position();
pos.tab_index = window.document_index(shell_window);
pos.host_id = window.host_id;
pos.custom_title = doc?.ViewModel?.TitleOverride;
return pos;
}
Logger.log(bts.log.Level.Warn, bts.log.Subsystem.Ui, "IShellWindow '{0}' does not have a valid position. It may not be displayed".fmt(shell_window.Title));
@@ -19,6 +19,7 @@ using mbts.persist;
namespace mbts.ui.shell
{
[Export]
public class WorkspacesModel : ViewModelBaseWithValidation
{
@@ -35,10 +36,11 @@ namespace mbts.ui.shell
private bool _closing = false;
private ILauncher _launcher;
private IUndoActions _undo;
readonly IBookmarks _bookmarks;
[ImportingConstructor]
public WorkspacesModel(ISettingsCollector settings, ISavedWindowFactory window_restore,
IWindowManager windows, ObservableCollection<IShellModule> modules, IPersist persist,
IWindowManager windows, ObservableCollection<IShellModule> modules, IPersist persist, IBookmarks bm,
IUserPersist user_persist, ILauncher launcher, IUndoActions undo, [Import("workspace")]string initial_workspace) {
_settings_collector = settings;
_window_restore = window_restore;
@@ -48,6 +50,7 @@ namespace mbts.ui.shell
_modules = modules;
_launcher = launcher;
_undo = undo;
_bookmarks = bm;
_launcher.closing += _launcher_closing;
Workspaces = new ObservableCollection<WorkspaceViewModel>();
init(initial_workspace).ignore();
@@ -150,6 +153,7 @@ namespace mbts.ui.shell
workspace.windows = _settings_collector.get_window_state().ToList();
workspace.modules = _settings_collector.get_module_state().ToList();
workspace.launcher_state = _settings_collector.get_launcher_state();
workspace.named_windows = _bookmarks.bookmarks.ToList();
return workspace;
}
@@ -166,6 +170,7 @@ namespace mbts.ui.shell
await Task.Yield();
}
workspace.launcher_state = _settings_collector.get_launcher_state();
workspace.named_windows = _bookmarks.bookmarks.ToList();
return workspace;
}
@@ -226,10 +231,15 @@ namespace mbts.ui.shell
}
}
// Notify workspace changed
if (WorkspaceLoaded != null) {
WorkspaceLoaded(name);
_bookmarks.clear();
if (def.named_windows != null) {
foreach (var window in def.named_windows) {
_bookmarks.save_bookmark(window);
}
}
// Notify workspace changed
WorkspaceLoaded?.Invoke(name);
}
public Task delete_workspace(string name, WorkspaceDefinition def) {
@@ -244,18 +254,20 @@ namespace mbts.ui.shell
}
return (def.is_shared ? _public : _private).clear(WORKSPACE_NAMESPACE, name);
}
}
// The serialized data for the Workspace
[ProtoContract(Name = "WorkspaceDefinition")]
public class WorkspaceDefinition
{
// Converted the protobuf type ShellSettings to c#
// Avoid re-using id: 1,3,4,5,6,7
[ProtoMember(2, IsRequired = false)]
public List<WindowState> windows { get; set; }
// named windows which are not currently open
[ProtoMember(3)]
public List<WindowState> named_windows { get; set; }
[ProtoMember(9, IsRequired = false)]
public List<ModuleState> modules { get; set; }
@@ -376,7 +376,7 @@ namespace mbts.spark
return x is SecurityImmutable && y is SecurityImmutable ? Compare((SecurityImmutable)x, (SecurityImmutable)y) : -1;
}
public int Compare(SecurityImmutable x, SecurityImmutable y) {
return x.EarliestExpiration().CompareTo(y.EarliestExpiration());
return SecurityHelper.Comparer.Compare(x, y);
}
@@ -1,63 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using bts.api;
using System.IO;
using mbts.ui.shell;
namespace mbts.spark
{
public class DataPersistor
{
private List<IDisposable> _disp_list = new List<IDisposable>();
private readonly IBus _bus;
private readonly string _loc = @"C:\ProgramData\bts\temp_securities.bin"; /// TODO
private SparkBusData.PersistedItems _persisted_items = new SparkBusData.PersistedItems();
public DataPersistor(IBus bus)
{
_bus = bus;
var sub = _bus.subscribe<SparkBusData.PersistMessage>(SparkTags.PersistCommandTag);
_disp_list.Add(sub.Subscribe(p => _on_new_persist_message(p)));
_persisted_items = _read_data_from_file();
_bus.post(SparkTags.PersistedItemsTag, _persisted_items);
}
private void _on_new_persist_message(SparkBusData.PersistMessage p)
{
_persisted_items.items.Add(p);
_write_to_file(_persisted_items);
_bus.post(SparkTags.PersistedItemsTag, new SparkBusData.PersistedItems(_persisted_items.items));
}
private void _write_to_file(SparkBusData.PersistedItems items)
{
byte[] data = SparkUtils.serialize_proto(items);
try
{
File.WriteAllBytes(_loc, data);
}
catch (Exception ex)
{
mbts.Logger.log(bts.log.Level.Error, bts.log.Subsystem.gen, ex.ToString());
}
}
private SparkBusData.PersistedItems _read_data_from_file()
{
var rv = new SparkBusData.PersistedItems();
try
{
if (File.Exists(_loc)) {
byte[] data = File.ReadAllBytes(_loc);
rv = SparkUtils.deserialize_proto<SparkBusData.PersistedItems>(data);
}
}
catch { }
return rv;
}
}
}
@@ -121,18 +121,6 @@ namespace mbts.spark {
public static string order_type_display_name(CustomOrderDefinition.CustomOneofCase order_type) =>
camel_to_title_case(Regex.Replace(order_type.ToString(), "Order", string.Empty, RegexOptions.IgnoreCase));
public static string order_collection_summary(ICollection<OrderRecordImmutable> orders, Comparison<double> compare_price, ContractSpecificationImmutable spec) {
if (orders.Count == 0) return string.Empty;
var price_lookup = orders.ToLookup(o => o.State.Price);
var best_price = price_lookup.MaxElement(g => g.Key, compare_price);
var best_price_volume = best_price.Sum(o => o.working_quantity());
return order_collection_summary(
best_price.Key,
best_price_volume,
best_price_volume + price_lookup.Where(g => g.Key != best_price.Key).SelectMany(g => g).Sum(o => o.working_quantity()),
spec);
}
public static string order_collection_summary(double best_price, int best_price_volume, int total_volume, ContractSpecificationImmutable spec) {
if (total_volume == 0)
return string.Empty;
+227 -140
View File
@@ -6,105 +6,40 @@ using mbts.utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace mbts.spark {
static class TickUtils {
#region TickFactors Cache
static readonly TickFactors _default_tick = new TickFactors(100, 1);
internal static ContractSpecificationImmutable spec_or_default(this ContractSpecificationImmutable? spec) =>
spec.HasValue ? spec.Value : SecurityDataHelper.Defaults.ContractSpecificationImmutable;
static readonly ConcurrentDictionary<double, bool> _resolved_unknown_ticks = new ConcurrentDictionary<double, bool>();
internal static ContractSpecificationImmutable spec_or_default(this SecurityImmutable security) =>
security.HasContractSpec ? security.ContractSpec : SecurityDataHelper.Defaults.ContractSpecificationImmutable;
static readonly ConcurrentDictionary<(double low_tick, double high_tick, double price_break), RegionalTickFactors> _regional_ticks_cache =
new ConcurrentDictionary<(double low_tick, double high_tick, double price_break), RegionalTickFactors>();
static readonly double[] _descending_tick_fractional_units;
static readonly IReadOnlyDictionary<double, TickFactors> _ticks_cache = new[] {
// 100 based
new { tick_unit = 0.01, tick_factors = new TickFactors(100, 1) }, // 1/100
new { tick_unit = 0.05, tick_factors = new TickFactors(100, 5) }, // 1/20
new { tick_unit = 0.10, tick_factors = new TickFactors(100, 10) }, // 1/10
new { tick_unit = 0.25, tick_factors = new TickFactors(100, 25) }, // 1/4
new { tick_unit = 0.50, tick_factors = new TickFactors(100, 50) }, // 1/2
// 1,000 based
new { tick_unit = 0.001, tick_factors = new TickFactors(1000, 1) }, // 1/1,000
new { tick_unit = 0.005, tick_factors = new TickFactors(1000, 5) }, // 1/200
new { tick_unit = 0.025, tick_factors = new TickFactors(1000, 25) }, // 1/40
// 10,000 based
new { tick_unit = 0.0001, tick_factors = new TickFactors(10000, 1) }, // 1/10,000
new { tick_unit = 0.0005, tick_factors = new TickFactors(10000, 5) }, // 1/2,000
new { tick_unit = 0.0025, tick_factors = new TickFactors(10000, 25) }, // 1/400
// 64 based
new { tick_unit = 0.015625, tick_factors = new TickFactors(64, 1) }, // 1/64
new { tick_unit = 0.03125, tick_factors = new TickFactors(64, 2) }, // 1/32
new { tick_unit = 0.0625, tick_factors = new TickFactors(64, 4) }, // 1/16
new { tick_unit = 0.125, tick_factors = new TickFactors(64, 8) }, // 1/8
// 640 based
new { tick_unit = 0.0015625, tick_factors = new TickFactors(640, 1) }, // 1/640
new { tick_unit = 0.003125, tick_factors = new TickFactors(640, 2) }, // 1/320
new { tick_unit = 0.00625, tick_factors = new TickFactors(640, 4) }, // 1/160
new { tick_unit = 0.0078125, tick_factors = new TickFactors(640, 5) }, // 1/128
new { tick_unit = 0.0125, tick_factors = new TickFactors(640, 8) }, // 1/80
new { tick_unit = 0.0390625, tick_factors = new TickFactors(640, 25) }, // 5/128
new { tick_unit = 0.078125, tick_factors = new TickFactors(640, 50) }, // 5/64
// 6,400 based
new { tick_unit = 0.00015625, tick_factors = new TickFactors(6400, 1) }, // 1/6,400
new { tick_unit = 0.0003125, tick_factors = new TickFactors(6400, 2) }, // 1/3,200
new { tick_unit = 0.000625, tick_factors = new TickFactors(6400, 4) }, // 1/1,600
new { tick_unit = 0.00078125, tick_factors = new TickFactors(6400, 5) }, // 1/1,280
new { tick_unit = 0.00125, tick_factors = new TickFactors(6400, 8) }, // 1/800
new { tick_unit = 0.00390625, tick_factors = new TickFactors(6400, 25) }, // 1/256
// 64,000 based
new { tick_unit = 0.000015625, tick_factors = new TickFactors(64000, 1) }, // 1/6,400
new { tick_unit = 0.00003125, tick_factors = new TickFactors(64000, 2) }, // 1/3,200
new { tick_unit = 0.0000625, tick_factors = new TickFactors(64000, 4) }, // 1/1,600
new { tick_unit = 0.000078125, tick_factors = new TickFactors(64000, 5) }, // 1/12,800
new { tick_unit = 0.000125, tick_factors = new TickFactors(64000, 8) }, // 1/8,000
new { tick_unit = 0.000390625, tick_factors = new TickFactors(64000, 25) }, // 1/2,560
new { tick_unit = 0.001953125, tick_factors = new TickFactors(64000, 125) },// 1/512
}.ToReadOnlyDictionary(obj => obj.tick_unit, obj => obj.tick_factors, EqualityComparer.Create<double>(MathUtils.epsilon_equals));
#endregion
static TickUtils() {
// overkill? Maybe, but ticks are fundamental to the entire application.
// Any changes to the tick defaults must be deliberate, and scrutinized.
Logger.assert(_ticks_cache.Count == 35);
foreach(var kv in _ticks_cache) {
double result = kv.Value.tick_unit / kv.Value.tick_base;
Logger.assert(result == kv.Key, $"{kv.Value.tick_unit}/{kv.Value.tick_base} ({result} != {kv.Key})");
}
_descending_tick_fractional_units = _ticks_cache.Keys.Ordered(new ReverseComparer<double>(Comparer<double>.Default)).ToLArray();
}
internal static ContractSpecificationImmutable spec_or_default(this SecurityImmutable? security) =>
security.HasValue && security.Value.HasContractSpec ? security.Value.ContractSpec : SecurityDataHelper.Defaults.ContractSpecificationImmutable;
public static double ticksize(this ContractSpecificationImmutable spec, double price) =>
Math.Abs(price) > spec.PriceBreak ? spec.HighTick : spec.LowTick;
public static string format(this ContractSpecificationImmutable spec, double price) {
var tick_factors = _get_tick_factors(spec.ticksize(price));
return tick_factors.format(price, false);
}
public static string format(this ContractSpecificationImmutable? spec, double price) {
if (spec.HasValue) {
return spec.Value.format(price);
}
return price.ToString("0.00000");
}
public static string format(this ContractSpecificationImmutable spec, double price) =>
TickStore.get(spec).format(price);
public static string format(this ContractSpecificationImmutable spec, double price, double display_multiplier, bool show_commas, string nan_text) {
var tick_factors = _get_tick_factors(spec.ticksize(price));
return tick_factors.format(price, display_multiplier, show_commas, nan_text);
}
public static string format(this ContractSpecificationImmutable? spec, double price) =>
spec.spec_or_default().format(price);
public static double parse(this ContractSpecificationImmutable spec, string text) {
var regional_tfs = get_regional_ticks(spec);
return regional_tfs.parse(text);
}
public static string format(this ContractSpecificationImmutable spec, double price, double display_multiplier, bool show_commas, string nan_text) =>
TickStore.get(spec).format(price, display_multiplier, show_commas, nan_text);
public static double parse(this ContractSpecificationImmutable spec, string text, double display_multiplier, string nan_text) {
var regional_tfs = get_regional_ticks(spec);
return regional_tfs.parse(text, display_multiplier, nan_text);
}
public static string format(this ContractSpecificationImmutable? spec, double price, double display_multiplier, bool show_commas, string nan_text) =>
spec.spec_or_default().format(price, display_multiplier, show_commas, nan_text);
public static double parse(this ContractSpecificationImmutable spec, string text, double display_multiplier, string nan_text) =>
TickStore.get(spec).parse(text, display_multiplier, nan_text);
public static double parse(this ContractSpecificationImmutable? spec, string text, double display_multiplier, string nan_text) =>
spec.spec_or_default().parse(text, display_multiplier, nan_text);
public static double low_tick(NativePriceSize[] bids, NativePriceSize[] asks) {
double low = bids[bids.Length - 1].price;
@@ -132,6 +67,9 @@ namespace mbts.spark {
return n_low + n_high;
}
public static int normalized_tick_diff(SecurityImmutable security, double x, double y) =>
normalized_tick_diff(security.spec_or_default(), x, y);
public static double mid_tick(ContractSpecificationImmutable spec, double bid, double ask) {
double raw_mid;
if (double.IsNaN(bid)) {
@@ -146,36 +84,35 @@ namespace mbts.spark {
return raw_mid - (raw_mid % tick_size);
}
public static IReadOnlyList<TickFactors> all_present_ticks() =>
_ticks_cache.Values.ToReadOnlyList();
public static TickFactors deduce_ticks(IEnumerable<ContractSpecification> contract_specs) {
var gcd = MathUtils.GCD(contract_specs.Select(s => s.LowTick));
if (gcd == 0) return null;
return _try_get_tick_factors(gcd);
var ticks = new HashSet<TickFactors>(contract_specs.Select(TickStore.get).SelectMany(rtf => new[] { rtf.low_tick, rtf.high_tick }));
switch (ticks.Count) {
case 0: return TickStore.default_rtf.low_tick;
case 1: return ticks.First();
default:
double tick_base_lcm = MathUtils.LCM(ticks.Select(t => (int)t.tick_base));
double tick_unit_gcd = MathUtils.GCD(ticks.Select(t => t.tick_unit * (tick_base_lcm / t.tick_base)));
return new TickFactors(tick_base_lcm, tick_unit_gcd);
}
}
public static double tickup(this ContractSpecificationImmutable contract, double value) =>
get_regional_ticks(contract).tickup(value);
public static double tickup(this ContractSpecificationImmutable spec, double value) =>
TickStore.get(spec).tickup(value);
public static double tickup(this ContractSpecificationImmutable contract, double value, int count) =>
get_regional_ticks(contract).tickup(value, (uint)count);
public static double tickup(this ContractSpecificationImmutable spec, double value, int count) =>
TickStore.get(spec).tickup(value, (uint)count);
public static double tickdown(this ContractSpecificationImmutable contract, double value) =>
get_regional_ticks(contract).tickdown(value);
public static double tickdown(this ContractSpecificationImmutable spec, double value) =>
TickStore.get(spec).tickdown(value);
public static double tickdown(this ContractSpecificationImmutable contract, double value, int count) =>
get_regional_ticks(contract).tickdown(value, (uint)count);
public static double tickdown(this ContractSpecificationImmutable spec, double value, int count) =>
TickStore.get(spec).tickdown(value, (uint)count);
public static bool tick_equals(this ContractSpecificationImmutable contract, double x, double y) =>
MathUtils.RoundFractionalUnits(x, contract.LowTick) == MathUtils.RoundFractionalUnits(y, contract.LowTick);
public static IEqualityComparer<double> tick_equality_comparer(this ContractSpecificationImmutable contract)
{
var eq = EqualityComparer.Create((double a, double b) => TickUtils.tick_equals(contract, a, b),
x => MathUtils.RoundFractionalUnits(x, contract.LowTick));
return eq;
}
public static IEqualityComparer<double> tick_equality_comparer(this ContractSpecificationImmutable contract) =>
EqualityComparer.Create((double a, double b) => tick_equals(contract, a, b), x => MathUtils.RoundFractionalUnits(x, contract.LowTick));
public static double round_to_tick(this ContractSpecificationImmutable spec, double value) {
double intpart = (long)value;
@@ -186,46 +123,196 @@ namespace mbts.spark {
}
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable spec) =>
_regional_ticks_cache.GetOrAdd((spec.LowTick, spec.HighTick, spec.PriceBreak), _create_regional_ticks);
TickStore.get(spec);
public static RegionalTickFactors get_regional_ticks(ContractSpecification spec) =>
_regional_ticks_cache.GetOrAdd((spec.LowTick, spec.HighTick, spec.PriceBreak), _create_regional_ticks);
public static RegionalTickFactors get_regional_ticks(SecurityImmutable security) =>
get_regional_ticks(security.spec_or_default());
static RegionalTickFactors _create_regional_ticks((double low_tick, double high_tick, double price_break) def) =>
new RegionalTickFactors(_get_tick_factors(def.low_tick), _get_tick_factors(def.high_tick), def.price_break);
static TickFactors _inflate_to_base(TickFactors tick_factors, double new_base) =>
new TickFactors(new_base, tick_factors.tick_unit * new_base / tick_factors.tick_base);
static TickFactors _get_tick_factors(double tick) {
if (_ticks_cache.TryGetValue(tick, out TickFactors result))
return result;
// try to return the largest known tick that is divisible by this tick
foreach (var tfu in _descending_tick_fractional_units) {
double mod = tick % tfu;
if (MathUtils.epsilon_equals(mod, 0)) {
if (_resolved_unknown_ticks.TryAdd(tick, true))
Logger.warn(bts.log.Subsystem.Ui, $"Resolved unknown tick {tick} to {tfu}, if this tick is expected, talk to John about adding it to the hard-coded defaults ASAP.");
return _ticks_cache[tfu];
}
public static class CompareUtils {
public static IComparer<double> base_comparer =>
Comparer<double>.Create(_compare_base);
public static IComparer<double> unit_comparer =>
Comparer<double>.Create(_compare_unit);
public static IComparer<TickFactors> base_unit_comparer =>
Comparer<TickFactors>.Create(utils.CompareUtils.safe_comparison<TickFactors>(compare_base_unit));
public static int compare_base_unit(TickFactors a, TickFactors b) {
int comp = _compare_base(a.tick_base, b.tick_base);
return comp == 0 ? a.tick_unit.CompareTo(b.tick_unit) : comp;
}
static int _compare_base(double a, double b) {
double reduced_a = MathUtils.factor_out(a, 10);
double reduced_b = MathUtils.factor_out(b, 10);
int comp = reduced_a.CompareTo(reduced_b);
return comp == 0 ? a.CompareTo(b) : comp;
}
static int _compare_unit(double a, double b) =>
_compare_unit(a, b, 0, 0);
static int _compare_unit(double a, double b, int a_mult10, int b_mult10) {
// fractional-coins are a special case, as they may not be an integer to begin with
if (!MathUtils.is_int(a))
return _compare_unit(a * 10, b, ++a_mult10, b_mult10);
if (!MathUtils.is_int(b))
return _compare_unit(a, b * 10, a_mult10, ++b_mult10);
double reduced_a = a_mult10 > 0 ? a : MathUtils.factor_out(a, 10, 5);
double reduced_b = b_mult10 > 0 ? b : MathUtils.factor_out(b, 10, 5);
int comp = reduced_a.CompareTo(reduced_b);
return comp == 0 ? (a * Math.Pow(10, -a_mult10)).CompareTo(b * Math.Pow(10, -b_mult10)) : comp;
}
Logger.assert(false, $"Unable to resolve tick ({tick}): going down because the application cannot run properly with unresolved ticks. " +
$"FOR DEVS: If this tick was not caused by a bug, please talk to John about adding it to the hard-coded defaults. " +
$"If you believe this tick belongs to a synthetic security, and should not be in the defaults, please delete said synthetic from the db " +
$"(note: the synthetic creation dialog has changed to only allow known ticks from now on, so if you get the latest code and delete old synthetics from the db, this will not happen again). " +
$"Direct any further questions to John.");
return null;
}
// Almost the same function as _get_tick_factors, only difference being no assert/log.
// This seemed more practical than trying to factor this behavior out, it's only a few lines of code.
static TickFactors _try_get_tick_factors(double tick) {
if (_ticks_cache.TryGetValue(tick, out TickFactors result))
return result;
// try to return the largest known tick that is divisible by this tick
foreach (var tfu in _descending_tick_fractional_units) {
double mod = tick % tfu;
if (MathUtils.epsilon_equals(mod, 0))
return _ticks_cache[tfu];
static class TickStore {
public static readonly RegionalTickFactors default_rtf = new RegionalTickFactors(new TickFactors(1000, 1), new TickFactors(1000, 1), double.MaxValue);
static readonly ConcurrentDictionary<SpecKey, RegionalTickFactors> _exchange_ticks = new ConcurrentDictionary<SpecKey, RegionalTickFactors>();
public static RegionalTickFactors get(ContractSpecification spec) =>
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
public static RegionalTickFactors get(ContractSpecificationImmutable spec) =>
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
public static RegionalTickFactors get(SecurityImmutable security) =>
security.HasContractSpec ? get(security.ContractSpec) : default_rtf;
public static RegionalTickFactors get(SecurityImmutable? security) =>
security.HasValue ? get(security.Value) : default_rtf;
static RegionalTickFactors _create_rtf(SpecKey key) {
double tick_base = key.denominator;
if (tick_base == 0) {
// indicates tick is a decimal
tick_base = 100;
}
return _create_rtf(key.low_tick, key.high_tick, key.price_break, tick_base, key.display_digits);
}
static RegionalTickFactors _create_rtf(double low_tick, double high_tick, double price_break, double reduced_target_base, uint display_digits_override) {
var low_tf = _try_create_tf(low_tick, reduced_target_base, display_digits_override);
var high_tf = _try_create_tf(high_tick, reduced_target_base, display_digits_override);
if (low_tf == null) {
low_tf = high_tf ?? (high_tf = default_rtf.low_tick);
} else if (high_tf == null)
high_tf = low_tf;
else {
// if low/high tick-bases do not match, inflate the smaller base so they do
// Note: this assumes the tick-bases are related by factors of 10 (i.e. they have all the same prime factors except for a different number of pairs of 5 and 2, e.g. 64 and 640)
if (low_tf.tick_base > high_tf.tick_base) {
high_tf = _inflate_to_base(high_tf, low_tf.tick_base);
} else if (low_tf.tick_base < high_tf.tick_base) {
low_tf = _inflate_to_base(low_tf, high_tf.tick_base);
}
}
return new RegionalTickFactors(low_tf, high_tf, price_break);
}
/// <summary>
/// Creates TickFactors with a base equal to &quot;<paramref name="tick_base"/> * 10^n,&quot; where 'n' is a natural number (including 0).
/// <para>Note: If <paramref name="display_digits_override"/> is non-zero, 'n' will be &quot;<paramref name="display_digits_override"/> - Math.Ceiling(Log10(<paramref name="tick_base"/>))&quot;</para>
/// </summary>
/// <param name="tick_size">The raw tick-size.</param>
/// <param name="tick_base">The raw tick-base.</param>
/// <returns>Well-defined TickFactors</returns>
static TickFactors _try_create_tf(double tick_size, double tick_base, uint display_digits_override) {
var tf = _try_create_tf(tick_size, tick_base);
if (tf != null && display_digits_override > 0) {
int display_digit_diff = (int)display_digits_override - tf.display_digits();
if (display_digit_diff != 0)
tf = _inflate_to_base(tf, tf.tick_base * Math.Pow(10, display_digit_diff));
}
return tf;
}
/// <summary>
/// Creates TickFactors with a base equal to &quot;reduced_target_base * 10^n,&quot; where 'n' is a natural number (including 0).
/// </summary>
/// <param name="tick_size">The raw tick-size.</param>
/// <param name="reduced_target_base">The smallest acceptable tick-base.</param>
/// <returns>Well-defined TickFactors</returns>
static TickFactors _try_create_tf(double tick_size, double reduced_target_base) {
if (!_check_tick_size(tick_size))
return null;
double raw_base = 1 / tick_size;
double raw_base_log10 = Math.Log10(raw_base);
double reduced_target_base_log10 = Math.Log10(reduced_target_base);
double log10_diff = raw_base_log10 - reduced_target_base_log10;
if (MathUtils.is_int(log10_diff))
return raw_base > reduced_target_base ? new TickFactors(raw_base, 1) : new TickFactors(reduced_target_base, Math.Pow(10, -log10_diff));
int count = 0;
double factored_base = log10_diff > 0 ? reduced_target_base * Math.Pow(10, Math.Ceiling(log10_diff)) : reduced_target_base;
do {
if (factored_base % raw_base == 0)
return new TickFactors(factored_base, factored_base / raw_base);
factored_base *= 10;
} while (++count < 10);
return null;
}
static TickFactors _create_tf(double tick_size) {
if (!_check_tick_size(tick_size))
return default_rtf.low_tick;
double matched_unit = double.MinValue;
TickFactors matched_tf = null;
foreach (var kv in _exchange_ticks) {
if (kv.Key.high_tick == tick_size) {
return kv.Value.high_tick;
} else if (kv.Key.low_tick == tick_size) {
return kv.Value.low_tick;
} else if (is_better_match(kv.Key.high_tick)) {
matched_unit = kv.Key.high_tick;
matched_tf = kv.Value.high_tick;
} else if (is_better_match(kv.Key.low_tick)) {
matched_unit = kv.Key.low_tick;
matched_tf = kv.Value.low_tick;
}
}
return matched_tf ?? new TickFactors(1.0 / tick_size, 1);
bool is_better_match(double known_unit) =>
known_unit < tick_size && known_unit > matched_unit && tick_size % known_unit == 0;
}
static bool _check_tick_size(double tick_size) =>
tick_size > 0 && tick_size <= 0.5;
struct SpecKey : IEquatable<SpecKey> {
public readonly uint denominator, display_digits;
public readonly double low_tick, high_tick, price_break;
public SpecKey(ContractSpecification spec)
: this(spec.MainFraction, spec.PriceDisplayFormat, spec.LowTick, spec.HighTick, spec.PriceBreak) { }
public SpecKey(ContractSpecificationImmutable spec)
: this(spec.MainFraction, spec.PriceDisplayFormat, spec.LowTick, spec.HighTick, spec.PriceBreak) { }
public SpecKey(uint denominator, uint display_digits, double low_tick, double high_tick, double price_break) {
this.denominator = denominator;
this.display_digits = display_digits;
this.low_tick = low_tick;
this.high_tick = high_tick;
this.price_break = price_break;
}
public bool Equals(SpecKey other) =>
denominator == other.denominator &&
display_digits == other.display_digits &&
low_tick == other.low_tick &&
high_tick == other.high_tick &&
price_break == other.price_break;
public override bool Equals(object obj) =>
obj is SpecKey sk && Equals(sk);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash((int)denominator, (int)display_digits, low_tick.GetHashCode(), high_tick.GetHashCode(), price_break.GetHashCode());
}
return null;
}
}
}
}
@@ -1,5 +1,4 @@
using bts.api;
using bts.api.orderman;
using mbts.co2;
using System.Threading.Tasks;
@@ -8,15 +7,15 @@ namespace mbts.spark {
readonly OrdersModel _order_gateway;
public LadderModel(SecurityMD source, OrdersModel ordergw) {
security = source.security;
md = source.md;
security_md = source;
_order_gateway = ordergw;
}
public enum CancelType { All, Bids, Asks }
public SecurityImmutable security { get; }
public MDConsumerHandle md { get; }
public SecurityMD security_md { get; }
public SecurityImmutable security => security_md.security;
public MDConsumerHandle md => security_md.md;
public OrdersModel order_gateway => _order_gateway;
public Task cancel_orders(CancelType ct) {
@@ -1,6 +1,5 @@
using bts.api;
using bts.api.orderman;
using bts.spark;
using mbts.ui;
using mbts.ui.shell;
using System;
@@ -71,7 +71,7 @@ namespace mbts.spark {
_dispose = Disposable.Create(
settings_command,
order_properties_vm = new OrderPropertiesEditorVM(model.order_gateway, Observable.Return(security), _settings_manager.subscribe_current().Select(s => new InlineOrderProperties(model.order_gateway, s))),
order_properties_vm = new OrderPropertiesEditorVM(model.order_gateway, Observable.Return(model.security_md), _settings_manager.subscribe_current().Select(s => new InlineOrderProperties(model.order_gateway, s))),
size_calculator = new CalculatorLogic(0, _settings_manager.subscribe_current().Select(s => s.calculator_buttons)),
bus.subscribe(SparkTags.LogActivity).OnDispatcher().Subscribe(status_update),
model.order_gateway.working_orders.OnDispatcher().Subscribe(working => {
@@ -143,6 +143,8 @@ namespace mbts.spark {
);
}
public IBus bus => _bus;
public event Action on_tick;
public MDConsumerHandle md => _model.md;
@@ -483,6 +483,7 @@
Background="{x:Null}" BorderBrush="{x:Null}"
PieBackground="Transparent"
SliceBackground="#a000"
Bus="{Binding bus}"
Command="{Binding RecenterCommand, ElementName=_ladder}">
<spark:RecenterCountdown.SliceStroke>
<Pen Thickness="1" Brush="Black" />
@@ -67,8 +67,8 @@ namespace mbts.spark {
double up = mid, down = mid;
// Starting from the middle iterate out to the edges
for (int x = 1; x <= range; ++x) {
up = TickUtils.tickup(spec, up);
down = TickUtils.tickdown(spec, down);
up = spec.tickup(up);
down = spec.tickdown(down);
result[middle - x] = up;
result[middle + x] = down;
}
Loaded 100 of 343 files, more files were not shown because too many files have changed in this diff. Show more