Compare commits

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

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

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

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

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-07-19 15:55:07 +00:00
Pedro Pinto bebbf2a0f1 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-07-19 15:48:08 +00:00
Pedro Pinto 7413814e4a Fixed script permissions. 2018-07-19 15:42:49 +00:00
Pedro Pinto 36474ef3ea SPARK-1442, Handling leg fills. 2018-07-19 15:42:10 +00:00
Taha Afzal 974be037fe forgot pushing this merge in 2018-07-19 11:23:56 -04:00
Eric Harding a9ee246436 Share MDConsumer between MDSource and SecuritySrc. Fixes problem with synthetics when nobody calls scan_securities() to do security discovery 2018-07-19 10:44:58 -04:00
Taha Afzal b692742e30 better error messages for TriggerOrder 2018-07-19 10:36:37 -04:00
Taha Afzal c61cbba316 TriggerOrder incorrect assertion 2018-07-19 10:36:37 -04:00
Kevin Darby 4d99ffc86c SPARK-1464
I couldn't get the order price to default to the bid, but sending to John
2018-07-19 10:36:37 -04:00
Eric Harding 52001a55c6 MDConsumer::get_handle() needs to process async work before searching handles 2018-07-19 10:36:37 -04:00
Eric Harding 570dd387d2 MDConsumer::get_handle() needs to check if the handle is a synthetic 2018-07-19 10:36:37 -04:00
Taha Afzal 28a122aa7a TriggerOrder completed 2018-07-19 10:36:36 -04:00
Taha Afzal b2facaecd9 polishing TriggerOrder 2018-07-19 10:36:36 -04:00
Taha Afzal e346858516 2 things left, completed vs cancelled, and price rounding for equality 2018-07-19 10:36:36 -04:00
Taha Afzal be8ca67be5 TriggerOrder handling cancelled state better 2018-07-19 10:36:36 -04:00
Kevin Darby 197e68e357 Trigger order UI 2018-07-19 10:36:36 -04:00
Kevin Darby dfa3e4a38d python... 2018-07-19 10:36:36 -04:00
Taha Afzal 0f7625ff4a Conditional Order mostly done 2018-07-19 10:36:36 -04:00
Taha Afzal 88ab4fbb79 fixing build 2018-07-19 10:36:36 -04:00
Taha Afzal 5cc9b8c875 SPARK-1404 trigger order proto's 2018-07-19 10:36:36 -04:00
Pedro Pinto 187793d47c Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-07-19 13:58:39 +00:00
John Colvin 656c7104f6 fix ticks merge from prod to master 2018-07-18 18:14:24 -04:00
Pedro Pinto 359e01e8bd Merged 2018-07-18 22:07:36 +00:00
John Colvin 2a452d3201 Merge branch 'prod' (ticks) 2018-07-18 17:26:59 -04:00
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
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
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
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
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
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
304 changed files with 14655 additions and 6069 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>();
}
}
+1
View File
@@ -107,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"];
}
+13
View File
@@ -753,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
@@ -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;
}
}
}
@@ -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;
}
}
}
}
@@ -335,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));
}
}
}
}
+1
View File
@@ -42,6 +42,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())
+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);
}
}
+7 -3
View File
@@ -19,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)>();
@@ -67,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;
}
}
}
@@ -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);
}
}
@@ -39,4 +39,4 @@ namespace mbts.utils {
}
#endregion
}
}
}
@@ -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>
@@ -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);
}
}
@@ -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
}
}
@@ -93,7 +93,9 @@ namespace mbts.ui.shell
_recently_closed_windows.Push(() => { undoaction(); });
}
}
_bookmark.save_bookmark(document.Model);
if (document.HasCustomTitle) {
_bookmark.save_bookmark(document.Model);
}
}
public void remove_document(TabDocument document) {
+2 -1
View File
@@ -25,7 +25,8 @@
ui:TextBoxBehavior.CommitOnEnter="True"
ui:TextBoxBehavior.FocusOnLoaded="True"
ui:TextBoxBehavior.SelectAllOnFocus="True"
ui:TextBoxBehavior.ReleaseOnEscape="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">
@@ -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;
@@ -315,4 +315,4 @@ namespace mbts.spark {
}
}
}
}
}
@@ -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;
}
@@ -1,81 +1,56 @@
using bts.api;
using bts.api;
using mbts.co2;
using mbts.ui.controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Threading;
namespace mbts.spark {
// Implement the recenter logic in a UI control
// Kind of a hack but it makes receiving input from the UI easier
public class RecenterCountdown : CountDownTimer {
DispatcherTimer _timer = new DispatcherTimer(DispatcherPriority.Background) {
Interval = TimeSpan.FromSeconds(1)
};
SecurityImmutable? _security;
MDConsumerHandle _handle;
DateTime _last_recenter = DateTime.MaxValue;
double _last_mid = 0.0;
IDisposable _dispose;
readonly TimeSpan _seconds_to_center;
IBus _bus;
public RecenterCountdown() {
DataContextChanged += RecenterCountdown_DataContextChanged;
Unloaded += (o,e) => on_unload(false);
_timer.Tick += _timer_Tick;
_timer.Start();
_seconds_to_center = TimeSpan.FromSeconds(config.Configuration.get<bts.config.LadderKnobs>().seconds_to_recenter);
Unloaded += (o, e) => on_unload(false);
}
static (double,double) tick_range(double price, int tick_count, ContractSpecificationImmutable spec) {
return (TickUtils.tickdown(spec, price, tick_count), TickUtils.tickup(spec, price, tick_count));
public IBus Bus {
get { return (IBus)GetValue(busProperty); }
set { SetValue(busProperty, value); }
}
private void _timer_Tick(object sender, EventArgs e) {
if (_security != null) {
var mid = _handle.mid();
if (//mid < low || mid > high ||
(_last_recenter + _seconds_to_center < DateTime.UtcNow)) {
trigger_recenter(mid);
}
public static readonly DependencyProperty busProperty =
DependencyProperty.Register("Bus", typeof(IBus), typeof(RecenterCountdown), new PropertyMetadata(null, bus_changed));
static void bus_changed(object sender, DependencyPropertyChangedEventArgs args) {
if (sender is RecenterCountdown self) {
self.setbus(args.NewValue as IBus);
}
}
void setbus(IBus value) {
if (value != null) {
Disposable.dispose(ref _dispose);
_bus = value;
_dispose =
Disposable.Create(
_bus?.subscribe(SparkTags.RecenterLadderBegin).OnDispatcher().Subscribe(begin_recenter),
_bus?.subscribe(SparkTags.RecenterLadderNow).OnDispatcher().Subscribe(_ => base.OnClick())
);
}
}
void begin_recenter(DateTime _) => start(null);
private void on_unload(bool _) {
Disposable.dispose(ref _dispose);
_timer.Stop();
}
private void RecenterCountdown_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) {
Disposable.dispose(ref _dispose);
var vm = e.NewValue as LadderVM;
if (vm != null) {
_security = vm.security;
_handle = vm.md;
reset_counters();
vm.OnDisposed += on_unload;
_dispose =
Disposable.Create(
Disposable.Create(() => vm.OnDisposed -= on_unload));
}
}
void trigger_recenter(double mid) {
reset_counters();
start(null);
}
protected override void OnClick() {
reset_counters();
base.OnClick();
}
void reset_counters() {
_last_recenter = DateTime.UtcNow;
_last_mid = _handle.mid();
_bus?.post(SparkTags.RequestRecenterLadder, null);
}
}
}
+146 -65
View File
@@ -2,6 +2,7 @@
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -10,69 +11,111 @@ using System.Linq;
using System.Reactive.Linq;
using System.Windows.Data;
using static bts.shell.PAndLWindowFactory;
using static bts.api.orderman.Fill.Types;
namespace mbts.spark {
class PAndLVM : DisposableViewModelBase {
public PAndLVM(IBus bus, PAndLModel model, PAndLStartupData state) {
public PAndLVM(IBus bus, PAndLModel model, PAndLStartupData startup_data) {
_bus = bus;
_model = model;
_selected_pnl_calc_type = backing<pnl_calc_type>(nameof(selected_pnl_calc_type));
// get initial value from settings
// else default to midpoint
// TODO:
// need to respond to settings change somehow as the
// combobox that selected_pnl_calc_type is bound to will be
// gone from this form.
// ditto start of day changes, but this will be a bit more involved
// since we'll need to create a new PnLCalculator when this changes. (infrequent, if ever)
_selected_pnl_calc_type.Value = (startup_data != null && startup_data.calc_type != pnl_calc_type.none) ? startup_data.calc_type : pnl_calc_type.midpoint;
_selected_pnl_calc_type.Changed += pnl_calc_type_changed;
_start_of_day_ts = (startup_data != null && startup_data.start_of_day_time.HasValue) ? startup_data.start_of_day_time.Value : new TimeSpan(16, 30, 0);
var pnl = model.on_pnl_data
.Sample(_POSITION_UPDATE_PERIOD)
.OnDispatcher()
.Subscribe(p => {
if (p != null) {
PnLCalculator calc = new PnLCalculator(p.state.Values);
PnLCalculator calc = new PnLCalculator(p.state.Values, _selected_pnl_calc_type.Value, _start_of_day_ts);
var summary_position_rows = calc.get_summary_position();
_pnl_data.sync_with(summary_position_rows);
_last_pnl_update_time = DateTime.Now;
}
});
pnl_view = CollectionViewSource.GetDefaultView(_pnl_data);
pnl_view.SetSortingCriteria(
new List<SortingCriteria>() {
new SortingCriteria("account", true)
, new SortingCriteria("exchange_string", true)
, new SortingCriteria("symbol", true)
, new SortingCriteria("security_text", true)
}
);
// restore filter state
var fs = startup_data?.filters;
if (fs != null) {
account_filter.load_state(fs.account_regex);
exchange_string_filter.load_state(fs.exchange_regex);
symbol_filter.load_state(fs.symbol_regex);
security_filter.load_state(fs.security_regex);
start_position_filter.load_state(fs.start_net_pos);
start_pnl_filter.load_state(fs.start_pnl);
day_long_quantity_filter.load_state(fs.day_long_pos);
day_short_quantity_filter.load_state(fs.day_short_pos);
day_pnl_filter.load_state(fs.day_pnl);
total_net_position_filter.load_state(fs.total_net_pos);
total_pnl_filter.load_state(fs.total_pnl);
pnl_price_filter.load_state(fs.pnl_price);
security_type_filter.load_state(fs.security_types);
}
// restore sort state or supply default value
if (startup_data?.sort_criteria?.Any() == true) {
pnl_view.SetSortingCriteria(startup_data.sort_criteria);
} else {
pnl_view.SetSortingCriteria(new List<SortingCriteria>() {
new SortingCriteria(nameof(PnLSummaryRow.account), true)
, new SortingCriteria(nameof(PnLSummaryRow.exchange_string), true)
, new SortingCriteria(nameof(PnLSummaryRow.symbol), true)
, new SortingCriteria(nameof(PnLSummaryRow.security_text), true)
});
}
pnl_view.CollectionChanged += Pnl_view_CollectionChanged;
//Account[] accounts =
#region Create grid filters and load state
#endregion
// setup the main Disposable and
// hook up grid filters
_dispose = Disposable.Create(pnl,
DataGridFilterHelper.hook_filters(pnl_view,
security_filter, pnl_filter,
buy_quantity_filter, sell_quantity_filter,
net_position_filter, weighted_average_buy_filter,
weighted_average_sell_filter, pnl_price_filter,
sod_position_filter, account_filter,
exchange_string_filter, symbol_filter),
account_filter,
exchange_string_filter,
symbol_filter,
security_filter,
start_position_filter,
start_pnl_filter,
day_long_quantity_filter,
day_short_quantity_filter,
day_pnl_filter,
total_net_position_filter,
total_pnl_filter,
pnl_price_filter,
weighted_average_buy_filter,
weighted_average_sell_filter,
security_type_filter),
WorkLoop.Start("PAndL", _UPDATE_PERIOD, _calculate_pnl)
);
}
protected override void Dispose(bool disposing) {
pnl_view.CollectionChanged -= Pnl_view_CollectionChanged;
}
private void Pnl_view_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
_update_totals();
}
private void pnl_calc_type_changed(pnl_calc_type oldval, pnl_calc_type newval) {
_calculate_pnl();
}
protected override void Dispose(bool disposing) {
pnl_view.CollectionChanged -= Pnl_view_CollectionChanged;
_selected_pnl_calc_type.Changed -= pnl_calc_type_changed;
}
void _update_totals() {
if (pnl_view != null) {
total_pnl = pnl_view.Cast<PnLSummaryRow>().Where(r => !Double.IsNaN(r.pnl)).Sum(r => r.pnl);
total_net_pos = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.net_position);
total_buy_qty = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.buy_quantity);
total_sell_qty = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.sell_quantity);
total_aggregate_pnl = pnl_view.Cast<PnLSummaryRow>().Where(r => !Double.IsNaN(r.total_pnl)).Sum(r => r.total_pnl);
total_aggregate_net_pos = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.total_net_position);
total_aggregate_buy_qty = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.day_long_quantity);
total_aggregate_sell_qty = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.day_short_quantity);
}
}
@@ -81,75 +124,113 @@ namespace mbts.spark {
// position update in a while
if (DateTime.Now.Subtract(_last_pnl_update_time) > _UPDATE_PERIOD) {
foreach (var r in _pnl_data) {
r.update_pnl(pnl_calc_type.waterfall);
if (_selected_pnl_calc_type.Value != pnl_calc_type.none) {
r.update_pnl(_selected_pnl_calc_type.Value);
}
}
_update_totals();
}
}
private DateTime _last_pnl_update_time = DateTime.MinValue;
private ObservableCollection<PnLSummaryRow> _pnl_data = new ObservableCollection<PnLSummaryRow>();
#region Properties
public ICollectionView pnl_view { get; }
private double _total_pnl;
public double total_pnl {
public List<pnl_calc_type> pnl_calc_types { get; } = EnumHelper.get_values_except(pnl_calc_type.none, pnl_calc_type.waterfall, pnl_calc_type.bid_or_ask, pnl_calc_type.open, pnl_calc_type.close).ToList();
public pnl_calc_type selected_pnl_calc_type {
get => _selected_pnl_calc_type.Value;
set => _selected_pnl_calc_type.Value = value;
}
public double total_aggregate_pnl {
get {
return _total_pnl;
return _total_aggregate_pnl;
}
set {
_total_pnl = value;
RaisePropertyChanged(nameof(total_pnl));
_total_aggregate_pnl = value;
RaisePropertyChanged();
}
}
private double _total_net_pos;
public double total_net_pos {
get { return _total_net_pos; }
public double total_aggregate_net_pos {
get { return _total_aggregate_net_pos; }
set {
_total_net_pos = value;
RaisePropertyChanged(nameof(total_net_pos));
_total_aggregate_net_pos = value;
RaisePropertyChanged();
}
}
private double _total_buy_qty;
public double total_buy_qty {
get { return _total_buy_qty; }
public double total_aggregate_buy_qty {
get { return _total_aggregate_buy_qty; }
set {
_total_buy_qty = value;
RaisePropertyChanged(nameof(total_buy_qty));
_total_aggregate_buy_qty = value;
RaisePropertyChanged();
}
}
private double _total_sell_qty;
public double total_sell_qty {
get { return _total_sell_qty; }
public double total_aggregate_sell_qty {
get { return _total_aggregate_sell_qty; }
set {
_total_sell_qty = value;
RaisePropertyChanged(nameof(total_sell_qty));
_total_aggregate_sell_qty = value;
RaisePropertyChanged();
}
}
public TimeSpan start_of_day {
get { return _start_of_day_ts; }
}
#region Filters
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> security_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.security_text, nameof(PnLSummaryRow.security_text));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.pnl, nameof(PnLSummaryRow.pnl));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> buy_quantity_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.buy_quantity, nameof(PnLSummaryRow.buy_quantity));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> sell_quantity_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.sell_quantity, nameof(PnLSummaryRow.sell_quantity));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> net_position_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.net_position, nameof(PnLSummaryRow.net_position));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_buy_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_buy, nameof(PnLSummaryRow.weighted_average_buy));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_sell_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_sell, nameof(PnLSummaryRow.weighted_average_sell));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> pnl_price_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.pnl_price, nameof(PnLSummaryRow.pnl_price));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> sod_position_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.sod_position, nameof(PnLSummaryRow.sod_position));
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> account_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.account, nameof(PnLSummaryRow.account));
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> exchange_string_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.exchange_string, nameof(PnLSummaryRow.exchange_string));
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> symbol_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.symbol, nameof(PnLSummaryRow.symbol));
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> security_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.security_text, nameof(PnLSummaryRow.security_text));
public IDataGridColumnPersistentFilter<PnLSummaryRow, IntMinMaxState> start_position_filter { get; } = new IntMinMax<PnLSummaryRow>(r => r.start_net_position, nameof(PnLSummaryRow.start_net_position));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> start_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.start_pnl, nameof(PnLSummaryRow.start_pnl));
public IDataGridColumnPersistentFilter<PnLSummaryRow, UIntMinMaxState> day_long_quantity_filter { get; } = new UIntMinMax<PnLSummaryRow>(r => r.day_long_quantity, nameof(PnLSummaryRow.day_long_quantity));
public IDataGridColumnPersistentFilter<PnLSummaryRow, UIntMinMaxState> day_short_quantity_filter { get; } = new UIntMinMax<PnLSummaryRow>(r => r.day_short_quantity, nameof(PnLSummaryRow.day_short_quantity));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> day_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.day_pnl, nameof(PnLSummaryRow.day_pnl));
public IDataGridColumnPersistentFilter<PnLSummaryRow, IntMinMaxState> total_net_position_filter { get; } = new IntMinMax<PnLSummaryRow>(r => r.total_net_position, nameof(PnLSummaryRow.total_net_position));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> total_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.total_pnl, nameof(PnLSummaryRow.total_pnl));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> pnl_price_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.pnl_price, nameof(PnLSummaryRow.pnl_price));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_buy_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_buy, nameof(PnLSummaryRow.weighted_average_buy));
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_sell_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_sell, nameof(PnLSummaryRow.weighted_average_sell));
public IDataGridColumnPersistentFilter<PnLSummaryRow, SecurityType[]> security_type_filter { get; } = DiscreteValuesFilterVM.Create<PnLSummaryRow, SecurityType>(EnumHelper.get_values_except<SecurityType>(SecurityType.Option, SecurityType.Stock, SecurityType.Synthetic).ToArray(), r => r.security_type, nameof(PnLSummaryRow.security_type));
public FilterStates get_filters_states() => new FilterStates {
account_regex = account_filter.get_state(),
exchange_regex = exchange_string_filter.get_state(),
symbol_regex = exchange_string_filter.get_state(),
security_regex = security_filter.get_state(),
start_net_pos = start_position_filter.get_state(),
start_pnl = start_pnl_filter.get_state(),
day_long_pos = day_long_quantity_filter.get_state(),
day_short_pos = day_short_quantity_filter.get_state(),
day_pnl = day_pnl_filter.get_state(),
total_net_pos = total_net_position_filter.get_state(),
total_pnl = total_pnl_filter.get_state(),
pnl_price = pnl_price_filter.get_state(),
security_types = security_type_filter.get_state()
};
#endregion
public List<SortingCriteria> get_sorting_states() => pnl_view.GetSortingCriteria();
#endregion
readonly IBus _bus;
readonly PAndLModel _model;
static readonly TimeSpan _UPDATE_PERIOD = TimeSpan.FromSeconds(1);
static readonly TimeSpan _POSITION_UPDATE_PERIOD = TimeSpan.FromMilliseconds(333);
readonly Property<pnl_calc_type> _selected_pnl_calc_type;
double _total_aggregate_pnl, _total_aggregate_net_pos, _total_aggregate_buy_qty, _total_aggregate_sell_qty;
TimeSpan _start_of_day_ts;
DateTime _last_pnl_update_time = DateTime.MinValue;
ObservableCollection<PnLSummaryRow> _pnl_data = new ObservableCollection<PnLSummaryRow>();
}
@@ -18,19 +18,26 @@
<Style x:Key="RightAlignedColumn" TargetType="{x:Type TextBlock}">
<Setter Property="TextAlignment" Value="Right" />
</Style>
<convert:DoubleTextConverter x:Key="DoubleWithNaNHiddenConverter" Format="F2" />
<convert:NumericSignBrushConverter x:Key="PnLBackgroundColorConverter" Opacity="0.33"/>
<convert:DoubleTextConverter x:Key="DoubleWithNaNHiddenConverter" Format="N0" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<!-- Table -->
<RowDefinition Height="Auto"/>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<c:DataGridEx Grid.Row="0" Grid.Column="0" x:Name="_grid" IsReadOnly="True" BorderThickness="0" GridLinesVisibility="None" CanUserSortColumns="True" SelectionUnit="FullRow"
<ComboBox Grid.Row="0"
ItemsSource="{Binding pnl_calc_types}"
SelectedItem="{Binding selected_pnl_calc_type}"
ItemTemplate="{StaticResource EnumDescriptionTemplate}">
</ComboBox>
<c:DataGridEx Grid.Row="1" Grid.Column="0" x:Name="_grid" IsReadOnly="True" BorderThickness="0" GridLinesVisibility="None" CanUserSortColumns="True" SelectionUnit="FullRow"
ItemsSource="{Binding pnl_view}" uc:DataGridBehavior.NotifyColumnsOfSorting="True">
<DataGrid.Columns>
<DataGridTextColumn x:Name="account" Header="Account" Binding="{Binding account}" IsReadOnly="true"
@@ -57,46 +64,78 @@
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
/>
<DataGridTextColumn x:Name="sod" Header="SOD" Binding="{Binding sod_position}" IsReadOnly="true"
<DataGridTextColumn x:Name="security_type" Header="Security Type" Binding="{Binding security_type}" IsReadOnly="true"
c:DataGridFilterProperties.Filter="{Binding DataContext.security_type_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_type_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_type_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"
/>
<DataGridTextColumn x:Name="start_net_pos" Header="Start Net Pos" Binding="{Binding start_net_position}" IsReadOnly="true"
ElementStyle="{StaticResource RightAlignedColumn}"
c:DataGridFilterProperties.Filter="{Binding DataContext.sod_position_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.sod_position_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.sod_position_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.start_position_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.start_position_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.start_position_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
/>
<DataGridTextColumn x:Name="buy_qty" Header="Buy Qty" Binding="{Binding buy_quantity}" IsReadOnly="true"
<DataGridTextColumn x:Name="start_pnl" Header="Start PnL" IsReadOnly="true" SortMemberPath="start_pnl"
Binding="{Binding start_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.start_pnl_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.start_pnl_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.start_pnl_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextAlignment" Value="Right" />
<Setter Property="Background" Value="{Binding start_pnl, Converter={StaticResource PnLBackgroundColorConverter}}" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn x:Name="day_long" Header="Day Long" Binding="{Binding day_long_quantity}" IsReadOnly="true"
ElementStyle="{StaticResource RightAlignedColumn}"
c:DataGridFilterProperties.Filter="{Binding DataContext.buy_quantity_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.buy_quantity_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.buy_quantity_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.day_long_quantity_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.day_long_quantity_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.day_long_quantity_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
/>
<DataGridTextColumn x:Name="sell_qty" Header="Sell Qty" Binding="{Binding sell_quantity}" IsReadOnly="true"
<DataGridTextColumn x:Name="day_short" Header="Day Short" Binding="{Binding day_short_quantity}" IsReadOnly="true"
ElementStyle="{StaticResource RightAlignedColumn}"
c:DataGridFilterProperties.Filter="{Binding DataContext.sell_quantity_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.sell_quantity_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.sell_quantity_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.day_short_quantity_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.day_short_quantity_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.day_short_quantity_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
/>
<DataGridTextColumn x:Name="net_pos" Header="Net Pos" Binding="{Binding net_position}" IsReadOnly="true"
ElementStyle="{StaticResource RightAlignedColumn}"
c:DataGridFilterProperties.Filter="{Binding DataContext.net_position_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.net_position_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.net_position_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
/>
<DataGridTextColumn x:Name="pnl" Header="PnL" IsReadOnly="true" SortMemberPath="pnl"
ElementStyle="{StaticResource RightAlignedColumn}"
c:DataGridFilterProperties.Filter="{Binding DataContext.pnl_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.pnl_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.pnl_filter.open, Source={uc:DirectReference _grid}}"
<DataGridTextColumn x:Name="day_pnl" Header="Day PnL" IsReadOnly="true" SortMemberPath="day_pnl"
Binding="{Binding day_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.day_pnl_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.day_pnl_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.day_pnl_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="pnl"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextAlignment" Value="Right" />
<Setter Property="Background" Value="{Binding day_pnl, Converter={StaticResource PnLBackgroundColorConverter}}" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn x:Name="total_net_pos" Header="Total Net Pos" Binding="{Binding total_net_position}" IsReadOnly="true"
ElementStyle="{StaticResource RightAlignedColumn}"
c:DataGridFilterProperties.Filter="{Binding DataContext.total_net_position_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.total_net_position_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.total_net_position_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
/>
<DataGridTextColumn x:Name="total_pnl" Header="Total PnL" IsReadOnly="true" SortMemberPath="total_pnl"
Binding="{Binding total_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}"
c:DataGridFilterProperties.Filter="{Binding DataContext.total_pnl_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.total_pnl_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.total_pnl_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextAlignment" Value="Right" />
<Setter Property="Background" Value="{Binding total_pnl, Converter={StaticResource PnLBackgroundColorConverter}}" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn x:Name="pnl_price" Header="PnL Price" IsReadOnly="true" SortMemberPath="pnl_price"
ElementStyle="{StaticResource RightAlignedColumn}"
@@ -107,24 +146,17 @@
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="pnl_price"/>
<Binding Path="security.ContractSpec"/>
<Binding Path="security"/>
</MultiBinding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<!-- we are hiding these until we have toggle on/off via settings
<DataGridTextColumn x:Name="avg_buy" Header="AvgBuy" Binding="{Binding weighted_average_buy, Converter={StaticResource DoubleWithNaNHiddenConverter}}" IsReadOnly="true" SortMemberPath="weighted_average_buy"
ElementStyle="{StaticResource RightAlignedColumn}"
c:DataGridFilterProperties.Filter="{Binding DataContext.weighted_average_buy_filter, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterActive="{Binding DataContext.weighted_average_buy_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.weighted_average_buy_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<!--
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="weighted_average_buy"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
-->
</DataGridTextColumn>
<DataGridTextColumn x:Name="avg_sell" Header="AvgSell" Binding="{Binding weighted_average_sell, Converter={StaticResource DoubleWithNaNHiddenConverter}}" IsReadOnly="true" SortMemberPath="weighted_average_sell"
ElementStyle="{StaticResource RightAlignedColumn}"
@@ -132,26 +164,19 @@
c:DataGridFilterProperties.FilterActive="{Binding DataContext.weighted_average_sell_filter.active, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.weighted_average_sell_filter.open, Source={uc:DirectReference _grid}}"
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
<!--
<DataGridTextColumn.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="weighted_average_sell"/>
<Binding Path="security.ContractSpec"/>
</MultiBinding>
</DataGridTextColumn.Binding>
-->
</DataGridTextColumn>
-->
</DataGrid.Columns>
</c:DataGridEx>
<StatusBar Grid.Row="1" Grid.Column="0" DockPanel.Dock="Bottom">
<StatusBar Grid.Row="2" Grid.Column="0" DockPanel.Dock="Bottom">
<StatusBarItem DockPanel.Dock="Right">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Total PnL: "/>
<TextBlock Grid.Column="1" Text="{Binding total_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}" />
<TextBlock Grid.Column="0" Text="Aggregate PnL: "/>
<TextBlock Grid.Column="1" Text="{Binding total_aggregate_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}" />
</Grid>
</StatusBarItem>
<StatusBarItem DockPanel.Dock="Right">
@@ -163,8 +188,8 @@
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Total Net Pos: "/>
<TextBlock Grid.Column="1" Text="{Binding total_net_pos}"/>
<TextBlock Grid.Column="0" Text="Aggregate Net Pos: "/>
<TextBlock Grid.Column="1" Text="{Binding total_aggregate_net_pos}"/>
</Grid>
</StatusBarItem>
<StatusBarItem DockPanel.Dock="Right">
@@ -176,8 +201,8 @@
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Total Sell Qty: "/>
<TextBlock Grid.Column="1" Text="{Binding total_sell_qty}"/>
<TextBlock Grid.Column="0" Text="Aggregate Sell Qty: "/>
<TextBlock Grid.Column="1" Text="{Binding total_aggregate_sell_qty}"/>
</Grid>
</StatusBarItem>
<StatusBarItem DockPanel.Dock="Right">
@@ -189,8 +214,8 @@
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Total Buy Qty: "/>
<TextBlock Grid.Column="1" Text="{Binding total_buy_qty}"/>
<TextBlock Grid.Column="0" Text="Aggregate Buy Qty: "/>
<TextBlock Grid.Column="1" Text="{Binding total_aggregate_buy_qty}"/>
</Grid>
</StatusBarItem>
<StatusBarItem />
@@ -14,26 +14,31 @@ namespace mbts.spark {
internal class PAndLWindow : SparkWindow<PAndLWindowFactory.PAndLStartupData>, ISharedWindow {
const string _window_type = "PAndL", PNL_THREAD_ID = "PnL";
PAndLWindowFactory.PAndLStartupData _state;
PAndLWindowFactory.PAndLStartupData _startup_data;
PAndLModel _model;
PAndLView _view;
PAndLVM _vm;
PAndLWindowFactory _factory;
public PAndLWindow(PAndLModel model, PAndLWindowFactory factory, IBus bus, PAndLWindowFactory.PAndLStartupData state, ImageSource Icon ) : base("P&L", bus, factory, SparkCommands.ShowPAndL.icon) {
public PAndLWindow(PAndLModel model, PAndLWindowFactory factory, IBus bus, PAndLWindowFactory.PAndLStartupData startup_data, ImageSource Icon ) : base("P&L", bus, factory, SparkCommands.ShowPAndL.icon) {
_model = model;
_factory = factory;
_state = state;
_startup_data = startup_data;
}
public override string window_type => _window_type;
public override PAndLWindowFactory.PAndLStartupData SaveState => _state =
new PAndLWindowFactory.PAndLStartupData();
public override PAndLWindowFactory.PAndLStartupData SaveState => _startup_data =
new PAndLWindowFactory.PAndLStartupData(
_vm.get_sorting_states(),
_vm.get_filters_states(),
_vm.selected_pnl_calc_type,
_vm.start_of_day
);
public override UIElement Content => _view;
public override double MinWidth => 894;
public string thread_id => PNL_THREAD_ID;
public void thread_init() {
_vm = new PAndLVM(_bus, _model, _state);
_vm = new PAndLVM(_bus, _model, _startup_data);
_view = new PAndLView(_vm);
_view.DataContext = _vm;
}
@@ -77,20 +77,31 @@ namespace mbts.spark {
class PnLCalculator {
readonly IEnumerable<PnLData> _data;
readonly pnl_calc_type _calc_type;
readonly DateTime _start_of_trading_day_utc;
public PnLCalculator(IEnumerable<PnLData> data) {
public PnLCalculator(IEnumerable<PnLData> data, pnl_calc_type calc_type, TimeSpan start_of_day_ts) {
_data = data;
_calc_type = calc_type;
// Note that this isn't the acutal start of the trading day
// but it's close enough for pnl purposes.
// In reality, instruments have different schedules
var _start_of_trading_day_local = DateTime.Now.Date.Add(start_of_day_ts);
if (_start_of_trading_day_local >= DateTime.Now) {
_start_of_trading_day_local = _start_of_trading_day_local.AddDays(-1);
}
_start_of_trading_day_utc = _start_of_trading_day_local.ToUniversalTime();
}
public IEnumerable<PnLSummaryRow> get_summary_position() {
return (from pnl_item in _data
group pnl_item by (pnl_item.account, pnl_item.exchange, pnl_item.symbol, pnl_item.security, pnl_item.sec_md) into grouping
let start_of_day = _get_start_of_day(grouping.Key.symbol)
select new PnLSummaryRow(grouping.Key,
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= start_of_day).Sum(y => y.buy_quantity),
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= start_of_day).Sum(y => y.sell_quantity),
grouping.Sum(y => y.buy_quantity - y.sell_quantity),
grouping.Where(y => y.fill.Timestamp.ToDateTime() < start_of_day).Sum(y => y.buy_quantity - y.sell_quantity),
group pnl_item by (pnl_item.account, pnl_item.exchange, pnl_item.symbol, pnl_item.security, pnl_item.sec_md, pnl_item.security_type) into grouping
/* let start_of_day = _get_start_of_day(grouping.Key.symbol) */
select new PnLSummaryRow(_calc_type, _start_of_trading_day_utc, grouping.Key,
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= _start_of_trading_day_utc).Sum(y => (uint)y.buy_quantity),
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= _start_of_trading_day_utc).Sum(y => (uint)y.sell_quantity),
grouping.Sum(y => (int)(y.buy_quantity - y.sell_quantity)),
grouping.Where(y => y.fill.Timestamp.ToDateTime() < _start_of_trading_day_utc).Sum(y => (int)(y.buy_quantity - y.sell_quantity)),
grouping.weighted_average(y => y.fill.LastPrice, y => y.buy_quantity),
grouping.weighted_average(y => y.fill.LastPrice, y => y.sell_quantity),
grouping.Select(i => i.fill)
@@ -98,21 +109,26 @@ namespace mbts.spark {
);
}
public static double get_driver(SecurityMD sec_md, pnl_calc_type pnl_request) {
ulong unused = 0;
sec_md.md.get_bbo(ref unused, out NativePriceSize bid, out NativePriceSize ask);
sec_md.md.get_last_trade(ref unused, out NativePriceSize last, out DateTime unused1, out ulong unused2);
public static bool get_driver(SecurityMD sec_md, pnl_calc_type pnl_request, ref double driver_price) {
// we always want to pass in 0 to the md query so that we don't get back NaN if the market
// data hasn't changed between query periods
ulong bbo_time = 0;
ulong last_trade_time = 0;
if (!sec_md.md.get_bbo(ref bbo_time, out NativePriceSize bid, out NativePriceSize ask)) return false;
if (!sec_md.md.get_last_trade(ref last_trade_time, out NativePriceSize last, out DateTime unused1, out ulong unused2)) return false;
if (pnl_request.HasFlag(pnl_calc_type.waterfall)) {
// last price is available and withing range of available bid and ask
if (!Double.IsNaN(last.price) && last.price >= bid.price && last.price <= ask.price) {
return last.price;
driver_price = last.price;
return true;
}
double mid = TickUtils.mid_tick(sec_md.security.ContractSpec, bid.price, ask.price);
// last price is not valid, so try the mid
if (!Double.IsNaN(mid)) {
return mid;
driver_price = mid;
return true;
}
// last bid
@@ -120,9 +136,11 @@ namespace mbts.spark {
if (!Double.IsNaN(last.price) && !Double.IsNaN(bid.price) && Double.IsNaN(ask.price)) {
// if last >= bid then last else bid
if (last.price >= bid.price) {
return last.price;
driver_price = last.price;
return true;
} else {
return bid.price;
driver_price = bid.price;
return true;
}
}
@@ -131,15 +149,18 @@ namespace mbts.spark {
if (!Double.IsNaN(last.price) && !Double.IsNaN(ask.price) && Double.IsNaN(bid.price)) {
// if last <= ask then last else ask
if (last.price <= ask.price) {
return last.price;
driver_price = last.price;
return true;
} else {
return ask.price;
driver_price = ask.price;
return true;
}
}
// last only
if (!Double.IsNaN(last.price)) {
return last.price;
driver_price = last.price;
return true;
}
// indicative, no support yet
@@ -148,36 +169,43 @@ namespace mbts.spark {
if ((!Double.IsNaN(bid.price) || !Double.IsNaN(ask.price)) && !Double.IsNaN(sec_md.md.settlement_price)) {
// if bid and no ask and settle and bid > Settle
if (!Double.IsNaN(bid.price) && Double.IsNaN(ask.price) && !Double.IsNaN(sec_md.md.settlement_price) && bid.price > sec_md.md.settlement_price) {
return bid.price;
driver_price = bid.price;
return true;
}
// if ask and no bid and settle and ask < Settle
if (!Double.IsNaN(ask.price) && Double.IsNaN(bid.price) && !Double.IsNaN(sec_md.md.settlement_price) && ask.price < sec_md.md.settlement_price) {
return ask.price;
driver_price = ask.price;
return true;
}
}
// Settlement
// neither bid or ask are available
if (!Double.IsNaN(sec_md.md.settlement_price)) {
return sec_md.md.settlement_price;
driver_price = sec_md.md.settlement_price;
return true;
}
// Close
return sec_md.md.close_price;
driver_price = sec_md.md.close_price;
return true;
} // end waterfall
if (pnl_request.HasFlag(pnl_calc_type.last)) return last.price;
if (pnl_request.HasFlag(pnl_calc_type.mid)) return TickUtils.mid_tick(sec_md.security.ContractSpec, bid.price, ask.price);
if (pnl_request.HasFlag(pnl_calc_type.open)) return sec_md.md.open_price;
if (pnl_request.HasFlag(pnl_calc_type.close)) return sec_md.md.close_price;
if (pnl_request.HasFlag(pnl_calc_type.settle)) return sec_md.md.settlement_price;
return Double.NaN;
if (pnl_request.HasFlag(pnl_calc_type.last)) { driver_price = last.price; return true; }
// note that bid_or_ask is not considered here since this method only
// supports returning a single driver price; see update_pnl
if (pnl_request.HasFlag(pnl_calc_type.midpoint)) { driver_price = (bid.price + ask.price) / 2.0; return true; }
if (pnl_request.HasFlag(pnl_calc_type.open)) { driver_price = sec_md.md.open_price; return true; }
if (pnl_request.HasFlag(pnl_calc_type.close)) { driver_price = sec_md.md.close_price; return true; }
if (pnl_request.HasFlag(pnl_calc_type.settle)) { driver_price = sec_md.md.settlement_price; return true; }
driver_price = Double.NaN;
return false;
}
public static double get_pnl(double bid_for_long, double ask_for_short, FillImmutable fill, double cpp) {
if (fill.Side == Side.Buy) { return _get_long_pnl(bid_for_long, fill.LastPrice, fill.LastShares, cpp); }
if (fill.Side == Side.Sell) { return _get_short_pnl(ask_for_short, fill.LastPrice, fill.LastShares, cpp); }
if (fill.Side == Side.Buy && !Double.IsNaN(bid_for_long)) { return _get_long_pnl(bid_for_long, fill.LastPrice, fill.LastShares, cpp); }
if (fill.Side == Side.Sell && !Double.IsNaN(ask_for_short)) { return _get_short_pnl(ask_for_short, fill.LastPrice, fill.LastShares, cpp); }
return Double.NaN;
}
@@ -197,10 +225,6 @@ namespace mbts.spark {
return (price - driver) * quantity * cpp;
}
private static DateTime _get_start_of_day(string symbool) {
// I rekcon this will change according to the symbol's session
return DateTime.Today;
}
}
}
+11 -2
View File
@@ -1,18 +1,26 @@
using bts.api;
using bts.api.orderman;
using System;
using System.ComponentModel;
namespace mbts.spark {
[Flags]
public enum pnl_calc_type {
none = 0,
[Description("Last Tradeable Price Avalanche")]
waterfall = 1,
[Description("Last")]
last = 2,
bid_and_ask = 4,
mid = 8,
[Description("Bid/Ask")]
bid_or_ask = 4,
[Description("Midpoint")]
midpoint = 8,
[Description("Open")]
open = 16,
[Description("Close")]
close = 32,
[Description("Settle")]
settle = 64
}
@@ -34,6 +42,7 @@ namespace mbts.spark {
public SecurityImmutable? security => sec_md?.security;
public double buy_quantity => (fill.Side == Side.Buy) ? fill.LastShares : 0;
public double sell_quantity => (fill.Side == Side.Sell) ? fill.LastShares : 0;
public SecurityType? security_type => security?.security_type();
public override string ToString() {
return $"Fill: ( {account} {exchange} {security.ToString()} buy: {buy_quantity} sell: {sell_quantity} ) Security: {security.ToString()}";
@@ -12,70 +12,106 @@ namespace mbts.spark {
internal class PnLSummaryRow : ViewModelBase, IEquatable<PnLSummaryRow> {
(string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md) key;
double _buy_quantity, _sell_quantity, _net_position, _sod_position, _pnl, _pnl_price, _weighted_average_buy, _weighted_average_sell;
DateTime _start_of_day;
(string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md, SecurityType? sec_type) key;
int _start_net_position, _total_net_position;
uint _day_long_quantity, _day_short_quantity;
double _total_pnl, _sod_pnl, _start_pnl, _pnl_price, _weighted_average_buy, _weighted_average_sell;
IEnumerable<FillImmutable> _fills;
//List<MarketSchedule> _schedules = new List<MarketSchedule>() { MarketSchedule.es_schedule };
double _driver;
static readonly Dictionary<string, double> cpp_dict = config.Configuration.get<bts.config.CppConfig>().cpp_entry;
public PnLSummaryRow((string, Venue?, string, SecurityImmutable?, SecurityMD?) key,
double buy_quant, double sell_quant, double net_pos, double sod_pos, double wavg_buy, double wavg_sell, IEnumerable<FillImmutable> fills) {
public PnLSummaryRow(pnl_calc_type calc_type,
DateTime start_of_day,
(string, Venue?, string, SecurityImmutable?, SecurityMD?, SecurityType?) key,
uint day_buy_quant, uint day_sell_quant, int net_pos, int sod_pos, double wavg_buy, double wavg_sell, IEnumerable<FillImmutable> fills) {
this._start_of_day = start_of_day;
this.key = key;
buy_quantity = buy_quant;
sell_quantity = sell_quant;
net_position = net_pos;
sod_position = sod_pos;
day_long_quantity = day_buy_quant;
day_short_quantity = day_sell_quant;
total_net_position = net_pos;
start_net_position = sod_pos;
weighted_average_buy = wavg_buy;
weighted_average_sell = wavg_sell;
_fills = fills;
// set the initial pnl and pnl_price when there has been a
// position update i.e. this constructor
update_pnl(pnl_calc_type.waterfall);
update_pnl(calc_type);
}
/// <summary>
/// Looks up the cash per point (cpp) for the given SecurityImmutable.
/// If the SecurityImmutable is a cross-product exchange-traded spread,
/// such as a crack spread, where the legs have differing cpps, then
/// we'll return Double.Nan;
/// </summary>
/// <param name="s">The SecurityImmutable for which we will look up a cpp</param>
/// <returns>The found cpp, else Double.NaN</returns>
private double _get_cpp_for_security(SecurityImmutable s) {
if (s.security_type() == SecurityType.Spread) {
var distinct_cpps = s.Spread.Legs
.Select(l => l.Security.ContractSpec.CashPerPoint)
.Select(l => get_cpp_for_symbol(l.Security))
.Distinct();
bool all_legs_have_same_cpp = distinct_cpps.Count() < 2;
if (all_legs_have_same_cpp) {
return s.Spread.Legs.First().Security.ContractSpec.CashPerPoint;
} else {
Logger.error(bts.log.Subsystem.Ui,
$"Security {s} contains legs with the following cash multipliers: { string.Join(",", distinct_cpps.Select(d => d.ToString())) } ");
return Double.NaN;
return all_legs_have_same_cpp ? distinct_cpps.First() : Double.NaN;
} else {
return get_cpp_for_symbol(s);
}
// local function for looking up cpp for a single SecurityImmutable
double get_cpp_for_symbol(SecurityImmutable si) {
if (cpp_dict.TryGetValue(si.Symbol, out double cpp_out)) {
return cpp_out;
}
return double.NaN;
}
return s.ContractSpec.CashPerPoint;
}
public void update_pnl(pnl_calc_type pnl_request) {
if (key.sec_md.HasValue) {
double cpp = _get_cpp_for_security(key.security.Value);
if (pnl_request.HasFlag(pnl_calc_type.bid_and_ask)) {
ulong unused = 0;
key.sec_md.Value.md.get_bbo(ref unused, out NativePriceSize bid, out NativePriceSize ask);
if (pnl_request.HasFlag(pnl_calc_type.bid_or_ask)) {
ulong bbo_ref = 0;
key.sec_md.Value.md.get_bbo(ref bbo_ref, out NativePriceSize bid, out NativePriceSize ask);
double summary_pnl = 0.0;
double start_pnl_total = 0.0;
double day_pnl_total = 0.0;
foreach (var fill in _fills) {
double _tick_pnl = PnLCalculator.get_pnl(bid.price, ask.price, fill, cpp);
summary_pnl += _tick_pnl;
if (fill.Timestamp.ToDateTime() >= _start_of_day) {
day_pnl_total += _tick_pnl;
} else {
start_pnl_total += _tick_pnl;
}
}
pnl = summary_pnl;
total_pnl = summary_pnl;
day_pnl = day_pnl_total;
start_pnl = start_pnl_total;
pnl_price = Double.NaN;
} else {
double driver = PnLCalculator.get_driver(key.sec_md.Value, pnl_request);
double summary_pnl = 0.0;
foreach (var fill in _fills) {
double _tick_pnl = PnLCalculator.get_pnl(driver, fill, cpp);
summary_pnl += _tick_pnl;
if (PnLCalculator.get_driver(key.sec_md.Value, pnl_request, ref _driver)) {
double summary_pnl = 0.0;
double start_pnl_total = 0.0;
double day_pnl_total = 0.0;
foreach (var fill in _fills) {
double _tick_pnl = PnLCalculator.get_pnl(_driver, fill, cpp);
summary_pnl += _tick_pnl;
if (fill.Timestamp.ToDateTime() >= _start_of_day) {
day_pnl_total += _tick_pnl;
} else {
start_pnl_total += _tick_pnl;
}
}
total_pnl = summary_pnl;
day_pnl = day_pnl_total;
start_pnl = start_pnl_total;
pnl_price = _driver;
}
pnl = summary_pnl;
pnl_price = driver;
}
}
}
@@ -89,9 +125,9 @@ namespace mbts.spark {
key.exchange == other.exchange &&
key.security == other.security &&
key.symbol == other.symbol &&
buy_quantity == other.buy_quantity &&
sell_quantity == other.sell_quantity &&
pnl == other.pnl;
day_long_quantity == other.day_long_quantity &&
day_short_quantity == other.day_short_quantity &&
total_pnl == other.total_pnl;
}
public override int GetHashCode() {
@@ -104,7 +140,7 @@ namespace mbts.spark {
}
public override string ToString() {
return $"account: {account} exchange: {exchange_string} symbol: {symbol} security: {security_text} buy_quantity: {buy_quantity} sell_quantity: {sell_quantity} pnl: {pnl}";
return $"account: {account} exchange: {exchange_string} symbol: {symbol} security: {security_text} buy_quantity: {day_long_quantity} sell_quantity: {day_short_quantity} pnl: {total_pnl}";
}
public string account {
@@ -134,62 +170,83 @@ namespace mbts.spark {
}
public SecurityImmutable? security => key.security;
public SecurityType security_type => key.sec_type.GetValueOrDefault();
public string security_text {
get => security.HasValue ? security.Value.ToString() : "Unknown Security";
set => ignore();
}
public double buy_quantity {
get => _buy_quantity;
public int start_net_position {
get => _start_net_position;
set {
if (_buy_quantity != value) {
_buy_quantity = value;
RaisePropertyChanged(nameof(buy_quantity));
if (_start_net_position != value) {
_start_net_position = value;
RaisePropertyChanged(nameof(start_net_position));
}
}
}
public double start_pnl {
get => _start_pnl;
set {
if (_start_pnl != value) {
_start_pnl = value;
RaisePropertyChanged();
}
}
}
public uint day_long_quantity {
get => _day_long_quantity;
set {
if (_day_long_quantity != value) {
_day_long_quantity = value;
RaisePropertyChanged(nameof(day_long_quantity));
}
}
}
public uint day_short_quantity {
get => _day_short_quantity;
set {
if (_day_short_quantity != value) {
_day_short_quantity = value;
RaisePropertyChanged(nameof(day_short_quantity));
}
}
}
public double day_pnl {
get => _sod_pnl;
set {
if (_sod_pnl != value) {
_sod_pnl = value;
RaisePropertyChanged();
}
}
}
public double sell_quantity {
get => _sell_quantity;
public int total_net_position {
get => _total_net_position;
set {
if (_sell_quantity != value) {
_sell_quantity = value;
RaisePropertyChanged(nameof(sell_quantity));
if (_total_net_position != value) {
_total_net_position = value;
RaisePropertyChanged(nameof(total_net_position));
}
}
}
public double sod_position {
get => _sod_position;
public double total_pnl {
get => _total_pnl;
set {
if (_sod_position != value) {
_sod_position = value;
RaisePropertyChanged(nameof(sod_position));
if (_total_pnl != value) {
_total_pnl = value;
RaisePropertyChanged(nameof(total_pnl));
}
}
}
public double net_position {
get => _net_position;
set {
if (_net_position != value) {
_net_position = value;
RaisePropertyChanged(nameof(net_position));
}
}
}
public double pnl {
get => _pnl;
set {
if (_pnl != value) {
_pnl = value;
RaisePropertyChanged(nameof(pnl));
}
}
}
public double pnl_price {
get => _pnl_price;
@@ -221,6 +278,9 @@ namespace mbts.spark {
}
}
//public IEnumerable<FillImmutable> sod_fills => _fills.Where(f => f.Timestamp.ToDateTime() >= _start_of_day);
//public IEnumerable<FillImmutable> pos_fills => _fills.Where(f => f.Timestamp.ToDateTime() < _start_of_day);
}
@@ -21,7 +21,7 @@ namespace mbts.spark {
IOrderBookCell current { get; }
//
IOrderBookCellStore select_other(CVOrderBookRowVM row);
bool refresh();
bool refresh(bool force_refresh);
void regenerate_cell();
}
@@ -93,9 +93,9 @@ namespace mbts.spark {
public IOrderBookCellStore<T> select_other(CVOrderBookRowVM row) => _select_other(row);
IOrderBookCellStore IOrderBookCellStore.select_other(CVOrderBookRowVM row) => select_other(row);
public bool refresh() {
public bool refresh(bool force_refresh = false) {
T v = _get_value(_row);
if (!_eq_comparer.Equals(v, _current.value)) {
if (force_refresh || !_eq_comparer.Equals(v, _current.value)) {
_current = new OrderBookCell<T>(this, v);
_row.RaisePropertyChanged(_prop_name);
return true;
@@ -54,6 +54,19 @@ namespace mbts.spark {
EqualityComparer.Create<SecurityImmutable, uint>(s => s.Id),
SecurityHelper.Comparer.InstanceImmutable
)));
// symbol
Add(symbol_column = new SimpleTextColumn<string[]>(
Columns.Symbol,
DefaultColumnHeaders.Symbol,
nameof(CVOrderBookRowVM.symbol),
r => r.symbol.value,
DefaultColumnWidths.Symbol,
DiscreteValuesFilterVM.Create<CVOrderBookRowVM, string>(
mbts.co2.UserSymbol.get_all().Select(u => u.symbol).Distinct().ToList(),
r => r.symbol.value,
value_path(nameof(CVOrderBookRowVM.symbol))
),
DataGridFilterKnownTemplates.DiscreteValues));
// time_created
Add(time_created_column = new TimeCreatedColumn(
new TimeFilterVM<CVOrderBookRowVM>(r => r.time_created.value, value_path(nameof(OrderBookRowVM.time_created)))));
@@ -213,7 +226,7 @@ namespace mbts.spark {
filled_qty_column.filter.load_state(fs.qty_filled);
side_column.filter.load_state(fs.sides);
order_type_column.filter.load_state(fs.order_types);
// TODO: add "sub_type column.filter" data to FilterStates
sub_type_column.filter.load_state(fs.order_sub_types);
time_in_force_column.filter.load_state(fs.tifs);
exchange_column.filter.load_state(fs.exchanges);
@@ -223,6 +236,7 @@ namespace mbts.spark {
status_column.filter.load_state(fs.statuses);
last_message_column.filter.load_state(fs.exchange_message_regex);
last_change_column.filter.load_state(fs.last_change);
symbol_column.filter.load_state(fs.symbols);
}
if (startup_data?.sort_criteria is List<SortingCriteria> lsc) {
foreach (var sc in lsc)
@@ -238,6 +252,7 @@ namespace mbts.spark {
public event Action filter_changed;
public SecurityColumn security_column { get; }
public SimpleTextColumn<string[]> symbol_column { get; }
public TimeCreatedColumn time_created_column { get; }
public LastChangeTimeColumn last_change_column { get; }
public SimpleTextColumn<string[]> user_column { get; }
@@ -269,6 +284,7 @@ namespace mbts.spark {
exchanges = exchange_column.filter.get_state(),
tifs = time_in_force_column.filter.get_state(),
order_types = order_type_column.filter.get_state(),
order_sub_types = sub_type_column.filter.get_state(),
sides = side_column.filter.get_state(),
qty = qty_column.filter.get_state(),
price = price_column.filter.get_state(),
@@ -278,7 +294,8 @@ namespace mbts.spark {
statuses = status_column.filter.get_state(),
exchange_message_regex = last_message_column.filter.get_state(),
last_change = last_change_column.filter.get_state(),
security_criteria = security_column.filter.get_state()
security_criteria = security_column.filter.get_state(),
symbols = symbol_column.filter.get_state()
};
public void Dispose() {
@@ -12,6 +12,8 @@ using OrderStatus = bts.api.orderman.OrderStatus;
namespace mbts.spark {
partial class CVOrderBookRowVM : DisposableViewModelBase, IEquatable<CVOrderBookRowVM>, ui.controls.DIYGrid.IRowVM, ui.controls.DIYGrid.IActionableRowVM {
const string _unknown_security_price_format_string = "0.#########";
const int _unknown_security_price_num_fractional_digits = 9;
readonly CVOrderBookVM _book_vm;
readonly OrdersModel _model;
readonly Property<bool> _visible, _expanded, _passes_filter;
@@ -29,6 +31,7 @@ namespace mbts.spark {
IOrderBookCellStore<uint> _security_key_store;
IOrderBookCellStore<SecurityImmutable?> _security_store;
IOrderBookCellStore<Venue> _exchange_store;
IOrderBookCellStore<string> _symbol_store;
// Def
IOrderBookCellStore<CustomOrderDefinition.CustomOneofCase> _order_type_store;
IOrderBookCellStore<Side> _side_store;
@@ -57,6 +60,7 @@ namespace mbts.spark {
_security_key_store = CellFactory.create(this, _book_vm.columns.security_key_column, nameof(security_key), r => r._order.security_key(), r => r._security_key_store);
_parent_order_key_store = CellFactory.create(this, _book_vm.columns.parent_key_column, nameof(parent_order_key), r => r._order.ParentKey, r => r._parent_order_key_store);
_order_key_store = CellFactory.create(this, _book_vm.columns.order_key_column, nameof(order_key), r => r._order.Key, r => r._order_key_store);
_symbol_store = CellFactory.create(this, _book_vm.columns.symbol_column, nameof(symbol), r => r._security.HasValue ? _security.Value.Symbol : "unknown symbol", r => r._symbol_store);
_time_created_store = CellFactory.create(this, _book_vm.columns.time_created_column, nameof(time_created), r => r._order.created(), r => r._time_created_store, format_date);
_user_store = CellFactory.create(this, _book_vm.columns.user_column, nameof(user), r => _order.user(), r => r._user_store);
_account_store = CellFactory.create(this, _book_vm.columns.account_column, nameof(account), r => r._order.account(), r => r._account_store);
@@ -74,7 +78,8 @@ namespace mbts.spark {
_status_store = CellFactory.create(this, _book_vm.columns.status_column, nameof(status), r => _order.status(), r => r._status_store);
_last_msg_store = CellFactory.create(this, _book_vm.columns.last_message_column, nameof(last_msg), r => _order.last_msg(), r => r._last_msg_store);
//
string format_price(double v) => _security.HasValue ? _security.Value.ContractSpec.format(v) : v.ToString();
string format_price(double v) => _security.HasValue ? _security.Value.ContractSpec.format(v) :
Math.Round(v, _unknown_security_price_num_fractional_digits, MidpointRounding.AwayFromZero).ToString(_unknown_security_price_format_string);
string format_date(DateTime v) => $"{v:M/d} {v:h:mm:ss.fff tt}";
string format_sub_type(OrderSubType v) {
switch (v) {
@@ -85,7 +90,7 @@ namespace mbts.spark {
}
//
_visible = backing(nameof(is_visible), true);
_expanded = backing(nameof(is_expanded), true);
_expanded = backing(nameof(is_expanded), false);
_depth_level = backing(nameof(depth_level), () => {
int i = 0;
var curr = this;
@@ -117,6 +122,7 @@ namespace mbts.spark {
public IOrderBookCell<uint> security_key => _security_key_store.current;
public IOrderBookCell<SecurityImmutable?> security => _security_store.current;
public IOrderBookCell<Venue> exchange => _exchange_store.current;
public IOrderBookCell<string> symbol => _symbol_store.current;
// Def
public IOrderBookCell<CustomOrderDefinition.CustomOneofCase> order_type => _order_type_store.current;
public IOrderBookCell<Side> side => _side_store.current;
@@ -187,6 +193,7 @@ namespace mbts.spark {
yield return _price_store;
yield return _avg_price_store;
yield return _last_price_store;
yield return _symbol_store;
}
}
@@ -279,6 +286,7 @@ namespace mbts.spark {
case Columns.ParentKey: return row._parent_order_key_store;
case Columns.SecurityKey: return row._security_key_store;
case Columns.Security: return row._security_store;
case Columns.Symbol: return row._symbol_store;
case Columns.Exchange: return row._exchange_store;
case Columns.OrderType: return row._order_type_store;
case Columns.Side: return row._side_store;
@@ -319,7 +327,11 @@ namespace mbts.spark {
void _update_partial(IEnumerable<IOrderBookCellStore> target_cell_stores) {
bool invalidate = false;
foreach (var cell_store in target_cell_stores) {
if (cell_store.refresh()) {
// although the price-related stores will be inlcuded in the IEnumerable,
// we need to force .refresh b/c the price* backing store's value does not
// change when the security becomes known
bool force_refresh = cell_store.prop_name.Equals(nameof(price)) || cell_store.prop_name.Equals(nameof(avg_price)) || cell_store.prop_name.Equals(nameof(last_price));
if (cell_store.refresh(force_refresh)) {
invalidate = true;
if (_book_vm.live_sorting_columns.Contains(cell_store.column.key)) {
// refresh the sort by calling regenerate_cell() for each descendent-row
@@ -38,6 +38,8 @@ namespace mbts.spark {
SetMap<uint, uint> _parent_to_children = new SetMap<uint, uint>();
SetMap<uint, uint> _security_orders = new SetMap<uint, uint>();
bool _expand_collapse_all_status = false;
public CVOrderBookVM(IBus bus, OrdersModel model, OrderBookStartupData state) {
columns = new CVOrderBookColumnCollection(bus, model, state, _order_securities_subject);
foreach (ColumnBase col in columns)
@@ -69,6 +71,15 @@ namespace mbts.spark {
SetMap<uint, uint> missing_security_orders = new SetMap<uint, uint>();
IReadOnlyDictionary<uint, SecurityMD> security_dict = new Dictionary<uint, SecurityMD>();
expand_collapse_all_rows_command = new DelegateCommand(
() => {
_order_rows.Where(r => r.is_visible && r.passes_filter).ForEach(r =>
r.is_expanded = !_expand_collapse_all_status
);
_expand_collapse_all_status = !_expand_collapse_all_status;
}
);
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(on_securities),
model.on_orders.OnDispatcher().Subscribe(on_orders),
@@ -232,6 +243,8 @@ namespace mbts.spark {
public CVOrderBookRowVM current_row { get => _current_row.Value; set => _current_row.Value = value ?? _selected_rows.FirstOrDefault(); }
public OrderRecordImmutable? current_order => current_row?.order;
public IEnumerable<OrderRecordImmutable> selected_orders => _selected_rows.Select(r => r.order);
public System.Windows.Input.ICommand expand_collapse_all_rows_command { get; }
void IOrderBookVM.on_sorting(DIYGridSortingEventArgs e) { }
public void row_selection_changed(SelectionChangedEventArgs e) {
@@ -18,6 +18,7 @@ using System.Linq;
using System.Reactive.Linq;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using static bts.shell.OrderBookWindowFactory;
using static mbts.spark.OrderBookView;
using static mbts.ui.controls.DIYGrid;
@@ -427,6 +428,9 @@ namespace mbts.spark {
public ObservableCollection<OrderBookRowVM> selected_rows => _selected_rows;
public IEnumerable<OrderRecordImmutable> selected_orders => _selected_rows.Select(r => r.order);
public ICommand expand_collapse_all_rows_command => throw new NotImplementedException();
#region Filters
public IDataGridColumnPersistentFilter<OrderBookRowVM, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<OrderBookRowVM>(r => r.price, nameof(OrderBookRowVM.price));
public IDataGridColumnPersistentFilter<OrderBookRowVM, UIntMinMaxState> qty_filter { get; } = new UIntMinMax<OrderBookRowVM>(r => r.quantity, nameof(OrderBookRowVM.quantity));
@@ -448,7 +452,6 @@ namespace mbts.spark {
public IDataGridColumnPersistentFilter<OrderBookRowVM, string> exchange_message_filter { get; }
public IDataGridColumnPersistentFilter<OrderBookRowVM, string> security_filter { get; } = new RegexFilterVM<OrderBookRowVM>(r => r.security_text, nameof(OrderBookRowVM.security_text));
public FilterStates get_filter_states() =>
new FilterStates {
order_key_regex = order_key_filter.get_state(),
@@ -17,10 +17,11 @@ namespace mbts.spark {
void on_sorting(DIYGridSortingEventArgs e);
List<SortingCriteria> get_sorting_states();
OrderBookWindowFactory.FilterStates get_filter_states();
System.Windows.Input.ICommand expand_collapse_all_rows_command { get; }
}
internal partial class OrderBookView : UserControl {
public enum Columns { Security, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey }
public enum Columns { Security, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Symbol }
public const string
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
@@ -27,7 +27,9 @@ namespace mbts.spark {
, _cancel_buy_orders_command
, _cancel_sell_orders_command
, _cancel_all_orders_command
, _cancel_selected_orders_command;
, _cancel_selected_orders_command
, _recreate_selected_orders_command
, _toggle_collapse_expand_all_command;
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon) : base("Order Book", bus, factory, SparkCommands.ShowOrders.icon) {
@@ -107,21 +109,41 @@ namespace mbts.spark {
can_execute: () => _vm.selected_orders.Any(x => x.status() == OrderStatus.Working)
);
_recreate_selected_orders_command = SparkCommands.RecreateSelectedOrders.complete(
() =>
_vm.selected_orders.Where(x => _passes_recreate_search_criteria(x))
.Select(x => (x.Def, x.State.Price, x.State.Quantity - x.State.FilledQuantity)),
can_execute: () => _vm.selected_orders.Any(x => _passes_recreate_search_criteria(x))
);
_toggle_collapse_expand_all_command = new SparkCommandCommand("Expand / Collapse All",
Icons.UpArrow,
_vm.expand_collapse_all_rows_command);
_commands = new SparkCommand[] {
_show_fills_command,
_modify_order_command,
_cancel_selected_orders_command,
_cancel_buy_orders_command,
_cancel_sell_orders_command,
_cancel_all_orders_command
_cancel_all_orders_command,
_recreate_selected_orders_command,
_toggle_collapse_expand_all_command
};
_vm.selected_order_changed += _vm_selected_order_changed;
}
private bool _passes_recreate_search_criteria(OrderRecordImmutable record) {
return (record.status() == OrderStatus.Canceled || record.status() == OrderStatus.Error)
&& !record.has_parent()
&& record.State.Quantity > record.State.FilledQuantity;
}
private void _vm_selected_order_changed(OrderRecordImmutable? e) {
if (e.HasValue) {
_modify_order_command.raiseCanExecuteChanged();
_cancel_selected_orders_command.raiseCanExecuteChanged();
_recreate_selected_orders_command.raiseCanExecuteChanged();
}
}
}
@@ -91,4 +91,4 @@ namespace mbts.spark.utilities {
return value;
}
}
}
}
@@ -32,10 +32,12 @@ namespace mbts.spark.utilities {
CustomOrderDefinition.CustomOneofCase.Lean,
CustomOrderDefinition.CustomOneofCase.Join,
CustomOrderDefinition.CustomOneofCase.Top,
CustomOrderDefinition.CustomOneofCase.Trigger,
}, _spreader_types = new[] {
CustomOrderDefinition.CustomOneofCase.Spreader,
CustomOrderDefinition.CustomOneofCase.Iceberg,
CustomOrderDefinition.CustomOneofCase.TimeBox
CustomOrderDefinition.CustomOneofCase.TimeBox,
CustomOrderDefinition.CustomOneofCase.Trigger,
};
readonly string _user;
readonly AdminModel _admin_model;
@@ -213,6 +215,12 @@ namespace mbts.spark.utilities {
Child = custom_order(),
TopThreshold = new QuantityMeasure(),
Hedge = custom_order(),
};
public TriggerOrderDefinition trigger_order() => new TriggerOrderDefinition
{
Child = custom_order(),
TriggerCondition = new TriggerCondition(),
TriggerAction = new TriggerAction(),
};
@@ -231,6 +239,7 @@ namespace mbts.spark.utilities {
case CustomOrderDefinition.CustomOneofCase.Lean: rv.Lean = lean_order(); break;
case CustomOrderDefinition.CustomOneofCase.Join: rv.Join = join_order(); break;
case CustomOrderDefinition.CustomOneofCase.Top: rv.Top = top_order(); break;
case CustomOrderDefinition.CustomOneofCase.Trigger: rv.Trigger = trigger_order(); break;
case CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = spreader_order(); break;
}
return rv;
@@ -250,6 +259,7 @@ namespace mbts.spark.utilities {
case CustomOrderDefinition.CustomOneofCase.Lean: return new OrderMetadata { KeyBinding = "L", Color = BTSColors.Orders.lean_order };
case CustomOrderDefinition.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
case CustomOrderDefinition.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
case CustomOrderDefinition.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.top_order };
case CustomOrderDefinition.CustomOneofCase.Spreader: goto default;
case CustomOrderDefinition.CustomOneofCase.None: goto default;
default: return _default_order_metadata;
@@ -267,6 +277,7 @@ namespace mbts.spark.utilities {
case OrderState.CustomOneofCase.Lean: return new OrderMetadata { KeyBinding = "L", Color = BTSColors.Orders.lean_order };
case OrderState.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
case OrderState.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
case OrderState.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.top_order };
case OrderState.CustomOneofCase.Spreader: goto default;
case OrderState.CustomOneofCase.None: goto default;
default: return _default_order_metadata;
@@ -302,6 +313,7 @@ namespace mbts.spark.utilities {
case nameof(LeanOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Lean;
case nameof(JoinOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Join;
case nameof(TopOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Top;
case nameof(TriggerOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Trigger;
default: throw new ArgumentException($"Unable to match '{order_type_def?.Descriptor.Name}' to an order definition type");
}
}
@@ -311,8 +323,8 @@ namespace mbts.spark.utilities {
public static PriceType PriceType => new PriceType { Status = PriceType.Types.Status.Bid };
public static PriceOffset PriceOffset => new PriceOffset { PriceType = PriceType };
public static QuantityTrigger QuantityTrigger => new QuantityTrigger { QuantityMeasure = QuantityMeasure, LessThanEqualto = true };
public static AbsolutePriceTrigger AbsolutePriceTrigger => new AbsolutePriceTrigger { PriceType = PriceType, QuantityTrigger = QuantityTrigger };
public static TrailingTrigger TrailingTrigger => new TrailingTrigger { PriceType = PriceType, Stop = true };
public static bts.spark.AbsolutePriceTrigger AbsolutePriceTrigger => new bts.spark.AbsolutePriceTrigger { PriceType = PriceType, QuantityTrigger = QuantityTrigger };
public static bts.spark.TrailingTrigger TrailingTrigger => new bts.spark.TrailingTrigger { PriceType = PriceType, Stop = true };
public static PriceTrigger PriceTrigger => new PriceTrigger { AbsolutePriceTrigger = AbsolutePriceTrigger, TrailingTrigger = TrailingTrigger };
public static OrderTime OrderTime => new OrderTime { Now = true };
public static BTSTimedWindowParameters TimeWindow => new BTSTimedWindowParameters { Cancel = true, StartTime = OrderTime, StopTime = new OrderTime() };
@@ -43,4 +43,4 @@ namespace mbts.spark {
public static readonly SecurityImmutable SecurityImmutable = new SecurityImmutable(Security);
}
}
}
}
+14 -14
View File
@@ -10,10 +10,10 @@ using System.Threading.Tasks;
namespace bts.api.core {
static class ApiNativeMethods {
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern IntPtr api_create_quickbus(byte[] qbconfig, long length);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void api_destroy_quickbus(IntPtr qbus);
@@ -51,24 +51,24 @@ namespace bts.api.core {
int change_count,
IntPtr extra);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern IntPtr api_client_create(IntPtr qbus, string type_name, string service_qualifier);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void api_client_destroy(IntPtr client);
[DllImport("spark_lib")]
[DllImport("libspark")]
[return:MarshalAs(UnmanagedType.I1)]
internal static extern bool api_client_is_on_qb_thread(IntPtr client);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void api_client_send(IntPtr nchannel, byte[] buffer, int length, raw_pb_callback p, IntPtr extra);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern IntPtr api_client_subscribe(IntPtr client, string method_and_qualifier, raw_subscription_callback cb, IntPtr extra);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void api_client_unsubscribe(IntPtr client, IntPtr subscription);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void api_client_log_error(string msg);
#endregion
@@ -81,10 +81,10 @@ namespace bts.api.core {
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate void raw_rpc_request_callback(byte* buffer, long buffer_size, IntPtr cb, IntPtr extra);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern IntPtr api_server_create(IntPtr qbus, string type_name, string service_qualifier, raw_rpc_request_callback callback, IntPtr extra);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void api_server_call_callback(ref IntPtr callback, byte[] buffer, int length);
[StructLayout(LayoutKind.Sequential)]
@@ -95,11 +95,11 @@ namespace bts.api.core {
public IntPtr buffer;
}
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern IntPtr api_server_create_table(IntPtr server, string method_and_qualifier);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void api_server_update_table(IntPtr table, [In] StreamUpdate[] changes, long change_count);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void api_server_destroy_table(IntPtr table);
#endregion
@@ -60,28 +60,36 @@ namespace bts.api.core {
bool first_update = true;
var result = Observable.Create<RawUpdate<TOut>>(observer => {
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
var added_items = new List<TOut>(change_count);
var added_keys = new List<int>(change_count);
var removed_keys = new List<int>(change_count);
foreach (var change in changes) {
if (change.type != ApiNativeMethods.ChangeType.REMOVE) {
// create or change
added_items.Add( converter( parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
added_keys.Add(change.key);
}
if (change.type != ApiNativeMethods.ChangeType.CREATE) {
// removed or changed
removed_keys.Add(change.key);
try {
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
var added_items = new List<TOut>(change_count);
var added_keys = new List<int>(change_count);
var removed_keys = new List<int>(change_count);
foreach (var change in changes) {
if (change.type != ApiNativeMethods.ChangeType.REMOVE) {
// create or change
added_items.Add( converter( parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
added_keys.Add(change.key);
}
if (change.type != ApiNativeMethods.ChangeType.CREATE) {
// removed or changed
removed_keys.Add(change.key);
}
}
observer.OnNext(new RawUpdate<TOut>(added_items, added_keys, removed_keys));
}
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
if (!first_update) {
observer.OnNext(RawUpdate<TOut>.Reset());
}
first_update = false;
}
observer.OnNext(new RawUpdate<TOut>(added_items, added_keys, removed_keys));
}
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
if (!first_update) {
observer.OnNext(RawUpdate<TOut>.Reset());
}
first_update = false;
catch (Exception e) {
ApiNativeMethods.api_client_log_error($"Exception in subscription callback for sub: '{method_and_qualifier}' \n--\n '{e}'");
// Sleep to let the log message get asyncronously written to disk
System.Threading.Thread.Sleep(100);
Environment.Exit(-1);
}
};
var subscription = ApiNativeMethods.api_client_subscribe(_client, method_and_qualifier, cb, IntPtr.Zero);
@@ -8,19 +8,19 @@ namespace mbts.utils
{
internal static class BTSNativeMethods
{
[DllImport("spark_lib")]
[DllImport("libspark")]
public static extern void provide_command_line(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] argv, int argc);
[DllImport("spark_lib")]
[DllImport("libspark")]
public static extern int get_random_port();
[DllImport("spark_lib")]
[DllImport("libspark")]
public static extern void init_spark_lib();
[DllImport("spark_lib")]
[DllImport("libspark")]
public static extern void finit_spark_lib();
[DllImport("spark_lib")]
[DllImport("libspark")]
public static extern UInt64 ll_now();
[DllImport("kernel32.dll")]
+23 -23
View File
@@ -23,14 +23,14 @@ namespace mbts.co2
}
}
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern IntPtr create_md_consumer(
[MarshalAs(UnmanagedType.LPArray)] UserSymbol[] symbols,
UInt32 count, bool shared);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void destroy_md_consumer(IntPtr consumer);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern bool md_consumer_all_heartbeats_ok(IntPtr consumer);
[StructLayout(LayoutKind.Sequential)]
@@ -42,72 +42,72 @@ namespace mbts.co2
public readonly IntPtr cold_header;
}
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern bool md_consumer_get_handle(IntPtr consumer, security_key key, out BlitPointerTuple tup);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate void raw_security_callback(security_key key, byte* buffer, UInt64 buffer_size, BlitPointerTuple tup);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern UInt64 md_consumer_walk_security_list(IntPtr consumer, raw_security_callback on_security);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void md_consumer_scan_securities(IntPtr consumer);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void md_consumer_set_on_security(IntPtr consumer, raw_security_callback on_security);
internal unsafe delegate void raw_just_security_callback(security_key key, byte* buffer, UInt64 buffer_size);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern void md_consumer_get_single_security(MDConsumerHandle.ColdEntry* entry, raw_just_security_callback on_security);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern bool md_consumer_is_heart_beating(MDConsumerHandle.ColdHeader* header);
internal unsafe delegate void raw_receive_pb(byte* buffer, UInt64 buffer_size);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern int md_consumer_read_trades(IntPtr consumer, UserSymbol channel, ref Int64 cursor, raw_receive_pb cb);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern int md_consumer_read_rfqs(IntPtr consumer, UserSymbol channel, ref Int64 cursor, raw_receive_pb cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal unsafe delegate void raw_usersymbol_callback(UserSymbol us);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern void md_consumer_get_system_md_symbols(raw_usersymbol_callback cb);
//
// Relay control
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern IntPtr start_private_relay(IntPtr consumer, UInt32 interval);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern IntPtr stop_relay(IntPtr relay);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern IntPtr add_relay_symbol(IntPtr relay, UserSymbol symbol, bts.co2.MDRequest.Types.Flags flags);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal unsafe static extern void post_on_scan_security_thread(Action callback);
}
internal static class Co2NativeTestMethods
{
[DllImport("spark_lib", EntryPoint = "co2_get_1234_handle")]
[DllImport("libspark", EntryPoint = "co2_get_1234_handle")]
internal static extern void get_1234_handle(out UInt64 size, out Co2NativeMethods.BlitPointerTuple pt);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void co2_get_sample_hot(out ulong size, ref MDConsumerHandle.HotEntry entry);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void co2_get_sample_depth(out ulong size, ref MDConsumerHandle.HotDepthEntry entry);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void co2_get_sample_cold(out ulong size, ref MDConsumerHandle.ColdEntry entry);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void co2_get_sample_cold_header(ref MDConsumerHandle.ColdHeader entry);
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern IntPtr co2_produce_test_md();
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void co2_destroy_test_md(IntPtr handle);
}
@@ -124,8 +124,8 @@ namespace mbts.co2
public double session_high { get { return _cold->session_high;} }
public double session_low { get { return _cold->session_low; } }
public double open_interest { get { return _cold->open_interest; } }
public double session_high_bid { get { return _cold->session_high_bid; } }
public double session_low_offer { get { return _cold->session_low_offer; } }
// public double session_high_bid { get { return _cold->session_high_bid; } }
// public double session_low_offer { get { return _cold->session_low_offer; } }
public double fixing_price { get { return _cold->fixing_price; } }
//
+5 -5
View File
@@ -60,16 +60,16 @@ namespace mbts.config
/// private
////////////
[DllImport("spark_lib", EntryPoint = "get_buffer")]
[DllImport("libspark", EntryPoint = "get_buffer")]
private static extern IntPtr raw_get_buffer(string key_name, ref int length);
[DllImport("spark_lib")]
[DllImport("libspark")]
private static extern bool initialize_nothrow(StringBuilder buffer, int length);
[DllImport("spark_lib")]
[DllImport("libspark")]
private static extern void reset();
[DllImport("spark_lib")]
[DllImport("libspark")]
internal static extern void configcheck(int count, string[] args);
@@ -105,7 +105,7 @@ namespace mbts.config
public static bool is_running_nunit() {
return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(ass => ass.FullName.ToLower().StartsWith("nunit.framework")) != null;
}
[DllImport("spark_lib")]
[DllImport("libspark")]
public static extern void force_unittest_environment();
}
+2 -2
View File
@@ -111,7 +111,7 @@ namespace mbts
return "Log file does not exist";
}
[DllImport("spark_lib")]
[DllImport("libspark")]
private static extern void Logger_log (Level level, Subsystem subsystem, string message, string file, int line);
delegate void on_crash_callback([MarshalAs(UnmanagedType.LPStr)]string msg, [MarshalAs(UnmanagedType.LPStr)] string details);
@@ -120,4 +120,4 @@ namespace mbts
}
public static event Action<string, object> OnSystemCrashing;
}
}
}
@@ -7,7 +7,7 @@ namespace mbts {
return string.Format(input, vals);
}
[DllImport("spark_lib")]
[DllImport("libspark")]
private static extern void ll_to_parts(UInt64 ll_time, out int year, out int month, out int day, out int hour, out int minute, out int second, out int ms);
public static DateTime lldatetime(this ulong ll) {
@@ -97,14 +97,14 @@ namespace bts.sdk
{
check_subscriptions(flags, x =>
(x.Exchange == product.Exchange && x.Type == product.Type && x.Symbol == product.Symbol));
return subscribe(
return _subscribe(
x => (x.Exchange == product.Exchange && ((SecurityImmutable)x).security_type() == product.Type && x.Symbol == product.Symbol),
update_interval,
scheduler);
}
public IObservable<ChannelUpdate> subscribe(Func<Security, bool> filter,
TimeSpan? update_interval = null, IScheduler scheduler = null)
IObservable<ChannelUpdate> _subscribe(Func<Security, bool> filter,
TimeSpan? update_interval = null, IScheduler scheduler = null)
{
Data[] slots = new Data[0]; long local_security_count = 0;
return timer_pump<Data>((last_update, observer) =>
@@ -173,7 +173,7 @@ namespace bts.sdk
long last_update_time = 0;
return Observable.Create<T>(observer =>
{
return Observable.Timer(interval.Value, scheduler ?? Scheduler.Default).Subscribe(_ =>
return Observable.Timer(interval.Value, interval.Value, scheduler ?? Scheduler.Default).Subscribe(_ =>
{
tick(last_update_time, observer);
last_update_time = DateTime.UtcNow.Ticks;
@@ -61,12 +61,13 @@ namespace bts.sdk
Venue ven = parse_venue(exch);
SecurityType st = parse_security_type(sectype);
Product prod = new Product() { Exchange = ven, Type = st, Symbol = symbol };
Func<Security, bool> filter = (x => x.Glbx.SecurityDesc == security_desc);
_disps.Add(_md.subscribe(filter).Subscribe(x =>
{
print_update(x);
}));
//if you have a security_id, we could make a more specifics subscription, but this is a command line driver....
_disps.Add(_md.subscribe(prod).Where(x => filter(x.Security)).Subscribe(x =>
{
print_update(x);
}));
}
public void Dispose()
+1 -13
View File
@@ -42,7 +42,7 @@ namespace bts.sdk
s.print_channel(args[1], args[2], args[3]);
}
else
md_poll_help();
md_help();
}
else
md_help();
@@ -101,7 +101,6 @@ namespace bts.sdk
Console.WriteLine();
Console.WriteLine("Commands:");
Console.WriteLine(" md <exchange> <security_type> <symbol> [<security_desc>]");
Console.WriteLine(" mdpoll <exchange> <security_type> <symbol> [<security_desc>]");
Console.WriteLine(" ui <exchange> <security_type> <symbol> <security_desc> <poll_interval_in_ms>");
Console.WriteLine(" order <exchange> <security_type> <symbol> <security_desc> <signed_quantity> [<price>]");
Console.WriteLine("Arguments:");
@@ -131,17 +130,6 @@ namespace bts.sdk
Environment.Exit(1);
}
static void md_poll_help()
{
Console.WriteLine("mdpoll <exchange> <security_type> <symbol> [<security_desc>]");
Console.WriteLine("This command subscribes to market data for an exchange-type-symbol channel and writes to console.");
Console.WriteLine("Marketdata is read off the wire on one thread and read and printed on another through IScheduler");
Console.WriteLine(" example: md_threaded GLBX Future ES");
Console.WriteLine(" md_threaded GLBX Future ES ESZ8");
Console.WriteLine(" md_threaded GLBX Spread ES");
Environment.Exit(1);
}
static void ui_help()
{
Console.WriteLine("ui <exchange> <security_type> <symbol> <security_desc> <poll_interval_in_ms>");
+1 -1
View File
@@ -50,7 +50,7 @@
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
<BuildOutputInPackage Include="$(OutputPath)..\net471\spark_lib.dll"/>
<BuildOutputInPackage Include="$(OutputPath)..\net471\libspark.dll"/>
</ItemGroup>
</Target>
</Project>
+1
View File
@@ -0,0 +1 @@
set(sources /home/ppinto/Dropbox/bts/spark/src/data_files/files/Onixs.BTS.lic;/home/ppinto/Dropbox/bts/spark/src/data_files/files/cert_secure_logon.txt)
+18
View File
@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.5)
project(data_files)
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
# DEPENDENCIES
file (GLOB_RECURSE To_Copy ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR} files/*)
update_deps_file("${To_Copy}")
# END DEPENDENCIES
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/data_files
COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/copy_data_files.sh
ARGS ${CMAKE_BINARY_DIR}/data_files ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Copying data files"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/copy_data_files.sh ${To_Copy})
add_custom_target(data_files_dir ALL DEPENDS ${CMAKE_BINARY_DIR}/data_files)
Loaded 100 of 304 files, more files were not shown because too many files have changed in this diff. Show more