Compare commits

...
200 Commits
Author SHA1 Message Date
John Colvin 8e43f96331 SPARK-1469 Fills Window - add "FillKind" column 2018-07-19 14:11:59 -04:00
Jake Norbut 3187e4134f Merged in SPARK-1461 (pull request #49)
SPARK-1461 - price columns don't update tick format when security becomes known

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

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

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

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

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

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

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-07-16 11:23:19 +00:00
Eric Harding 78c8234eef SPARK-1456 ladder re-center 2018-07-16 07:22:34 -04:00
Pedro Pinto 1f1f92d337 SPARK-1156 2018-07-15 00:34:15 +00:00
John Colvin a6426e7a98 SPARK-1455 Tick display wrong for ZB, likely others 2018-07-14 17:48:21 -04:00
Eric Harding dc1a7b3f5d Remove defunct DataPersistor 2018-07-13 17:22:59 -04:00
Pedro Pinto 2a549e551f SPARK-1156 2018-07-13 20:09:42 +00:00
Pedro Pinto c158188557 SPARK-1156 2018-07-13 19:40:32 +00:00
Jake Norbut 17614b663e Merge branch 'master' of bitbucket.org:bluetradesys/spark into pnl_nits2 2018-07-13 14:21:46 -05:00
Pedro Pinto 5e36a22aba Merge 2018-07-13 13:19:01 -04:00
Pedro Pinto 442fbd2888 Updating the Web UI to support the SIM exchange. 2018-07-13 17:15:38 +00:00
Jake Norbut d1975cdd00 misc refactoring 2018-07-13 11:44:50 -05:00
Eric Harding a2c75f08d8 Fix up f# for new exchange. 2018-07-13 12:43:16 -04:00
Pedro Pinto 02a867f8a4 Towards sim exchange. 2018-07-13 16:32:08 +00:00
Jake Norbut adc48d7871 Cpps will be manually maintained; adding helper script to aid in their generation 2018-07-13 11:20:14 -05:00
Michael Harding 33bd089e74 SPARK-1370: Remove wildcards, confirmation modals, SMP 2018-07-12 16:27:49 -04:00
Jake Norbut edcb5a5a24 Changing default day start time to 4:30 as a result of new info from dev call 2018-07-12 13:29:21 -05:00
Jake Norbut 6f39705d4f PnL will now use cpp from config rather than security 2018-07-12 13:26:47 -05:00
Jake Norbut 07377b8cc9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into pnl_nits2 2018-07-12 12:07:08 -05:00
Jake Norbut 77ad9ca011 fixing no UTC to UTC time comparison bug; also, somehow swapped day vs start of day comparison 2018-07-12 12:00:46 -05:00
Pedro Pinto 10f24aa2e9 Towards SPARK-1156 2018-07-12 14:21:48 +00:00
Pedro Pinto 37fc5880ea Ran clang-format. 2018-07-12 13:34:28 +00:00
Eric Harding 910d991d92 SPARK-1448 Commit/cancel 'add to favorites' on focus move 2018-07-11 18:20:41 -04:00
Eric Harding ced8bd9fea CMakeDeps wants to update. ok. 2018-07-11 18:18:04 -04:00
Jake Norbut c5004f46ee SPARK-1458 cpp config proto and defaults 2018-07-11 17:15:34 -05:00
Eric Harding 1deb5d25b0 SPARK-1376 - My Windows : Cancel on escape 2018-07-11 16:45:31 -04:00
Jake Norbut b5950ab8c0 actually save the start of day rather than overwriting it anew each time 2018-07-11 13:04:34 -05:00
Jake Norbut a233451333 add start of day to pnl settings 2018-07-11 12:55:15 -05:00
Pedro Pinto deef8136df Adding script to synch up sdk repo 2018-07-11 12:56:16 -04:00
Pedro Pinto 9dda440a48 Cleaned up sdk. 2018-07-11 16:26:26 +00:00
Eric Harding f22442e665 fix build 2018-07-11 12:23:06 -04:00
Michael Harding 1ba7ceaf6a SparkAdmin: Context menus on menu items 2018-07-11 11:58:02 -04:00
Jake Norbut 54676d5aa9 Adding pnl_calc_type to state proro; tidying up VM 2018-07-11 09:12:06 -05:00
Jake Norbut edf5c78405 part of SPARK-1454; column filters and sort criteria 2018-07-10 16:56:23 -05:00
Eric Harding f621d08f72 SPARK-1437 Simple market data producer sample 2018-07-10 17:41:52 -04:00
Eric Harding 724243c762 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-07-10 17:16:07 -04:00
Eric Harding f70e8de497 Update git_switch_linux to purge before checkout 2018-07-10 17:15:08 -04:00
Pedro Pinto c97e4be79e Added libs to sdk 2018-07-10 19:02:22 +00:00
Taha Afzal 95be0015d6 fixing build 2018-07-10 14:30:11 -04:00
Pedro Pinto cbe8917f28 Fixed unit tests. 2018-07-10 16:02:14 +00:00
Pedro Pinto 653b1c7814 clang build fixes. 2018-07-10 15:37:16 +00:00
Michael Harding 9a1098669e SparkAdmin: SPARK-1447 - Limits Changes 2018-07-10 10:54:40 -04:00
Michael Harding dcf6c69ef9 SparkAdmin: Fix Lazy Nav 2018-07-10 10:41:49 -04:00
Michael Harding fdcd245ef5 SparkAdmin: Reorganize limits code, add dropdown menu control. 2018-07-10 10:32:55 -04:00
Taha Afzal cf9133da8d SPARK-1404 Trigger Order proto's polished 2018-07-10 10:29:34 -04:00
Michael Harding 802501a337 Merged SparkAdmin into master 2018-07-10 09:13:22 -04:00
Michael Harding b9c6f8d309 Merged master into SparkAdmin 2018-07-10 09:13:00 -04:00
Michael Harding ee94f43628 fix build 2018-07-10 09:11:58 -04:00
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
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
264 changed files with 18205 additions and 5592 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()
+5
View File
@@ -0,0 +1,5 @@
1.1.0
Added self match protection, location and self match protection
instructions tags, these will have to be set in the UI for the user
for orders to be entered.
+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>();
}
}
@@ -14,6 +14,7 @@
<ResourceDictionary Source="/view/Resources/DataTemplates.xaml"/>
<ResourceDictionary Source="/view/Controls/Popup/PopupControl.xaml"/>
<ResourceDictionary Source="/Modules/Spreader/ParameterSectionView.xaml"/>
<ResourceDictionary Source="/view/Controls/TickFactorsPicker/TickFactorsPicker.xaml"/>
</ResourceDictionary.MergedDictionaries>
<sys:Double x:Key="RowHeaderWidth">285</sys:Double>
<sys:Double x:Key="RowHeight">16</sys:Double>
@@ -365,20 +366,11 @@
Grid.Column="2" VerticalAlignment="Center" Margin="6,0,0,0" Text="Ticks" IsEnabled="{TemplateBinding IsNew}"
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
/>
<ComboBox
x:Name="PART_Tick_ComboBox" Grid.Column="3" Margin="6,0,0,0" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
ItemsSource="{TemplateBinding AvailableTickFactors}" SelectedItem="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}}"
<c:TickFactorsPicker
x:Name="PART_TickFactorsPicker" Grid.Column="3" Margin="6,0,0,0" IsEnabled="{TemplateBinding IsNew}" HorizontalAlignment="Stretch"
SelectedTickFactors="{Binding TickFactors, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Visibility="{Binding LegTuples.Count, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IntNotZeroVisibilityConverter}}"
>
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ComboBox.DelineatedGroupContainerStyle}"/>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource TickFactorsTextConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
/>
<TextBlock Grid.Column="4" VerticalAlignment="Center" Margin="6,0,0,0">Pricing</TextBlock>
<ComboBox
Grid.Column="5" Margin="6,0,0,0" IsEnabled="False"
@@ -50,13 +50,6 @@ namespace mbts.ui.spark {
public TickFactors TickFactors { get => GetValue(TickFactorsProperty) as TickFactors; set => SetValue(TickFactorsProperty, value); }
#endregion
#region AvailableTickFactors
public static readonly DependencyProperty AvailableTickFactorsProperty = DependencyProperty.Register(
nameof(AvailableTickFactors), typeof(IEnumerable), typeof(ExecutionStrategyEditor), new PropertyMetadata(new TickFactors[0]));
public IEnumerable AvailableTickFactors { get => GetValue(AvailableTickFactorsProperty) as IEnumerable; set => SetValue(AvailableTickFactorsProperty, value); }
#endregion
#region IsNew
public static readonly DependencyProperty IsNewProperty =
DependencyProperty.Register(
@@ -253,11 +246,11 @@ namespace mbts.ui.spark {
class ExecutionStrategyErrorAdorner : NotifyDataErrorAdorner<ExecutionStrategyEditor, SyntheticEditorVM>, IDisposable {
readonly ChildProperty<TextBox> _name_text_box;
readonly ChildProperty<ComboBox> _tick_combo_box;
readonly ChildProperty<TickFactorsPicker> _tick_factors_picker;
public ExecutionStrategyErrorAdorner(ExecutionStrategyEditor editor) : base(editor) {
_name_text_box = child_property<TextBox>("PART_Name_TextBox");
_tick_combo_box = child_property<ComboBox>("PART_Tick_ComboBox");
_tick_factors_picker = child_property<TickFactorsPicker>("PART_TickFactorsPicker");
}
protected override void OnRender(DrawingContext drawing_context) {
@@ -269,7 +262,7 @@ namespace mbts.ui.spark {
if (_vm != null && _vm.GetErrors(nameof(SyntheticEditorVM.name)).Cast<string>().Any())
this.draw_border(drawing_context, _name_text_box.value, null, new Pen(Brushes.Red, 1));
if (_vm != null && _vm.GetErrors(nameof(SyntheticEditorVM.tick_factors)).Cast<string>().Any())
this.draw_border(drawing_context, _tick_combo_box.value, null, new Pen(Brushes.Red, 1));
this.draw_border(drawing_context, _tick_factors_picker.value, null, new Pen(Brushes.Red, 1));
string error_text = string.Join(", ", _vm.AllErrors);
this.draw_relative_text(RelativePosition.Below, error_text, drawing_context, _name_text_box.value, BTSColors.header_error_text_brush, 0, y_offset);
}
@@ -23,7 +23,6 @@
HorizontalAlignment="Stretch" Margin="6"
SyntheticName="{Binding name}"
TickFactors="{Binding tick_factors}"
AvailableTickFactors="{Binding available_tick_factors_view}"
IsNew="{Binding is_new}"
CanUserAddNewLeg="{Binding can_add_leg}"
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
@@ -1,6 +1,7 @@
using bts.api;
using bts.shell;
using bts.spark;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.spark;
@@ -10,11 +11,9 @@ using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
@@ -23,7 +22,6 @@ namespace mbts.spark {
/// The view model designed to serve as the DataContext for the SyntheticEditorView
/// </summary>
partial class SyntheticEditorVM : DisposableViewModelBase, IRequestViewClose, IRequestBindingSourceUpdate, IDisposable {
readonly ObservableCollection<TickFactors> _available_tick_factors = new ObservableCollection<TickFactors>(TickUtils.all_present_ticks());
readonly QuantityModel _quantity_model;
readonly PriceMultiplierModel _price_mult_model;
readonly List<IDisposable> _resources = new List<IDisposable>();
@@ -121,7 +119,7 @@ namespace mbts.spark {
flash_error_msg("Name already taken", SparkUtils.UI.MessageFlashDuration, nameof(name)).ignore();
valid = false;
}
if (_tick_factors == null || !_tick_factors.is_valid() || !_available_tick_factors.Contains(_tick_factors)) {
if (_tick_factors == null || !_tick_factors.is_valid()) {
flash_error_msg("Please select ticks", SparkUtils.UI.MessageFlashDuration, nameof(tick_factors)).ignore();
valid = false;
}
@@ -133,6 +131,8 @@ namespace mbts.spark {
_synthetic.ContractSpec.LowTick = tick_frac;
_synthetic.ContractSpec.HighTick = tick_frac;
_synthetic.ContractSpec.PriceBreak = double.MaxValue;
_synthetic.ContractSpec.MainFraction = (uint)MathUtils.factor_out(_tick_factors.tick_base, 10);
_synthetic.ContractSpec.PriceDisplayFormat = (uint)_tick_factors.display_digits();
uint? id = await model.create_synthetic(_synthetic);
if (id.HasValue && id != 0) {
if (await model.save_strategy(id.Value, get_strategy())) {
@@ -152,8 +152,6 @@ namespace mbts.spark {
_set_ticks_automatically = is_new;
_set_quantities_automatically = is_new;
_set_price_mults_automatically = is_new;
available_tick_factors_view = CollectionViewSource.GetDefaultView(_available_tick_factors);
available_tick_factors_view.GroupDescriptions.Add(new PropertyGroupDescription(nameof(TickFactors.tick_base)));
_quantity_model = new QuantityModel(!is_new);
_price_mult_model = new PriceMultiplierModel(!is_new);
@@ -245,9 +243,11 @@ namespace mbts.spark {
public TickFactors tick_factors {
get => _tick_factors;
set {
_tick_factors = value;
_set_ticks_automatically = false;
RaisePropertyChanged();
if (_tick_factors != value) {
_tick_factors = value;
_set_ticks_automatically = false;
RaisePropertyChanged();
}
}
}
public bool has_working_orders => _has_working_orders.Value;
@@ -261,7 +261,6 @@ namespace mbts.spark {
public IRaiseCanExecuteChangedCommand commit { get; }
public ICommand remove_leg { get; }
public ContractPickerModel contract_picker_model { get; }
public ICollectionView available_tick_factors_view { get; }
public ExecutionStrategyImmutable get_strategy() {
var mutable_strategy = _strategy.CloneToMutable();
@@ -328,8 +327,6 @@ namespace mbts.spark {
void _deduce_ticks() {
_tick_factors = TickUtils.deduce_ticks(leg_tuples.Select(l => l.leg.Security.ContractSpec));
if (_tick_factors != null && !_available_tick_factors.Contains(tick_factors))
_available_tick_factors.Add(_tick_factors);
RaisePropertyChanged(nameof(tick_factors));
}
@@ -338,4 +335,4 @@ namespace mbts.spark {
else clear_error(nameof(leg_count));
}
}
}
}
@@ -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,8 @@ namespace bts.shell {
public TimeFilterTuple last_change;
[ProtoMember(18)]
public string security_regex;
[ProtoMember(19)]
public api.orderman.OrderSubType[] order_sub_types;
}
}
}
@@ -1,6 +1,7 @@
using bts.api;
using bts.spark;
using mbts.spark;
using mbts.spark.filters;
using mbts.ui;
using mbts.ui.spark;
using ProtoBuf;
@@ -20,7 +21,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 +33,58 @@ 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;
}
}
+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);
@@ -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;
}
@@ -18,6 +18,7 @@ namespace mbts.spark {
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 trigger(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TriggerOrderVM(model, security_source);
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityImmutable> security_source) => new IcebergOrderVM(model, security_source);
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source) => new SpreaderOrderVM(model, security_source);
@@ -35,6 +36,7 @@ namespace mbts.spark {
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.Trigger: return trigger(model, security_source);
case CustomOneofCase.Iceberg: return iceberg(model, security_source);
case CustomOneofCase.Spreader: return spreader(model, security_source);
default: throw new NotImplementedException();
@@ -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<SecurityImmutable> _security_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<SecurityImmutable> security_source)
: this(model, security_source, security_source.Select(s =>
new InlineTriggerOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
public TriggerOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
IObservable<InlineTriggerOrderProperties> inline_properties) : base(model)
{
_security_source = security_source;
_dispose = Disposable.Create(
security_source.OnDispatcher()
.Subscribe(s => _security = s),
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_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;
}
}
}
@@ -17,8 +17,10 @@ namespace mbts {
public static double tick_unit_frac(this TickFactors tick) =>
tick.tick_unit / tick.tick_base;
public static double reduced_base(this TickFactors tick) => factor_out(tick.tick_base, 10);
public static string format(this TickFactors tick, double value) =>
format(tick, value, double.NaN, true, _nan_text);
format(tick, value, double.NaN, false, _nan_text);
public static string format(this TickFactors tick, double value, bool show_commas) =>
format(tick, value, double.NaN, show_commas, _nan_text);
@@ -176,9 +178,10 @@ namespace mbts {
if (DoubleHelper.IsNaNOrInfinity(input)) return input;
return input <= regional_tick.low_tick_inclusive_end ? regional_tick.low_tick.round(input) : regional_tick.high_tick.round(input);
}
public static int display_digits(this TickFactors tick) => display_digits(tick.tick_base);
#endregion
public static int n_display_digits(double tick_base) {
public static int display_digits(double tick_base) {
double log_tick_base = Math.Log10(tick_base);
int floor = (int)Math.Floor(log_tick_base);
double dec = log_tick_base - floor;
@@ -216,7 +219,7 @@ namespace mbts {
if (numerator_part >= tick_base) numerator_part /= Math.Pow(10, Math.Ceiling(Math.Log10(numerator_part) - Math.Log10(tick_base)));
else {
//e.g [tick_base 640] 1989-8 == 1989-80, or 1989-32 == 1989-320
int ndd_diff = n_display_digits(tick_base) - numerator_text.Length;
int ndd_diff = display_digits(tick_base) - numerator_text.Length;
if (ndd_diff > 0) numerator_part *= Math.Pow(10, ndd_diff);
if (numerator_part >= tick_base) numerator_part /= 10;
}
@@ -255,16 +258,21 @@ namespace mbts {
double numerator_dec = value - integer_part;
double unit_frac = tick.tick_unit / tick.tick_base;
int n_units = (int)Math.Round(numerator_dec / unit_frac);
if (n_units == tick.tick_base) return (integer_part + 1).ToString() + tick.separator() + new string('0', n_display_digits(tick.tick_base));
if (n_units == tick.tick_base) return (integer_part + 1).ToString() + tick.separator() + new string('0', display_digits(tick.tick_base));
double decimal_part = n_units * tick.tick_unit;
if (decimal_part == tick.tick_base) {
++integer_part;
decimal_part = 0;
} else {
// some ticks are not formatted with 100% precision, using less than the required display_digits
// for such cases, we need to truncate the decimal part rather than round it.
decimal_part = Math.Floor(decimal_part);
}
string integer_part_text = show_commas ? integer_part.ToString("#,0") : integer_part.ToString();
return integer_part_text + tick.separator() + decimal_part.ToString(new string('0', n_display_digits(tick.tick_base)));
return integer_part_text + tick.separator() + decimal_part.ToString(new string('0', display_digits(tick.tick_base)));
}
} catch (OverflowException ex) {
}
catch (OverflowException ex) {
Logger.warn(bts.log.Subsystem.Ui, $"Arithmetic overflow exception in format_tick (tick:{tick}, value:{value}):{ex}");
return "Overflow";
}
@@ -272,7 +280,7 @@ namespace mbts {
private static double _round_tick(double tick_unit_frac, double input) {
return input - Math.IEEERemainder(input, tick_unit_frac);
}
}
#endregion
}
}
@@ -8,10 +8,10 @@ namespace mbts.ui.native
{
public static class BTSUINativeMethods
{
[DllImport("spark_lib")]
[DllImport("libspark")]
public static extern void enable_limits();
[DllImport("spark_lib", CharSet = CharSet.Unicode)]
[DllImport("libspark", CharSet = CharSet.Unicode)]
public static extern void set_window_app_id(IntPtr hwnd, string stack_id);
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace mbts.utils {
@@ -25,6 +26,9 @@ namespace mbts.utils {
public static IEqualityComparer<T?> CreateNullable<T>(Func<T, T, bool> equals) where T : struct =>
new AnonymousEqualityComparer<T?>((a, b) => a.HasValue ? b.HasValue && equals(a.Value, b.Value) : !b.HasValue);
public static IEqualityComparer to_non_generic<T>(this IEqualityComparer<T> comparer) =>
new AnonymousEqualityComparer<T>(comparer.Equals, comparer.GetHashCode);
public static bool NullCheckEquals<T>(T x, T y, Func<T, T, bool> non_null_equals) where T : class, IEquatable<T> =>
x == null ? y == null :
y != null &&
@@ -55,7 +59,7 @@ namespace mbts.utils {
static void _accumulate_hash(ref int aggregate_hash, ref int item_hash) =>
aggregate_hash = aggregate_hash * _hash_multiplier + item_hash;
private class AnonymousEqualityComparer<T> : IEqualityComparer<T> {
private class AnonymousEqualityComparer<T> : IEqualityComparer<T>, IEqualityComparer {
readonly Func<T, T, bool> _equals;
readonly Func<T, int> _get_hash_code;
@@ -67,6 +71,9 @@ namespace mbts.utils {
public bool Equals(T x, T y) => _equals(x, y);
public int GetHashCode(T obj) => _get_hash_code(obj);
bool IEqualityComparer.Equals(object x, object y) => x is T xt && y is T yt && _equals(xt, yt);
int IEqualityComparer.GetHashCode(object obj) => throw new NotImplementedException();
}
}
}
+20 -1
View File
@@ -1,4 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace mbts.utils {
public static class CompareUtils {
@@ -19,5 +21,22 @@ namespace mbts.utils {
b.HasValue ? compare(a.Value, b.Value) : 1 :
b.HasValue ? -1 : 0;
#endregion
#region Comparer
public static IComparer to_non_generic<T>(this IComparer<T> comparer) =>
new NonGenericComparerWrapper<T>(comparer);
public static IComparer<TOutter> wrap<TInner, TOutter>(this IComparer<TInner> inner_comparer, Func<TOutter, TInner> selector) =>
Comparer<TOutter>.Create((a, b) => inner_comparer.Compare(a == null ? default : selector(a), b == null ? default : selector(b)));
class NonGenericComparerWrapper<T> : IComparer {
readonly IComparer<T> _comparer;
public NonGenericComparerWrapper(IComparer<T> comparer) => _comparer = comparer;
public int Compare(object x, object y) =>
_comparer.Compare(ObjectHelper.try_convert<T>(x), ObjectHelper.try_convert<T>(y));
}
#endregion
}
}
}
@@ -0,0 +1,6 @@
namespace mbts.spark {
interface IValueFactoryVM<T> {
T create();
void load_from(T value);
}
}
@@ -335,7 +335,45 @@
/>
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.TopOrderTemplate>
</usp:OrderModificationTemplateSelector.TopOrderTemplate>
<usp:OrderModificationTemplateSelector.TriggerOrderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Trigger Price Type</TextBlock>
<ComboBox
Grid.Row="1" Grid.Column="2"
ItemsSource="{Binding trigger_price_types}"
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
SelectedItem="{Binding trigger_price_type}"
/>
<TextBlock Grid.Row="3" VerticalAlignment="Center">Order Price</TextBlock>
<usp:PriceEntryControl Grid.Row="3" Grid.Column="2" Value="{Binding order_price}" ContractSpec="{Binding spec}" />
<TextBlock Grid.Row="5">Child</TextBlock>
<uag:OrderDetailsPopupControl
Grid.Row="5" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
ItemsSource="{Binding child_order_types}"
SelectedItem="{Binding child_order}"
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
/>
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.TriggerOrderTemplate>
<usp:OrderModificationTemplateSelector.IcebergOrderTemplate>
<DataTemplate>
<Grid>
@@ -375,7 +413,6 @@
DataContext="{Binding strategy_editor}"
SyntheticName="{Binding name}"
TickFactors="{Binding tick_factors}"
AvailableTickFactors="{Binding available_tick_factors_view}"
IsNew="False"
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
PriceCalc="{Binding price_calc}"
@@ -41,7 +41,7 @@ namespace mbts.ui.spark {
#region ContractSpec
public static readonly DependencyProperty ContractSpecProperty = DependencyProperty.Register("ContractSpec", typeof(ContractSpecificationImmutable), typeof(PriceEntryControl),
new PropertyMetadata(SecurityDataHelper.Defaults.ContractSpecificationImmutable, OnContractSpecChanged));
new PropertyMetadata(SecurityDataHelper.Defaults.ContractSpecificationImmutablePennies, OnContractSpecChanged));
static void OnContractSpecChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((PriceEntryControl)d)._update_text_binding();
public ContractSpecificationImmutable? ContractSpec { get => GetValue(ContractSpecProperty) as ContractSpecificationImmutable?; set => SetValue(ContractSpecProperty, value); }
#endregion
@@ -0,0 +1,75 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
xmlns:cv="clr-namespace:mbts.ui.converters"
xmlns:txt="clr-namespace:mbts.ui.utility.text"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/view/Resources/ComboBoxes.xaml"/>
<ResourceDictionary Source="/view/Resources/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="c:TickFactorsPicker">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="c:TickFactorsPicker">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="4"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox
MinWidth="48"
IsEditable="True"
DisplayMemberPath="value"
SelectedValuePath="value"
ItemsSource="{Binding TickUnitPresetsView, RelativeSource={RelativeSource TemplatedParent}}"
SelectedValue="{Binding Controller.tick_unit, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Text="{Binding Controller.tick_unit, Converter={StaticResource DoubleTextConverter}, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
>
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ComboBox.DelineatedGroupContainerStyle}"/>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{x:Static c:TickFactorsPicker.TickUnitVisibleTextConverter}">
<Binding Path="Controller.tick_base" RelativeSource="{RelativeSource AncestorType=c:TickFactorsPicker}"/>
<Binding Path="value"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="1" Foreground="{u:BTSColor UI.input_disabled_text}" Text="{Binding value, Converter={x:Static c:TickFactorsPicker.TickUnitHiddenTextConverter}}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox
Grid.Column="2"
MinWidth="60"
IsEditable="True"
DisplayMemberPath="value"
SelectedValuePath="value"
ItemsSource="{Binding TickBasePresetsView, RelativeSource={RelativeSource TemplatedParent}}"
SelectedValue="{Binding Controller.tick_base, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Text="{Binding Controller.tick_base, Converter={StaticResource DoubleTextConverter}, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
>
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource ComboBox.DelineatedGroupContainerStyle}"/>
</ComboBox.GroupStyle>
</ComboBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -0,0 +1,157 @@
using mbts.spark;
using mbts.spark.utilities;
using mbts.ui.converters;
using mbts.ui.shell;
using mbts.ui.utility;
using mbts.user.settings;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace mbts.ui.controls {
/// <summary>
/// Interaction logic for TickPicker.xaml
/// </summary>
class TickFactorsPicker : Control {
readonly ObservableCollection<double> _tick_unit_presets = new ObservableCollection<double>();
static TickFactorsPicker() =>
DefaultStyleKeyProperty.OverrideMetadata(typeof(TickFactorsPicker), new FrameworkPropertyMetadata(typeof(TickFactorsPicker)));
public TickFactorsPicker() {
Controller = new TickFactorsController(this);
TickBasePresetsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(TickBaseVM.reduced_base_group)));
((ListCollectionView)TickBasePresetsView).CustomSort = TickUtils.CompareUtils.base_comparer.wrap<double, TickBaseVM>(vm => vm.value).to_non_generic();
TickUnitPresetsView = CollectionViewSource.GetDefaultView(new ObservableAutoWrapper<TickUnitVM, double>(_tick_unit_presets, v => new TickUnitVM(v)));
TickUnitPresetsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(TickUnitVM.reduced_unit_group)));
((ListCollectionView)TickUnitPresetsView).CustomSort = Comparer<double>.Default.wrap<double, TickUnitVM>(vm => vm.value).to_non_generic();
}
public static IEnumerable<double> TickBasePresets =>
new [] {
8, 16, 32, 64, 100
}.SelectMany(i => Enumerable.Range(0, 4).Select(log10 => i * Math.Pow(10, log10)));
public static IMultiValueConverter TickUnitVisibleTextConverter =>
ValueConverter.create((double tick_base, double tick_unit) => {
int display_digits = TickExtensions.display_digits(tick_base);
return ((int)(tick_unit)).ToString(new string('0', display_digits));
});
public static IValueConverter TickUnitHiddenTextConverter =>
ValueConverter.create((double tick_unit) => new string(tick_unit.ToString().SkipWhile(c => c != '.').ToArray()));
public static readonly DependencyProperty SelectedTickFactorsProperty =
WPFHelper.create_dp<TickFactors, TickFactorsPicker>(
nameof(SelectedTickFactors),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
(o, v) => o.Controller.load_from(v),
new TickFactors());
public TickFactors SelectedTickFactors {
get => GetValue(SelectedTickFactorsProperty) as TickFactors;
set => SetValue(SelectedTickFactorsProperty, value);
}
public TickFactorsController Controller { get; }
public ICollectionView TickBasePresetsView { get; } = CollectionViewSource.GetDefaultView(TickBasePresets.Select(v => new TickBaseVM(v)).ToList());
public ICollectionView TickUnitPresetsView { get; }
static IEnumerable<double> _get_tick_unit_presets(double tick_base) {
double reduced_base = MathUtils.factor_out(tick_base, 10, out int base_log10);
return reduced_base == 1 ?
_get_dbased_tick_unit_presets(base_log10) :
_get_2based_tick_unit_presets((int)Math.Log(reduced_base, 2), base_log10);
}
static IEnumerable<double> _get_2based_tick_unit_presets(int log2, int log10) =>
(log10 == 0 ? new[] { 1.0 } : _get_dbased_tick_unit_presets(log10)).Concat(
Enumerable.Range(1, log2 - 1).SelectMany(pow2 => {
double root = Math.Pow(2, pow2);
return Enumerable.Range(0, log10 + 1).Select(pow10 => root * Math.Pow(10, pow10));
}));
static IEnumerable<double> _get_dbased_tick_unit_presets(int log10) =>
Enumerable.Range(0, log10).SelectMany(_coins);
static IEnumerable<double> _coins(int pow10) {
double d = Math.Pow(10, pow10);
yield return d;
yield return 1.25 * d;
yield return 2.5 * d;
yield return 5.0 * d;
}
public class TickFactorsController : ViewModelBase, IValueFactoryVM<TickFactors> {
readonly TickFactorsPicker _view;
readonly Property<double> _tick_base, _tick_unit;
public TickFactorsController(TickFactorsPicker view) {
var init = (_view = view).SelectedTickFactors;
_tick_base = backing(nameof(tick_base), init.tick_base);
_tick_unit = backing(nameof(tick_unit), init.tick_unit);
}
public double tick_base {
get => _tick_base.Value;
set {
_tick_base.set(value);
WPFHelper.set_current_if_changed(_view, SelectedTickFactorsProperty, new TickFactors(value, tick_unit));
_view._tick_unit_presets.sync_with(_get_tick_unit_presets(value));
}
}
public double tick_unit {
get => _tick_unit.Value;
set {
_tick_unit.set(value);
WPFHelper.set_current_if_changed(_view, SelectedTickFactorsProperty, new TickFactors(tick_base, value));
}
}
public TickFactors create() => new TickFactors(tick_base, tick_unit);
public void load_from(TickFactors value) {
if (value != null) {
_tick_base.set(value.tick_base);
_tick_unit.set(value.tick_unit);
}
}
}
public class TickBaseVM : IEquatable<TickBaseVM> {
public TickBaseVM(double value) =>
this.value = value;
public double value { get; }
public double reduced_base_group => MathUtils.factor_out(value, 10);
public bool Equals(TickBaseVM other) => other != null && value == other.value;
public override bool Equals(object obj) => Equals(obj as TickBaseVM);
public override int GetHashCode() => value.GetHashCode();
}
public class TickUnitVM : IEquatable<TickUnitVM> {
public TickUnitVM(double value) =>
this.value = value;
public double value { get; }
public double reduced_unit_group {
get {
if (!MathUtils.is_int(value))
return 1;
else return MathUtils.factor_out(value, 10, 5);
}
}
public bool Equals(TickUnitVM other) => other != null && value == other.value;
public override bool Equals(object obj) => Equals(obj as TickUnitVM);
public override int GetHashCode() => value.GetHashCode();
}
}
}
@@ -2,8 +2,15 @@
using System.Globalization;
using System.Windows.Data;
namespace mbts.ui.converters
{
namespace mbts.ui.converters {
static class ValueConverter {
public static IValueConverter create<TIn, TOut>(Func<TIn, TOut> convert) =>
new AnonymousValueConverter<TIn, TOut>(convert);
public static IMultiValueConverter create<TIn1, TIn2, TOut>(Func<TIn1, TIn2, TOut> convert) =>
new AnonymousMultiValueConverter<TIn1, TIn2, TOut>(convert);
}
public class AnonymousValueConverter : IValueConverter {
public AnonymousValueConverter(Func<object, object> convert) : this(convert, null) { }
public AnonymousValueConverter(Func<object, object> convert, Func<object, object> convert_back) {
@@ -13,7 +13,6 @@ namespace mbts.ui.converters
Minimum = Double.NaN;
Maximum = Double.NaN;
Multiplier = Double.NaN;
Format = FORMAT_WITH_COMMAS;
HideNaN = true;
}
@@ -14,9 +14,20 @@ namespace mbts.ui.converters {
static NumericSignBrushConverter() =>
_error_brush.Freeze();
public Color PositiveColor { get => _positive_brush.Color; set => (_positive_brush = new SolidColorBrush(value)).Freeze(); }
public Color NeutralColor { get => _neutral_brush.Color; set => (_neutral_brush = new SolidColorBrush(value)).Freeze(); }
public Color NegativeColor { get => _negative_brush.Color; set => (_negative_brush = new SolidColorBrush(value)).Freeze(); }
public Color PositiveColor { get => _positive_brush.Color; set => (_positive_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
public Color NeutralColor { get => _neutral_brush.Color; set => (_neutral_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
public Color NegativeColor { get => _negative_brush.Color; set => (_negative_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
private double _opacity = 1.0;
public Double Opacity {
get { return _opacity; }
set {
_opacity = value;
(_positive_brush = new SolidColorBrush(_positive_brush.Color) { Opacity = _opacity }).Freeze();
(_negative_brush = new SolidColorBrush(_negative_brush.Color) { Opacity = _opacity }).Freeze();
(_neutral_brush = new SolidColorBrush(_neutral_brush.Color) { Opacity = _opacity }).Freeze();
}
}
public IComparable Zero { get; set; } = 0.0;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
@@ -26,23 +26,16 @@ namespace mbts.ui.spark.converters {
}
class TickDisplayTwoWayConverter : IValueConverter {
public ContractSpecificationImmutable? ContractSpec { get; set; } =
new ContractSpecification {
CashPerPoint = 1.0,
LowTick = 0.01,
HighTick = 0.01,
PriceBreak = double.MaxValue,
PriceMultiplier = 1.0
};
public ContractSpecificationImmutable? ContractSpec { get; set; } = SecurityDataHelper.Defaults.ContractSpecificationImmutablePennies;
public double DisplayMultiplier { get; set; } = double.NaN;
public bool ShowCommas { get; set; } = false;
public string NaNText { get; set; } = string.Empty;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
ContractSpec.Value.format((double)value, DisplayMultiplier, ShowCommas, NaNText);
ContractSpec.format((double)value, DisplayMultiplier, ShowCommas, NaNText);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
ContractSpec.Value.parse(value as string, DisplayMultiplier, NaNText);
ContractSpec.parse(value as string, DisplayMultiplier, NaNText);
}
}
@@ -7,7 +7,7 @@ namespace mbts.ui.converters {
class TickFactorsTextConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is TickFactors tf) {
int n_digits = TickExtensions.n_display_digits(tf.tick_base);
int n_digits = tf.display_digits();
if (n_digits > 1) {
string n_digits_format = new string('0', n_digits);
string format = "{0:" + n_digits_format + "}/{1}";
@@ -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
}
}
+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">
@@ -1,63 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using bts.api;
using System.IO;
using mbts.ui.shell;
namespace mbts.spark
{
public class DataPersistor
{
private List<IDisposable> _disp_list = new List<IDisposable>();
private readonly IBus _bus;
private readonly string _loc = @"C:\ProgramData\bts\temp_securities.bin"; /// TODO
private SparkBusData.PersistedItems _persisted_items = new SparkBusData.PersistedItems();
public DataPersistor(IBus bus)
{
_bus = bus;
var sub = _bus.subscribe<SparkBusData.PersistMessage>(SparkTags.PersistCommandTag);
_disp_list.Add(sub.Subscribe(p => _on_new_persist_message(p)));
_persisted_items = _read_data_from_file();
_bus.post(SparkTags.PersistedItemsTag, _persisted_items);
}
private void _on_new_persist_message(SparkBusData.PersistMessage p)
{
_persisted_items.items.Add(p);
_write_to_file(_persisted_items);
_bus.post(SparkTags.PersistedItemsTag, new SparkBusData.PersistedItems(_persisted_items.items));
}
private void _write_to_file(SparkBusData.PersistedItems items)
{
byte[] data = SparkUtils.serialize_proto(items);
try
{
File.WriteAllBytes(_loc, data);
}
catch (Exception ex)
{
mbts.Logger.log(bts.log.Level.Error, bts.log.Subsystem.gen, ex.ToString());
}
}
private SparkBusData.PersistedItems _read_data_from_file()
{
var rv = new SparkBusData.PersistedItems();
try
{
if (File.Exists(_loc)) {
byte[] data = File.ReadAllBytes(_loc);
rv = SparkUtils.deserialize_proto<SparkBusData.PersistedItems>(data);
}
}
catch { }
return rv;
}
}
}
@@ -121,18 +121,6 @@ namespace mbts.spark {
public static string order_type_display_name(CustomOrderDefinition.CustomOneofCase order_type) =>
camel_to_title_case(Regex.Replace(order_type.ToString(), "Order", string.Empty, RegexOptions.IgnoreCase));
public static string order_collection_summary(ICollection<OrderRecordImmutable> orders, Comparison<double> compare_price, ContractSpecificationImmutable spec) {
if (orders.Count == 0) return string.Empty;
var price_lookup = orders.ToLookup(o => o.State.Price);
var best_price = price_lookup.MaxElement(g => g.Key, compare_price);
var best_price_volume = best_price.Sum(o => o.working_quantity());
return order_collection_summary(
best_price.Key,
best_price_volume,
best_price_volume + price_lookup.Where(g => g.Key != best_price.Key).SelectMany(g => g).Sum(o => o.working_quantity()),
spec);
}
public static string order_collection_summary(double best_price, int best_price_volume, int total_volume, ContractSpecificationImmutable spec) {
if (total_volume == 0)
return string.Empty;
+227 -140
View File
@@ -6,105 +6,40 @@ using mbts.utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace mbts.spark {
static class TickUtils {
#region TickFactors Cache
static readonly TickFactors _default_tick = new TickFactors(100, 1);
internal static ContractSpecificationImmutable spec_or_default(this ContractSpecificationImmutable? spec) =>
spec.HasValue ? spec.Value : SecurityDataHelper.Defaults.ContractSpecificationImmutable;
static readonly ConcurrentDictionary<double, bool> _resolved_unknown_ticks = new ConcurrentDictionary<double, bool>();
internal static ContractSpecificationImmutable spec_or_default(this SecurityImmutable security) =>
security.HasContractSpec ? security.ContractSpec : SecurityDataHelper.Defaults.ContractSpecificationImmutable;
static readonly ConcurrentDictionary<(double low_tick, double high_tick, double price_break), RegionalTickFactors> _regional_ticks_cache =
new ConcurrentDictionary<(double low_tick, double high_tick, double price_break), RegionalTickFactors>();
static readonly double[] _descending_tick_fractional_units;
static readonly IReadOnlyDictionary<double, TickFactors> _ticks_cache = new[] {
// 100 based
new { tick_unit = 0.01, tick_factors = new TickFactors(100, 1) }, // 1/100
new { tick_unit = 0.05, tick_factors = new TickFactors(100, 5) }, // 1/20
new { tick_unit = 0.10, tick_factors = new TickFactors(100, 10) }, // 1/10
new { tick_unit = 0.25, tick_factors = new TickFactors(100, 25) }, // 1/4
new { tick_unit = 0.50, tick_factors = new TickFactors(100, 50) }, // 1/2
// 1,000 based
new { tick_unit = 0.001, tick_factors = new TickFactors(1000, 1) }, // 1/1,000
new { tick_unit = 0.005, tick_factors = new TickFactors(1000, 5) }, // 1/200
new { tick_unit = 0.025, tick_factors = new TickFactors(1000, 25) }, // 1/40
// 10,000 based
new { tick_unit = 0.0001, tick_factors = new TickFactors(10000, 1) }, // 1/10,000
new { tick_unit = 0.0005, tick_factors = new TickFactors(10000, 5) }, // 1/2,000
new { tick_unit = 0.0025, tick_factors = new TickFactors(10000, 25) }, // 1/400
// 64 based
new { tick_unit = 0.015625, tick_factors = new TickFactors(64, 1) }, // 1/64
new { tick_unit = 0.03125, tick_factors = new TickFactors(64, 2) }, // 1/32
new { tick_unit = 0.0625, tick_factors = new TickFactors(64, 4) }, // 1/16
new { tick_unit = 0.125, tick_factors = new TickFactors(64, 8) }, // 1/8
// 640 based
new { tick_unit = 0.0015625, tick_factors = new TickFactors(640, 1) }, // 1/640
new { tick_unit = 0.003125, tick_factors = new TickFactors(640, 2) }, // 1/320
new { tick_unit = 0.00625, tick_factors = new TickFactors(640, 4) }, // 1/160
new { tick_unit = 0.0078125, tick_factors = new TickFactors(640, 5) }, // 1/128
new { tick_unit = 0.0125, tick_factors = new TickFactors(640, 8) }, // 1/80
new { tick_unit = 0.0390625, tick_factors = new TickFactors(640, 25) }, // 5/128
new { tick_unit = 0.078125, tick_factors = new TickFactors(640, 50) }, // 5/64
// 6,400 based
new { tick_unit = 0.00015625, tick_factors = new TickFactors(6400, 1) }, // 1/6,400
new { tick_unit = 0.0003125, tick_factors = new TickFactors(6400, 2) }, // 1/3,200
new { tick_unit = 0.000625, tick_factors = new TickFactors(6400, 4) }, // 1/1,600
new { tick_unit = 0.00078125, tick_factors = new TickFactors(6400, 5) }, // 1/1,280
new { tick_unit = 0.00125, tick_factors = new TickFactors(6400, 8) }, // 1/800
new { tick_unit = 0.00390625, tick_factors = new TickFactors(6400, 25) }, // 1/256
// 64,000 based
new { tick_unit = 0.000015625, tick_factors = new TickFactors(64000, 1) }, // 1/6,400
new { tick_unit = 0.00003125, tick_factors = new TickFactors(64000, 2) }, // 1/3,200
new { tick_unit = 0.0000625, tick_factors = new TickFactors(64000, 4) }, // 1/1,600
new { tick_unit = 0.000078125, tick_factors = new TickFactors(64000, 5) }, // 1/12,800
new { tick_unit = 0.000125, tick_factors = new TickFactors(64000, 8) }, // 1/8,000
new { tick_unit = 0.000390625, tick_factors = new TickFactors(64000, 25) }, // 1/2,560
new { tick_unit = 0.001953125, tick_factors = new TickFactors(64000, 125) },// 1/512
}.ToReadOnlyDictionary(obj => obj.tick_unit, obj => obj.tick_factors, EqualityComparer.Create<double>(MathUtils.epsilon_equals));
#endregion
static TickUtils() {
// overkill? Maybe, but ticks are fundamental to the entire application.
// Any changes to the tick defaults must be deliberate, and scrutinized.
Logger.assert(_ticks_cache.Count == 35);
foreach(var kv in _ticks_cache) {
double result = kv.Value.tick_unit / kv.Value.tick_base;
Logger.assert(result == kv.Key, $"{kv.Value.tick_unit}/{kv.Value.tick_base} ({result} != {kv.Key})");
}
_descending_tick_fractional_units = _ticks_cache.Keys.Ordered(new ReverseComparer<double>(Comparer<double>.Default)).ToLArray();
}
internal static ContractSpecificationImmutable spec_or_default(this SecurityImmutable? security) =>
security.HasValue && security.Value.HasContractSpec ? security.Value.ContractSpec : SecurityDataHelper.Defaults.ContractSpecificationImmutable;
public static double ticksize(this ContractSpecificationImmutable spec, double price) =>
Math.Abs(price) > spec.PriceBreak ? spec.HighTick : spec.LowTick;
public static string format(this ContractSpecificationImmutable spec, double price) {
var tick_factors = _get_tick_factors(spec.ticksize(price));
return tick_factors.format(price, false);
}
public static string format(this ContractSpecificationImmutable? spec, double price) {
if (spec.HasValue) {
return spec.Value.format(price);
}
return price.ToString("0.00000");
}
public static string format(this ContractSpecificationImmutable spec, double price) =>
TickStore.get(spec).format(price);
public static string format(this ContractSpecificationImmutable spec, double price, double display_multiplier, bool show_commas, string nan_text) {
var tick_factors = _get_tick_factors(spec.ticksize(price));
return tick_factors.format(price, display_multiplier, show_commas, nan_text);
}
public static string format(this ContractSpecificationImmutable? spec, double price) =>
spec.spec_or_default().format(price);
public static double parse(this ContractSpecificationImmutable spec, string text) {
var regional_tfs = get_regional_ticks(spec);
return regional_tfs.parse(text);
}
public static string format(this ContractSpecificationImmutable spec, double price, double display_multiplier, bool show_commas, string nan_text) =>
TickStore.get(spec).format(price, display_multiplier, show_commas, nan_text);
public static double parse(this ContractSpecificationImmutable spec, string text, double display_multiplier, string nan_text) {
var regional_tfs = get_regional_ticks(spec);
return regional_tfs.parse(text, display_multiplier, nan_text);
}
public static string format(this ContractSpecificationImmutable? spec, double price, double display_multiplier, bool show_commas, string nan_text) =>
spec.spec_or_default().format(price, display_multiplier, show_commas, nan_text);
public static double parse(this ContractSpecificationImmutable spec, string text, double display_multiplier, string nan_text) =>
TickStore.get(spec).parse(text, display_multiplier, nan_text);
public static double parse(this ContractSpecificationImmutable? spec, string text, double display_multiplier, string nan_text) =>
spec.spec_or_default().parse(text, display_multiplier, nan_text);
public static double low_tick(NativePriceSize[] bids, NativePriceSize[] asks) {
double low = bids[bids.Length - 1].price;
@@ -132,6 +67,9 @@ namespace mbts.spark {
return n_low + n_high;
}
public static int normalized_tick_diff(SecurityImmutable security, double x, double y) =>
normalized_tick_diff(security.spec_or_default(), x, y);
public static double mid_tick(ContractSpecificationImmutable spec, double bid, double ask) {
double raw_mid;
if (double.IsNaN(bid)) {
@@ -146,36 +84,35 @@ namespace mbts.spark {
return raw_mid - (raw_mid % tick_size);
}
public static IReadOnlyList<TickFactors> all_present_ticks() =>
_ticks_cache.Values.ToReadOnlyList();
public static TickFactors deduce_ticks(IEnumerable<ContractSpecification> contract_specs) {
var gcd = MathUtils.GCD(contract_specs.Select(s => s.LowTick));
if (gcd == 0) return null;
return _try_get_tick_factors(gcd);
var ticks = new HashSet<TickFactors>(contract_specs.Select(TickStore.get).SelectMany(rtf => new[] { rtf.low_tick, rtf.high_tick }));
switch (ticks.Count) {
case 0: return TickStore.default_rtf.low_tick;
case 1: return ticks.First();
default:
double tick_base_lcm = MathUtils.LCM(ticks.Select(t => (int)t.tick_base));
double tick_unit_gcd = MathUtils.GCD(ticks.Select(t => t.tick_unit * (tick_base_lcm / t.tick_base)));
return new TickFactors(tick_base_lcm, tick_unit_gcd);
}
}
public static double tickup(this ContractSpecificationImmutable contract, double value) =>
get_regional_ticks(contract).tickup(value);
public static double tickup(this ContractSpecificationImmutable spec, double value) =>
TickStore.get(spec).tickup(value);
public static double tickup(this ContractSpecificationImmutable contract, double value, int count) =>
get_regional_ticks(contract).tickup(value, (uint)count);
public static double tickup(this ContractSpecificationImmutable spec, double value, int count) =>
TickStore.get(spec).tickup(value, (uint)count);
public static double tickdown(this ContractSpecificationImmutable contract, double value) =>
get_regional_ticks(contract).tickdown(value);
public static double tickdown(this ContractSpecificationImmutable spec, double value) =>
TickStore.get(spec).tickdown(value);
public static double tickdown(this ContractSpecificationImmutable contract, double value, int count) =>
get_regional_ticks(contract).tickdown(value, (uint)count);
public static double tickdown(this ContractSpecificationImmutable spec, double value, int count) =>
TickStore.get(spec).tickdown(value, (uint)count);
public static bool tick_equals(this ContractSpecificationImmutable contract, double x, double y) =>
MathUtils.RoundFractionalUnits(x, contract.LowTick) == MathUtils.RoundFractionalUnits(y, contract.LowTick);
public static IEqualityComparer<double> tick_equality_comparer(this ContractSpecificationImmutable contract)
{
var eq = EqualityComparer.Create((double a, double b) => TickUtils.tick_equals(contract, a, b),
x => MathUtils.RoundFractionalUnits(x, contract.LowTick));
return eq;
}
public static IEqualityComparer<double> tick_equality_comparer(this ContractSpecificationImmutable contract) =>
EqualityComparer.Create((double a, double b) => tick_equals(contract, a, b), x => MathUtils.RoundFractionalUnits(x, contract.LowTick));
public static double round_to_tick(this ContractSpecificationImmutable spec, double value) {
double intpart = (long)value;
@@ -186,46 +123,196 @@ namespace mbts.spark {
}
public static RegionalTickFactors get_regional_ticks(ContractSpecificationImmutable spec) =>
_regional_ticks_cache.GetOrAdd((spec.LowTick, spec.HighTick, spec.PriceBreak), _create_regional_ticks);
TickStore.get(spec);
public static RegionalTickFactors get_regional_ticks(ContractSpecification spec) =>
_regional_ticks_cache.GetOrAdd((spec.LowTick, spec.HighTick, spec.PriceBreak), _create_regional_ticks);
public static RegionalTickFactors get_regional_ticks(SecurityImmutable security) =>
get_regional_ticks(security.spec_or_default());
static RegionalTickFactors _create_regional_ticks((double low_tick, double high_tick, double price_break) def) =>
new RegionalTickFactors(_get_tick_factors(def.low_tick), _get_tick_factors(def.high_tick), def.price_break);
static TickFactors _inflate_to_base(TickFactors tick_factors, double new_base) =>
new TickFactors(new_base, tick_factors.tick_unit * new_base / tick_factors.tick_base);
static TickFactors _get_tick_factors(double tick) {
if (_ticks_cache.TryGetValue(tick, out TickFactors result))
return result;
// try to return the largest known tick that is divisible by this tick
foreach (var tfu in _descending_tick_fractional_units) {
double mod = tick % tfu;
if (MathUtils.epsilon_equals(mod, 0)) {
if (_resolved_unknown_ticks.TryAdd(tick, true))
Logger.warn(bts.log.Subsystem.Ui, $"Resolved unknown tick {tick} to {tfu}, if this tick is expected, talk to John about adding it to the hard-coded defaults ASAP.");
return _ticks_cache[tfu];
}
public static class CompareUtils {
public static IComparer<double> base_comparer =>
Comparer<double>.Create(_compare_base);
public static IComparer<double> unit_comparer =>
Comparer<double>.Create(_compare_unit);
public static IComparer<TickFactors> base_unit_comparer =>
Comparer<TickFactors>.Create(utils.CompareUtils.safe_comparison<TickFactors>(compare_base_unit));
public static int compare_base_unit(TickFactors a, TickFactors b) {
int comp = _compare_base(a.tick_base, b.tick_base);
return comp == 0 ? a.tick_unit.CompareTo(b.tick_unit) : comp;
}
static int _compare_base(double a, double b) {
double reduced_a = MathUtils.factor_out(a, 10);
double reduced_b = MathUtils.factor_out(b, 10);
int comp = reduced_a.CompareTo(reduced_b);
return comp == 0 ? a.CompareTo(b) : comp;
}
static int _compare_unit(double a, double b) =>
_compare_unit(a, b, 0, 0);
static int _compare_unit(double a, double b, int a_mult10, int b_mult10) {
// fractional-coins are a special case, as they may not be an integer to begin with
if (!MathUtils.is_int(a))
return _compare_unit(a * 10, b, ++a_mult10, b_mult10);
if (!MathUtils.is_int(b))
return _compare_unit(a, b * 10, a_mult10, ++b_mult10);
double reduced_a = a_mult10 > 0 ? a : MathUtils.factor_out(a, 10, 5);
double reduced_b = b_mult10 > 0 ? b : MathUtils.factor_out(b, 10, 5);
int comp = reduced_a.CompareTo(reduced_b);
return comp == 0 ? (a * Math.Pow(10, -a_mult10)).CompareTo(b * Math.Pow(10, -b_mult10)) : comp;
}
Logger.assert(false, $"Unable to resolve tick ({tick}): going down because the application cannot run properly with unresolved ticks. " +
$"FOR DEVS: If this tick was not caused by a bug, please talk to John about adding it to the hard-coded defaults. " +
$"If you believe this tick belongs to a synthetic security, and should not be in the defaults, please delete said synthetic from the db " +
$"(note: the synthetic creation dialog has changed to only allow known ticks from now on, so if you get the latest code and delete old synthetics from the db, this will not happen again). " +
$"Direct any further questions to John.");
return null;
}
// Almost the same function as _get_tick_factors, only difference being no assert/log.
// This seemed more practical than trying to factor this behavior out, it's only a few lines of code.
static TickFactors _try_get_tick_factors(double tick) {
if (_ticks_cache.TryGetValue(tick, out TickFactors result))
return result;
// try to return the largest known tick that is divisible by this tick
foreach (var tfu in _descending_tick_fractional_units) {
double mod = tick % tfu;
if (MathUtils.epsilon_equals(mod, 0))
return _ticks_cache[tfu];
static class TickStore {
public static readonly RegionalTickFactors default_rtf = new RegionalTickFactors(new TickFactors(1000, 1), new TickFactors(1000, 1), double.MaxValue);
static readonly ConcurrentDictionary<SpecKey, RegionalTickFactors> _exchange_ticks = new ConcurrentDictionary<SpecKey, RegionalTickFactors>();
public static RegionalTickFactors get(ContractSpecification spec) =>
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
public static RegionalTickFactors get(ContractSpecificationImmutable spec) =>
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
public static RegionalTickFactors get(SecurityImmutable security) =>
security.HasContractSpec ? get(security.ContractSpec) : default_rtf;
public static RegionalTickFactors get(SecurityImmutable? security) =>
security.HasValue ? get(security.Value) : default_rtf;
static RegionalTickFactors _create_rtf(SpecKey key) {
double tick_base = key.denominator;
if (tick_base == 0) {
// indicates tick is a decimal
tick_base = 100;
}
return _create_rtf(key.low_tick, key.high_tick, key.price_break, tick_base, key.display_digits);
}
static RegionalTickFactors _create_rtf(double low_tick, double high_tick, double price_break, double reduced_target_base, uint display_digits_override) {
var low_tf = _try_create_tf(low_tick, reduced_target_base, display_digits_override);
var high_tf = _try_create_tf(high_tick, reduced_target_base, display_digits_override);
if (low_tf == null) {
low_tf = high_tf ?? (high_tf = default_rtf.low_tick);
} else if (high_tf == null)
high_tf = low_tf;
else {
// if low/high tick-bases do not match, inflate the smaller base so they do
// Note: this assumes the tick-bases are related by factors of 10 (i.e. they have all the same prime factors except for a different number of pairs of 5 and 2, e.g. 64 and 640)
if (low_tf.tick_base > high_tf.tick_base) {
high_tf = _inflate_to_base(high_tf, low_tf.tick_base);
} else if (low_tf.tick_base < high_tf.tick_base) {
low_tf = _inflate_to_base(low_tf, high_tf.tick_base);
}
}
return new RegionalTickFactors(low_tf, high_tf, price_break);
}
/// <summary>
/// Creates TickFactors with a base equal to &quot;<paramref name="tick_base"/> * 10^n,&quot; where 'n' is a natural number (including 0).
/// <para>Note: If <paramref name="display_digits_override"/> is non-zero, 'n' will be &quot;<paramref name="display_digits_override"/> - Math.Ceiling(Log10(<paramref name="tick_base"/>))&quot;</para>
/// </summary>
/// <param name="tick_size">The raw tick-size.</param>
/// <param name="tick_base">The raw tick-base.</param>
/// <returns>Well-defined TickFactors</returns>
static TickFactors _try_create_tf(double tick_size, double tick_base, uint display_digits_override) {
var tf = _try_create_tf(tick_size, tick_base);
if (tf != null && display_digits_override > 0) {
int display_digit_diff = (int)display_digits_override - tf.display_digits();
if (display_digit_diff != 0)
tf = _inflate_to_base(tf, tf.tick_base * Math.Pow(10, display_digit_diff));
}
return tf;
}
/// <summary>
/// Creates TickFactors with a base equal to &quot;reduced_target_base * 10^n,&quot; where 'n' is a natural number (including 0).
/// </summary>
/// <param name="tick_size">The raw tick-size.</param>
/// <param name="reduced_target_base">The smallest acceptable tick-base.</param>
/// <returns>Well-defined TickFactors</returns>
static TickFactors _try_create_tf(double tick_size, double reduced_target_base) {
if (!_check_tick_size(tick_size))
return null;
double raw_base = 1 / tick_size;
double raw_base_log10 = Math.Log10(raw_base);
double reduced_target_base_log10 = Math.Log10(reduced_target_base);
double log10_diff = raw_base_log10 - reduced_target_base_log10;
if (MathUtils.is_int(log10_diff))
return raw_base > reduced_target_base ? new TickFactors(raw_base, 1) : new TickFactors(reduced_target_base, Math.Pow(10, -log10_diff));
int count = 0;
double factored_base = log10_diff > 0 ? reduced_target_base * Math.Pow(10, Math.Ceiling(log10_diff)) : reduced_target_base;
do {
if (factored_base % raw_base == 0)
return new TickFactors(factored_base, factored_base / raw_base);
factored_base *= 10;
} while (++count < 10);
return null;
}
static TickFactors _create_tf(double tick_size) {
if (!_check_tick_size(tick_size))
return default_rtf.low_tick;
double matched_unit = double.MinValue;
TickFactors matched_tf = null;
foreach (var kv in _exchange_ticks) {
if (kv.Key.high_tick == tick_size) {
return kv.Value.high_tick;
} else if (kv.Key.low_tick == tick_size) {
return kv.Value.low_tick;
} else if (is_better_match(kv.Key.high_tick)) {
matched_unit = kv.Key.high_tick;
matched_tf = kv.Value.high_tick;
} else if (is_better_match(kv.Key.low_tick)) {
matched_unit = kv.Key.low_tick;
matched_tf = kv.Value.low_tick;
}
}
return matched_tf ?? new TickFactors(1.0 / tick_size, 1);
bool is_better_match(double known_unit) =>
known_unit < tick_size && known_unit > matched_unit && tick_size % known_unit == 0;
}
static bool _check_tick_size(double tick_size) =>
tick_size > 0 && tick_size <= 0.5;
struct SpecKey : IEquatable<SpecKey> {
public readonly uint denominator, display_digits;
public readonly double low_tick, high_tick, price_break;
public SpecKey(ContractSpecification spec)
: this(spec.MainFraction, spec.PriceDisplayFormat, spec.LowTick, spec.HighTick, spec.PriceBreak) { }
public SpecKey(ContractSpecificationImmutable spec)
: this(spec.MainFraction, spec.PriceDisplayFormat, spec.LowTick, spec.HighTick, spec.PriceBreak) { }
public SpecKey(uint denominator, uint display_digits, double low_tick, double high_tick, double price_break) {
this.denominator = denominator;
this.display_digits = display_digits;
this.low_tick = low_tick;
this.high_tick = high_tick;
this.price_break = price_break;
}
public bool Equals(SpecKey other) =>
denominator == other.denominator &&
display_digits == other.display_digits &&
low_tick == other.low_tick &&
high_tick == other.high_tick &&
price_break == other.price_break;
public override bool Equals(object obj) =>
obj is SpecKey sk && Equals(sk);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash((int)denominator, (int)display_digits, low_tick.GetHashCode(), high_tick.GetHashCode(), price_break.GetHashCode());
}
return null;
}
}
}
}
@@ -1,6 +1,5 @@
using bts.api;
using bts.api.orderman;
using bts.spark;
using mbts.ui;
using mbts.ui.shell;
using System;
@@ -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);
}
}
}
+141 -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;
@@ -13,66 +14,105 @@ using static bts.shell.PAndLWindowFactory;
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);
}
// 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),
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 +121,111 @@ 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 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()
};
#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:NumericSignBrushConverter x:Key="PnLBackgroundColorConverter" Opacity="0.33"/>
<convert:DoubleTextConverter x:Key="DoubleWithNaNHiddenConverter" Format="F2" />
</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,87 @@
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="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"
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.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
/>
<DataGridTextColumn x:Name="sell_qty" Header="Sell Qty" Binding="{Binding sell_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.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="start_pnl" Header="Start PnL" IsReadOnly="true" SortMemberPath="start_pnl"
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.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="pnl"/>
<Binding Path="security.ContractSpec"/>
<Binding Path="start_pnl"/>
<Binding Path="security"/>
</MultiBinding>
</DataGridTextColumn.Binding>
<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.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="day_short" Header="Day Short" Binding="{Binding day_short_quantity}" IsReadOnly="true"
ElementStyle="{StaticResource RightAlignedColumn}"
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="day_pnl" Header="Day PnL" IsReadOnly="true" SortMemberPath="day_pnl"
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="day_pnl"/>
<Binding Path="security"/>
</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"
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.Binding>
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
<Binding Path="total_pnl"/>
<Binding Path="security"/>
</MultiBinding>
</DataGridTextColumn.Binding>
<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 +155,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 +173,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 +197,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 +210,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 +223,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,27 @@ 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
_start_of_trading_day_utc = DateTime.Now.Date.Add(start_of_day_ts).AddDays(-1).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),
/* 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 +105,23 @@ 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(ref ulong bbo_time, ref ulong last_trade_time, SecurityMD sec_md, pnl_calc_type pnl_request, ref double driver_price) {
// check with eric as I'm not sure if i was every querying md correctly
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 +129,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 +142,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 +162,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 = TickUtils.mid_tick(sec_md.security.ContractSpec, bid.price, ask.price); 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 +218,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;
}
}
}
+10 -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
}
@@ -12,70 +12,106 @@ namespace mbts.spark {
internal class PnLSummaryRow : ViewModelBase, IEquatable<PnLSummaryRow> {
DateTime _start_of_day;
(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;
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 };
ulong _bbo_ref, _last_ref;
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?) 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);
}
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;
return distinct_cpps.First();
} else {
Logger.error(bts.log.Subsystem.Ui,
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;
}
} 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;
} else {
Logger.error(bts.log.Subsystem.Ui, $"No cash multiplier found for Security {si}, Symbol {si.Symbol}");
}
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)) {
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(ref _bbo_ref, ref _last_ref, 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 {
@@ -140,56 +176,77 @@ namespace mbts.spark {
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;
@@ -213,7 +213,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);
@@ -269,6 +269,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(),
@@ -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;
@@ -74,7 +76,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) {
@@ -319,7 +322,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
@@ -27,7 +27,8 @@ 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;
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon) : base("Order Book", bus, factory, SparkCommands.ShowOrders.icon) {
@@ -107,21 +108,36 @@ 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))
);
_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
};
_vm.selected_order_changed += _vm_selected_order_changed;
}
private bool _passes_recreate_search_criteria(OrderRecordImmutable record) {
return record.status() == OrderStatus.Canceled
&& !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();
}
}
}
@@ -63,5 +63,32 @@ namespace mbts.spark.utilities {
}
public static int RoundFractionalUnits(double value, double fractional_unit) =>
(int)Math.Round(value / fractional_unit);
public static bool is_int(double d) =>
epsilon_equals(d, Math.Round(d));
public static bool is_int(double d, out double with_int_value) {
with_int_value = Math.Round(d);
return epsilon_equals(with_int_value, d);
}
public static double factor_out(double value, params double[] factors) {
foreach (double f in factors)
value = factor_out(value, f, out int _);
return value;
}
public static double factor_out(double value, double factor, out int n_factors_removed) {
n_factors_removed = 0;
if (value == 0)
return value;
double quotient = value / factor;
while (is_int(quotient, out quotient)) {
value = quotient;
++n_factors_removed;
quotient /= factor;
}
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() };
@@ -1,10 +1,8 @@
using bts.api;
using System.Collections.Generic;
namespace mbts.spark
{
static class SecurityDataHelper
{
namespace mbts.spark {
static class SecurityDataHelper {
public static class EqualityComparer {
public static readonly IEqualityComparer<SecurityMD> MDComparer = utils.EqualityComparer.Create<SecurityMD>(Equals, GetHashCode);
public static bool Equals(SecurityMD x, SecurityMD y) => x.security.Id == y.security.Id;
@@ -14,14 +12,35 @@ namespace mbts.spark
}
public static class Defaults {
public static ContractSpecification ContractSpecification => new ContractSpecification { CashPerPoint = 1.0, LowTick = 0.01, HighTick = 0.01, PriceBreak = double.MaxValue, PriceMultiplier = 1.0 };
public static ContractSpecification ContractSpecification =>
new ContractSpecification {
CashPerPoint = 1.0,
LowTick = 0.0001,
HighTick = 0.0001,
PriceBreak = double.MaxValue,
PriceMultiplier = 1.0,
MainFraction = 10000
};
public static readonly ContractSpecificationImmutable ContractSpecificationImmutable = new ContractSpecificationImmutable(ContractSpecification);
public static Security Security => new Security {
Symbol = string.Empty,
Exchange = Venue.Bts,
ContractSpec = ContractSpecification,
};
public static ContractSpecification ContractSpecificationPennies =>
new ContractSpecification {
CashPerPoint = 1.0,
LowTick = 0.01,
HighTick = 0.01,
PriceBreak = double.MaxValue,
PriceMultiplier = 1.0,
MainFraction = 100
};
public static ContractSpecificationImmutable ContractSpecificationImmutablePennies = new ContractSpecificationImmutable(ContractSpecificationPennies);
public static Security Security =>
new Security {
Symbol = string.Empty,
Exchange = Venue.Bts,
ContractSpec = ContractSpecification
};
public static readonly SecurityImmutable SecurityImmutable = new SecurityImmutable(Security);
}
}
}
}
@@ -212,6 +212,15 @@ namespace mbts.ui {
d.SetValue(property, new_value);
}
public static void set_current_if_changed<T>(DependencyObject d, DependencyProperty property, T new_value) =>
set_current_if_changed(d, property, new_value, EqualityComparer<T>.Default);
public static void set_current_if_changed<T>(DependencyObject d, DependencyProperty property, T new_value, IEqualityComparer<T> comparer) {
object curr = d.GetValue(property);
if (!comparer.to_non_generic().Equals(d.GetValue(property), new_value))
d.SetCurrentValue(property, new_value);
}
public static PropertyChangedCallback prop_changed_callback<TValue, TOwner>(PropertyChangedCallback<TValue, TOwner> callback) =>
(d, e) => callback(ObjectHelper.try_convert<TOwner>(d), ObjectHelper.try_convert<TValue>(e.OldValue), ObjectHelper.try_convert<TValue>(e.NewValue));
+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);
}
+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)
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
rm -rf $1
mkdir $1
cp -r $2/files/* $1
+21
View File
@@ -0,0 +1,21 @@
<license version="1.0">
<type>production</type>
<customer>
<name>Blue Trading Systems</name>
</customer>
<period>
<since>2017-04-24</since>
<till>2018-08-29</till>
</period>
<dotNetProducts>
<product id="41" name="FIX Analyser" />
</dotNetProducts>
<dotNetCheckSum>jqN4XONnMeiDSJIAa6CK9m9SitquhMq3MhJW97fG26Fp+GPog9ObMLS2elPvippdZsRWmJGsjTmFxAg8d41Vr+tpNgObi3VT3DtfYjGESTmi+u4vvJ9PNi8tQWQ7HPYBJe1X6j84ijhe5e1jHEcaXZ08ExDbESebn2ndHb4Phv4=</dotNetCheckSum>
<products>
<product id="1" name="OnixS FIX Engine for C++" />
<product id="4" name="CME Market Data Handler for C++" />
<product id="7" name="ICE iMpact Multicast Price Feed Handler for C++" />
<product id="13" name="NYSE ArcaBook Multicast for Equities Handler for C++" />
</products>
<checksum>F5502F3BD9CE954022ABCBF94A7E4052</checksum>
</license>
@@ -0,0 +1,12 @@
Session ID Access ID Secret Key Creation Date Expiration Date Environment
1U7 i9pLY5mVhWLXs7FWNb9X 8esbwiaHaqmR2rXZMkJRHt00fW6l_-ppCGoheWWuBZQ 2018-04-05 2019-04-05 CERT
1U7 ozXmORijPtJriTg6Wwny A1KOwrhH8GojNotjkCfVwHRSi_OS280xihaRZwJLFKo 2018-04-03 2018-05-03 CERT
ANS 6ogfYoF54g695tqaHsFC CtDkFnuDuStB1S05nC_pWvANb6FWsV2IMNKkmM3EcP4 2018-04-05 2019-04-05 NEWRELEASE
KTK IrUp518ekr8SzbfJGdPZ QmilC-cKgqlIiIBs_BV59fowbPfuBWn0QjeEc1dYj38 2018-04-05 2019-04-05 NEWRELEASE
M5Y bUFtyvZgPhZFCYa434ap rfZ2XLLwtHt26szWwMxYRw9uPb7jE4evEWaKuKf2uyU 2018-04-05 2019-04-05 CERT
MBX tbna9JLxT4Jlrzj0wtSS EcYqrLmR4ysSIT_NCBeC2AjDo7IAHsVZ0rOZ0cpAh_o 2018-04-05 2019-04-05 CERT
MC1 0N10hKkE2B2KUt86ewd8 dJ5I52JNA-6aZZ8hnO0byVKcyqA8Of6kkstfIF89AAM 2018-04-05 2019-04-05 CERT
MC5 e4uBmhQ1ebaXAeWHa7HK PNOM86eofsiCwzzKUsB5yAmrNoyZypnYD72pT2A8zgc 2018-04-05 2019-04-05 CERT
MC7 S6KfCnay5UJ67P55ibHd 5lSs1TbX7N0p6Duu63_JDMghPUqBAWacvkM91MUv1uM 2018-04-05 2019-04-05 CERT
MC8 AbEEtBn63WIAuItPDz1c n3ndjYb03Hx6wBO3me35HoONyDgO5TZKWk0tDSaX9KA 2018-04-05 2019-04-05 CERT
T39 UWggBgHWAe07aqW3ps2I tbb3it0rUQSSTa0TC9QcRwkrHEMDLWhaJH4VQ0FqGB4 2018-04-05 2019-04-05 NEWRELEASE
+1 -1
View File
@@ -1 +1 @@
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/ShellDiagnosticService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/testkeys.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPriceLantencyTester.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/windows_test_driver.cpp;utils/Dispatcher.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp)
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cpp_config.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/testkeys.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cpp_config.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPriceLantencyTester.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/Dispatcher.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp)
+33 -28
View File
@@ -1,28 +1,33 @@
cmake_minimum_required(VERSION 3.5)
project(spark_lib)
project(libspark)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
include(${CMAKE_SOURCE_DIR}/tools/Protobuf.cmake)
# DEPENDENCIES
# config protos
file(GLOB_RECURSE Config_Protos RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} config/protos/*.proto)
# service protos
file (GLOB_RECURSE Service_Protos RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} api/services/*.proto)
# non config/non service protos
file(GLOB_RECURSE NonConfig_Protos RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.proto)
list(REMOVE_ITEM NonConfig_Protos ${Service_Protos} ${Config_Protos})
# normal cpps
file(GLOB_RECURSE SparkLib_Sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
# defaults
file(GLOB_RECURSE Conf_Defaults RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} config/defaults/*.conf)
# this is used to communicate to other repositories that the build
# needs to happen again (because a file was added)
if (WIN32)
file(GLOB_RECURSE SparkLib_Sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
api/*.cpp
config/*.cpp
domain_model/*.cpp
expression/*.cpp
logging/*.cpp
md/*.cpp
os/*.cpp
quickbus/*.cpp
sparkproto/*.cpp
testing/*.cpp
utils/*.cpp)
else ()
file(GLOB_RECURSE SparkLib_Sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
endif()
update_deps_file("${SparkLib_Sources};${Config_Protos};${Service_Protos};${NonConfig_Protos};${Conf_Defaults}")
# END DEPENDENCIES
@@ -55,15 +60,15 @@ foreach (cfg_file_path ${Conf_Defaults})
configure_file(${cfg_file_path} ${CMAKE_BINARY_DIR}/src/server/sparkd/defaults/${cfg_file_name})
endforeach(cfg_file_path)
# spark_lib
# lib
if (WIN32)
add_library(spark_lib SHARED
add_library(libspark SHARED
${SparkLib_Sources}
${PROTO_SRCS}
${SERVICE_COMPILER_SRCS}
)
else ()
add_library(spark_lib STATIC
add_library(libspark SHARED
${SparkLib_Sources}
${PROTO_SRCS}
${SERVICE_COMPILER_SRCS}
@@ -71,10 +76,10 @@ else ()
endif()
#include paths
target_include_directories( spark_lib PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src)
target_include_directories( libspark PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src)
if (WIN32)
target_link_libraries( spark_lib
target_link_libraries( libspark
libcurl
libfmt
libtbb
@@ -88,12 +93,8 @@ if (WIN32)
libprotobuf
)
else ()
target_link_libraries( spark_lib
curl
ssl
crypto
fmt
tbb
target_link_libraries( libspark
protobuf
boost_system
boost_thread
boost_filesystem
@@ -101,9 +102,13 @@ else ()
boost_chrono
boost_date_time
boost_atomic
protobuf
uuid.a
sqlite3
curl
uuid
ssl
crypto
fmt
tbb
cryptopp
-lrt
-ldl
@@ -116,4 +121,4 @@ if (WIN32)
include(${CMAKE_SOURCE_DIR}/tools/vs_filters.cmake)
endif()
@@ -53,7 +53,7 @@ internal abstract class {{name}}Base : ApiServer, IDisposable {
}
ApiNativeMethods.StreamUpdate pack({{output_type_full}} item, bool is_change, List<GCHandle> pinned_buffers) {
var data = item.ToByteArray();
var data = Google.Protobuf.MessageExtensions.ToByteArray(item);
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
pinned_buffers.Add(handle);
return new ApiNativeMethods.StreamUpdate() {
@@ -59,18 +59,18 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
{{#each method}}{{#if server_streaming}}
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher(std::string const&qualifier) const {
TableHandle handle = _channel.create_table(std::string("{{name}}/") + qualifier);
bts::api::TableHandle handle = _channel.create_table(std::string("{{name}}/") + qualifier);
return make_{{name}}_publisher(handle);
}
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher(const {{input_type_cpp}}& qualifier) const {
TableHandle handle = _channel.create_table(format_qualifier("{{name}}", qualifier));
bts::api::TableHandle handle = _channel.create_table(format_qualifier("{{name}}", qualifier));
return make_{{name}}_publisher(handle);
}
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher() const {
TableHandle handle = _channel.create_table("{{name}}");
bts::api::TableHandle handle = _channel.create_table("{{name}}");
return make_{{name}}_publisher(handle);
}
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher(TableHandle handle) const {
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher(bts::api::TableHandle handle) const {
{{#unless key_type.natural_key}}
std::unordered_map<{{key_type.cpptype}}, int> _map;
int _next_id;
@@ -97,9 +97,9 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
int total_updates = add_count + change_count + remove_count;
// Optimize small collections
StreamUpdate stack_buffer[256];
std::vector<StreamUpdate> heap_buffer;
StreamUpdate* pupdates = stack_buffer;
bts::api::StreamUpdate stack_buffer[256];
std::vector<bts::api::StreamUpdate> heap_buffer;
bts::api::StreamUpdate* pupdates = stack_buffer;
if (total_updates > 256) {
heap_buffer.resize(total_updates);
pupdates = &heap_buffer[0];
@@ -108,13 +108,13 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
int dest = 0;
if (added) {
for(const auto& item : *added) {
StreamUpdate& update = pupdates[dest++];
update.type = StreamUpdate::CREATE;
bts::api::StreamUpdate& update = pupdates[dest++];
update.type = bts::api::StreamUpdate::CREATE;
if (removed != nullptr) {
auto iter = removed->find({{name}}_key(item));
if (iter != removed->end()) {
removed->erase(iter);
update.type = StreamUpdate::REPLACE;
update.type = bts::api::StreamUpdate::REPLACE;
}
}
{{#if key_type.natural_key}}
@@ -130,8 +130,8 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
}
if (changed) {
for (const auto& item : *changed) {
StreamUpdate& update = pupdates[dest++];
update.type = StreamUpdate::REPLACE;
bts::api::StreamUpdate& update = pupdates[dest++];
update.type = bts::api::StreamUpdate::REPLACE;
{{#if key_type.natural_key}}
update.key = (int)({{name}}_key(item));
{{else}}
@@ -145,8 +145,8 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
}
if (removed) {
for (const auto& key : *removed) {
StreamUpdate& update = pupdates[dest++];
update.type = StreamUpdate::REMOVE;
bts::api::StreamUpdate& update = pupdates[dest++];
update.type = bts::api::StreamUpdate::REMOVE;
{{#if key_type.natural_key}}
update.key = (int)(key);
{{else}}
@@ -61,7 +61,7 @@ protected:
private:
void dispatch(const bts::api::RequestHeader&, google::protobuf::io::ZeroCopyInputStream&, OnResponse) override;
{{#each method}}{{#if server_streaming}}
{{cap name}}Publisher make_{{name}}_publisher(TableHandle) const;
{{cap name}}Publisher make_{{name}}_publisher(bts::api::TableHandle) const;
{{/if}}{{/each}}
};
+269 -38
View File
@@ -1,5 +1,6 @@
#include "lib/api/services/AdminService.server.pb.h"
#include "lib/api/service_helpers/AdminServiceUtils.h"
#include "lib/md/get_symbols.h"
namespace bts{
namespace admin{
@@ -30,6 +31,9 @@ bool AdminServiceUtils::has_exchange(const bts::api::admin::ExchangeConnection&
(conn.cme_connection().has_cgw_connection() || conn.cme_connection().has_msgw_connection());
break;
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_SIM:
rv = conn.has_sim_connection();
break;
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_CBOE:
case api::Venue::VENUE_CBOECFE:
@@ -70,6 +74,20 @@ AdminServiceUtils::resolve_cme_connection(const bts::api::admin::AdminData& data
return nullptr;
}
const bts::api::admin::ExchangeConnection*
AdminServiceUtils::resolve_sim_connection(const bts::api::admin::AdminData& data,
const bts::api::admin::BTSAccount& account,
const bts::api::Security& sec){
bts_assert(sec.exchange() == api::Venue::VENUE_SIM);
if(account.has_connections())
for (const std::string& conn_name : account.connections().exchange_connection_names()){
const bts::api::admin::ExchangeConnection* conn = find_connection(data, conn_name);
if (conn!=nullptr && conn->has_sim_connection())
return conn;
}
return nullptr;
}
const bts::api::admin::ExchangeConnection*
AdminServiceUtils::resolve_connection(const bts::api::admin::AdminData& data,
const bts::api::admin::BTSAccount& account,
@@ -81,6 +99,9 @@ AdminServiceUtils::resolve_connection(const bts::api::admin::AdminData& data,
break;
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_SIM:
rv = resolve_sim_connection(data, account, sec);
break;
case api::Venue::VENUE_CBOE:
case api::Venue::VENUE_CBOECFE:
case api::Venue::VENUE_ICE:
@@ -104,16 +125,26 @@ void AdminServiceUtils::fill_connections_from_config(bts::api::admin::AdminData&
admin_data.clear_connections();
auto &all_connections = config::get<config::JetFIX>().connection();
for (auto const &c : all_connections){
// TODO:(assumes all exchanges are CME)
bts::api::admin::ExchangeConnection connection;
uint32_t new_id = find_max_connection_id(admin_data)+1;
connection.set_id(new_id);
connection.set_name(c.name());
if (c.cme_market_segment_id()==0)
connection.mutable_cme_connection()->mutable_cgw_connection();
else
connection.mutable_cme_connection()->mutable_msgw_connection()->set_market_segment_id(c.cme_market_segment_id());
add_connection(admin_data,connection);
switch (c.platform()) {
case config::JetFIX::Connection::GLBX:
if (c.cme_market_segment_id()==0)
connection.mutable_cme_connection()->mutable_cgw_connection();
else
connection.mutable_cme_connection()->mutable_msgw_connection()->set_market_segment_id(c.cme_market_segment_id());
add_connection(admin_data,connection);
break;
case config::JetFIX::Connection::CBOE:
break;
case config::JetFIX::Connection::SIM:
connection.mutable_sim_connection();
add_connection(admin_data,connection);
break;
}
}
for(int i=0;i<admin_data.accounts_size();i++)
sync_account_connections(admin_data, *admin_data.mutable_accounts(i));
@@ -193,6 +224,19 @@ const bts::api::admin::CMEAccountData* AdminServiceUtils::get_account_cme_data(c
}
return nullptr;
}
const bts::api::admin::SIMAccountData* AdminServiceUtils::get_account_sim_data(const bts::api::admin::BTSAccount& account,
const bts::api::admin::AdminData& data){
if (account.has_exchange_account_data() && account.exchange_account_data().has_sim_account_data())
return &(account.exchange_account_data().sim_account_data());
else if (has_id(account.parent_account_id())){
const bts::api::admin::BTSAccount* parent_account = find_account(data, account.parent_account_id());
if (parent_account != nullptr)
return get_account_sim_data (*parent_account, data);
}
return nullptr;
}
bool AdminServiceUtils::account_has_connection(const bts::api::admin::BTSAccount& account,
const std::string& connection_name){
if(account.has_connections()){
@@ -207,15 +251,21 @@ std::string AdminServiceUtils::find_account_exchange_account (const bts::api::ad
api::Venue exchange,
bts::api::admin::AdminServiceOperationResult& out_result){
out_result.set_is_success(true);
const api::admin::CMEAccountData* cme_account_data;
std::string rv = "";
switch(exchange)
{
case api::Venue::VENUE_GLBX:
cme_account_data = get_account_cme_data(account, data);
case api::Venue::VENUE_GLBX: {
auto cme_account_data = get_account_cme_data(account, data);
if (cme_account_data != nullptr)
rv = cme_account_data->clearing_account();
break;
}
case api::Venue::VENUE_SIM: {
auto sim_account_data = get_account_sim_data(account, data);
if (sim_account_data != nullptr)
rv = sim_account_data->clearing_account();
break;
}
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_CBOE:
@@ -228,6 +278,7 @@ std::string AdminServiceUtils::find_account_exchange_account (const bts::api::ad
}
return rv;
}
std::map<uint32_t,bool> AdminServiceUtils::accounts_map(const bts::api::admin::AdminData& admin_data){
std::map<uint32_t,bool> rv;
auto account_ptrs = accounts(admin_data, false);
@@ -315,7 +366,8 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
out_result.set_error_text("Could not find connection");
return rv;
}
if(sec.exchange()==bts::api::Venue::VENUE_GLBX){
switch (sec.exchange()) {
case bts::api::Venue::VENUE_GLBX: {
const bts::api::admin::CMEUserData* cme_user_data = get_user_cme_data(*user_ptr, data);
if(cme_user_data==nullptr){
out_result.set_is_success(false);
@@ -334,6 +386,11 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
out_result.set_error_text("Operator ID is not set");
return rv;
}
if (cme_user_data->location().length() == 0){
out_result.set_is_success(false);
out_result.set_error_text("Location is not set");
return rv;
}
if (cme_account_data->clearing_account().length() == 0){
out_result.set_is_success(false);
out_result.set_error_text("Clearing Account is not set");
@@ -346,17 +403,77 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
}
rv.set_session(conn_ptr->name());
rv.set_operator_id(cme_user_data->operator_id());
rv.set_location(cme_user_data->location());
rv.set_smp_id(cme_user_data->smp_id());
if (cme_user_data->smp_instruction()== "O")
rv.set_smp_instruction(api::orderman::SelfMatchPreventionInstruction::SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_RESTING);
else if (cme_user_data->smp_instruction()== "N")
rv.set_smp_instruction(api::orderman::SelfMatchPreventionInstruction::SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_AGRESSING);
else
rv.set_smp_instruction(api::orderman::SelfMatchPreventionInstruction::SELF_MATCH_PREVENTION_INSTRUCTION_UNDEFINED);
rv.set_account(cme_account_data->clearing_account());
rv.set_cti_code(cme_account_data->cti());
rv.set_customer_or_firm(cme_account_data->origin());
return rv;
}
else{
out_result.set_is_success(false);
out_result.set_error_text("Only contracts at the CME are supported right now");
return rv;
case api::Venue::VENUE_SIM: {
const bts::api::admin::SIMUserData* sim_user_data = get_user_sim_data(*user_ptr, data);
if(sim_user_data==nullptr){
out_result.set_is_success(false);
out_result.set_error_text("Could not find SIM data for user");
return rv;
}
const bts::api::admin::SIMAccountData* sim_account_data = get_account_sim_data(*account_ptr, data);
if(sim_account_data==nullptr){
out_result.set_is_success(false);
out_result.set_error_text("Could not find SIM data for account");
return rv;
}
// check for data corectness
if (sim_user_data->operator_id().length() == 0){
out_result.set_is_success(false);
out_result.set_error_text("Operator ID is not set");
return rv;
}
if (sim_user_data->location().length() == 0){
out_result.set_is_success(false);
out_result.set_error_text("Location is not set");
return rv;
}
if (sim_account_data->clearing_account().length() == 0){
out_result.set_is_success(false);
out_result.set_error_text("Clearing Account is not set");
return rv;
}
if (sim_account_data->cti()==bts::api::orderman::CTICode::CTI_CODE_UNDEFINED){
out_result.set_is_success(false);
out_result.set_error_text("CTI Code is not set");
return rv;
}
rv.set_session(conn_ptr->name());
rv.set_operator_id(sim_user_data->operator_id());
rv.set_location(sim_user_data->location());
rv.set_account(sim_account_data->clearing_account());
rv.set_cti_code(sim_account_data->cti());
rv.set_customer_or_firm(sim_account_data->origin());
return rv;
}
break;
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_CBOE:
case api::Venue::VENUE_CBOECFE:
case api::Venue::VENUE_ICE:
case api::Venue::Venue_INT_MIN_SENTINEL_DO_NOT_USE_:
case api::Venue::Venue_INT_MAX_SENTINEL_DO_NOT_USE_:
out_result.set_is_success(false);
out_result.set_error_text("Unsupported exchange");
break;
}
return rv;
}
////////////////////////////////////////////
// account mutators ////////////////////////
////////////////////////////////////////////
@@ -493,6 +610,19 @@ const bts::api::admin::CMEUserData* AdminServiceUtils::get_group_cme_data(const
}
return nullptr;
}
const bts::api::admin::SIMUserData* AdminServiceUtils::get_group_sim_data(const bts::api::admin::BTSUserGroup& group,
const bts::api::admin::AdminData& data){
if (group.has_user_data() && group.user_data().has_exchange_user_data() && group.user_data().exchange_user_data().has_sim_data())
return &(group.user_data().exchange_user_data().sim_data());
else if (has_id(group.parent_group_id())){
const bts::api::admin::BTSUserGroup* parent_group = find_group(data, group.parent_group_id());
if (parent_group != nullptr)
return get_group_sim_data(*parent_group, data);
}
return nullptr;
}
const bts::api::admin::UserAccountPermissions* const AdminServiceUtils::get_group_user_permissions (const bts::api::admin::AdminData& admin_data,
uint32_t group_id){
const bts::api::admin::BTSUserGroup* const group = find_group(admin_data, group_id);
@@ -598,6 +728,7 @@ uint32_t AdminServiceUtils::find_max_user_id (const bts::api::admin::AdminData&
rv = user_ptr->id();
return rv;
}
const bts::api::admin::CMEUserData* AdminServiceUtils::get_user_cme_data(const bts::api::admin::BTSUser& user,
const bts::api::admin::AdminData& data){
if (user.has_user_data() && user.user_data().has_exchange_user_data() && user.user_data().exchange_user_data().has_cme_data())
@@ -609,22 +740,41 @@ const bts::api::admin::CMEUserData* AdminServiceUtils::get_user_cme_data(const b
}
return nullptr;
}
const bts::api::admin::SIMUserData* AdminServiceUtils::get_user_sim_data(const bts::api::admin::BTSUser& user,
const bts::api::admin::AdminData& data){
if (user.has_user_data() && user.user_data().has_exchange_user_data() && user.user_data().exchange_user_data().has_sim_data())
return &(user.user_data().exchange_user_data().sim_data());
else{
const bts::api::admin::BTSUserGroup* parent_group = find_group(data, user.parent_group_id());
if (parent_group != nullptr)
return get_group_sim_data(*parent_group, data);
}
return nullptr;
}
// finds the "exchange user" (ex: "operator_id" at CME or "authorized_trader"
// at ICE) for a user
std::string AdminServiceUtils::find_user_exchange_id (const bts::api::admin::AdminData& data,
const bts::api::admin::BTSUser& user,
api::Venue exchange,
bts::api::admin::AdminServiceOperationResult& out_result){
const api::admin::CMEUserData* cme_user_data;
out_result.set_is_success(true);
std::string rv = "";
switch(exchange)
{
case api::Venue::VENUE_GLBX:
cme_user_data = get_user_cme_data(user, data);
case api::Venue::VENUE_GLBX: {
auto cme_user_data = get_user_cme_data(user, data);
if (cme_user_data != nullptr)
rv = cme_user_data->operator_id();
break;
}
case api::Venue::VENUE_SIM: {
auto sim_user_data = get_user_sim_data(user, data);
if (sim_user_data != nullptr)
rv = sim_user_data->operator_id();
break;
}
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_CBOE:
@@ -963,19 +1113,49 @@ api::SecurityType AdminServiceUtils::security_type(const api::Security& security
////////////////////////////////////////////
// boostrapping ////////////////////////////
////////////////////////////////////////////
void add_product_position_limits(bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type)
{
std::vector<md::UserSymbol> symbols;
md::get_system_md_symbols(venue, sec_type,&symbols);
for (auto const &s : symbols) {
auto acct_a_prod_limit = to_account->add_product_position_limits();
acct_a_prod_limit->mutable_product()->set_exchange(s.exchange());
acct_a_prod_limit->mutable_product()->set_security_type(s.type());
acct_a_prod_limit->mutable_product()->set_security_symbol(s.symbol());
acct_a_prod_limit->set_long_position_limit(500000);
acct_a_prod_limit->set_short_position_limit(500000);
}
}
void add_security_clip_sizes_to_account(bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type)
{
std::vector<md::UserSymbol> symbols;
md::get_system_md_symbols(venue, sec_type,&symbols);
for (auto const &s : symbols) {
auto acct_a_clip_size = to_account->mutable_security_clip_limits()->add_limit();
acct_a_clip_size->mutable_security()->set_exchange(s.exchange());
acct_a_clip_size->mutable_security()->set_security_type(s.type());
acct_a_clip_size->mutable_security()->set_security_symbol(s.symbol());
acct_a_clip_size->set_clip_size(50);
}
}
void AdminServiceUtils::bootstrap_data (bts::api::admin::AdminData& data){
data.set_id(1);
bts::api::admin::AdminServiceOperationResult result;
bts::api::admin::AdminServiceCreateAccountResult account_result;
bts::api::admin::AdminServiceCreateEntityResult user_result;
// adding accounts
auto parent_account = create_account(data, 0, std::string("ACC A"), std::string("description"), true, account_result);
auto account_1 = create_account(data, parent_account->id(), std::string("ACC A1"), std::string("description"), true, account_result); account_1->set_is_active(true);
auto account_2 = create_account(data, parent_account->id(), std::string("ACC A2"), std::string("description"), true, account_result); account_2->set_is_active(true);
auto account_3 = create_account(data, parent_account->id(), std::string("ACC A3"), std::string("description"), true, account_result); account_3->set_is_active(true);
create_account(data, parent_account->id(), std::string("ACC A4"), std::string("description"), true, account_result);
auto parent_account = create_account(data, 0, std::string("acc a"), std::string("description"), true, account_result);
auto account_1 = create_account(data, parent_account->id(), std::string("acc a1"), std::string("description"), true, account_result); account_1->set_is_active(true);
auto account_2 = create_account(data, parent_account->id(), std::string("acc a2"), std::string("description"), true, account_result); account_2->set_is_active(true);
auto account_3 = create_account(data, parent_account->id(), std::string("acc a3"), std::string("description"), true, account_result); account_3->set_is_active(true);
create_account(data, parent_account->id(), std::string("acc a4"), std::string("description"), true, account_result);
// add group
auto def_group = create_group(data, 0, std::string("DEFAULT"), result);
auto def_group = create_group(data, 0, std::string("default"), result);
// adding users to the group
bts::api::admin::LicenseData license_data;
bts::api::admin::LicenseEntitlements license_entitlements;
@@ -994,68 +1174,119 @@ void AdminServiceUtils::bootstrap_data (bts::api::admin::AdminData& data){
set_user_accounts(user_1, account_permissions);
set_user_accounts(user_2, account_permissions);
set_user_accounts(user_3, account_permissions);
// add cme account data
// add cme and sim account data
bts::api::admin::ExchangeAccountData exchange_account_data;
exchange_account_data.mutable_cme_account_data()->set_cti(bts::api::orderman::CTICode::CTI_CODE_CTI_CODE1);
exchange_account_data.mutable_cme_account_data()->set_origin(bts::api::orderman::CustomerOrFirm::CUSTOMER_OR_FIRM_FIRM);
exchange_account_data.mutable_cme_account_data()->set_clearing_account("ca_" + parent_account->name());
exchange_account_data.mutable_sim_account_data()->set_cti(bts::api::orderman::CTICode::CTI_CODE_CTI_CODE1);
exchange_account_data.mutable_sim_account_data()->set_origin(bts::api::orderman::CustomerOrFirm::CUSTOMER_OR_FIRM_FIRM);
exchange_account_data.mutable_sim_account_data()->set_clearing_account("ca_" + parent_account->name());
set_account_exchange_data(parent_account, exchange_account_data);
exchange_account_data.mutable_cme_account_data()->set_clearing_account("ca_" + account_1->name());
exchange_account_data.mutable_sim_account_data()->set_clearing_account("ca_" + account_1->name());
set_account_exchange_data(account_1, exchange_account_data);
exchange_account_data.mutable_cme_account_data()->set_clearing_account("ca_" + account_2->name());
exchange_account_data.mutable_sim_account_data()->set_clearing_account("ca_" + account_2->name());
set_account_exchange_data(account_2, exchange_account_data);
exchange_account_data.mutable_cme_account_data()->set_clearing_account("ca_" + account_3->name());
exchange_account_data.mutable_sim_account_data()->set_clearing_account("ca_" + account_3->name());
set_account_exchange_data(account_3, exchange_account_data);
// add cme user data
// add cme and sim user data
bts::api::admin::ExchangeUserData exchange_user_data;
exchange_user_data.mutable_cme_data()->set_operator_id("op_id_" + user_1->name());
exchange_user_data.mutable_cme_data()->set_location("us,nc");
exchange_user_data.mutable_sim_data()->set_operator_id("op_id_" + user_1->name());
exchange_user_data.mutable_sim_data()->set_location("us,nc");
set_user_exchange_data(user_1, exchange_user_data);
exchange_user_data.mutable_cme_data()->set_operator_id("op_id_" + user_2->name());
exchange_user_data.mutable_cme_data()->set_location("us,nc");
exchange_user_data.mutable_sim_data()->set_operator_id("op_id_" + user_2->name());
exchange_user_data.mutable_sim_data()->set_location("us,nc");
set_user_exchange_data(user_2, exchange_user_data);
exchange_user_data.mutable_cme_data()->set_operator_id("op_id_" + user_3->name());
exchange_user_data.mutable_cme_data()->set_location("us,nc");
exchange_user_data.mutable_sim_data()->set_operator_id("op_id_" + user_3->name());
exchange_user_data.mutable_sim_data()->set_location("us,nc");
set_user_exchange_data(user_3, exchange_user_data);
// associate all connections to all accounts
for(int i=0; i<data.connections_size();i++){
std::string conn_name = data.connections(i).name();
for(int j=0; j<data.accounts_size();j++)
data.mutable_accounts(j)->mutable_connections()->add_exchange_connection_names(conn_name);
}
// add prod limit for ACC A
auto acct_a_prod_limit = parent_account->add_product_position_limits();
acct_a_prod_limit->mutable_product()->set_exchange(api::Venue::VENUE_GLBX);
acct_a_prod_limit->mutable_product()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
acct_a_prod_limit->mutable_product()->set_security_symbol("*");
acct_a_prod_limit->set_long_position_limit(500000);
acct_a_prod_limit->set_short_position_limit(500000);
// add prod limit for acc a
add_product_position_limits(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE);
add_product_position_limits(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD);
add_product_position_limits(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE);
add_product_position_limits(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD);
// security clip sizes for ACC A
auto acct_a_clip_size = parent_account->mutable_security_clip_limits()->add_limit();
acct_a_clip_size->mutable_security()->set_exchange(api::Venue::VENUE_GLBX);
acct_a_clip_size->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
acct_a_clip_size->mutable_security()->set_security_symbol("*");
acct_a_clip_size->set_clip_size(50);
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE);
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD);
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE);
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD);
// add sec limit for ACC A1
// cme
auto acct_a1_sec_limit = account_1->add_security_position_limits();
acct_a1_sec_limit->mutable_security()->set_exchange(api::Venue::VENUE_GLBX);
acct_a1_sec_limit->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
acct_a1_sec_limit->mutable_security()->set_security_symbol("*");
acct_a1_sec_limit->set_long_position_limit (10000);
acct_a1_sec_limit->set_short_position_limit (10000);
acct_a1_sec_limit = account_1->add_security_position_limits();
// sim
acct_a1_sec_limit = account_1->add_security_position_limits();
acct_a1_sec_limit->mutable_security()->set_exchange(api::Venue::VENUE_SIM);
acct_a1_sec_limit->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
acct_a1_sec_limit->mutable_security()->set_security_symbol("*");
acct_a1_sec_limit->set_long_position_limit (10000);
acct_a1_sec_limit->set_short_position_limit (10000);
acct_a1_sec_limit = account_1->add_security_position_limits();
// add prod limit for ACC A1
// cme
auto acct_a1_prod_limit = account_1->add_product_position_limits();
acct_a1_prod_limit->mutable_product()->set_exchange(api::Venue::VENUE_GLBX);
acct_a1_prod_limit->mutable_product()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
acct_a1_prod_limit->mutable_product()->set_security_symbol("*");
acct_a1_prod_limit->set_long_position_limit(1000000);
acct_a1_prod_limit->set_short_position_limit(1000000);
// sim
acct_a1_prod_limit = account_1->add_product_position_limits();
acct_a1_prod_limit->mutable_product()->set_exchange(api::Venue::VENUE_SIM);
acct_a1_prod_limit->mutable_product()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
acct_a1_prod_limit->mutable_product()->set_security_symbol("*");
acct_a1_prod_limit->set_long_position_limit(1000000);
acct_a1_prod_limit->set_short_position_limit(1000000);
// security clip sizes for ACC A1
// cme
auto acct_a1_clip_size = account_1->mutable_security_clip_limits()->add_limit();
acct_a1_clip_size->mutable_security()->set_exchange(api::Venue::VENUE_GLBX);
acct_a1_clip_size->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
acct_a1_clip_size->mutable_security()->set_security_symbol("*");
acct_a1_clip_size->set_clip_size(100);
// sim
acct_a1_clip_size = account_1->mutable_security_clip_limits()->add_limit();
acct_a1_clip_size->mutable_security()->set_exchange(api::Venue::VENUE_SIM);
acct_a1_clip_size->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
acct_a1_clip_size->mutable_security()->set_security_symbol("*");
acct_a1_clip_size->set_clip_size(100);
}
bool AdminServiceUtils::has_id (uint32_t id){return id>0;}
bool AdminServiceUtils::is_symbol_set (const std::string& symbol){
return symbol !="*";
}
}}
}}
@@ -32,6 +32,9 @@ public:
static const bts::api::admin::ExchangeConnection* resolve_cme_connection(const bts::api::admin::AdminData& data,
const bts::api::admin::BTSAccount& account,
const bts::api::Security& sec);
static const bts::api::admin::ExchangeConnection* resolve_sim_connection(const bts::api::admin::AdminData& data,
const bts::api::admin::BTSAccount& account,
const bts::api::Security& sec);
static const bts::api::admin::ExchangeConnection* resolve_connection(const bts::api::admin::AdminData& data,
const bts::api::admin::BTSAccount& account,
const bts::api::Security& sec);
@@ -67,6 +70,8 @@ public:
// if needed)
static const bts::api::admin::CMEAccountData* get_account_cme_data(const bts::api::admin::BTSAccount& account,
const bts::api::admin::AdminData& data);
static const bts::api::admin::SIMAccountData* get_account_sim_data(const bts::api::admin::BTSAccount& account,
const bts::api::admin::AdminData& data);
// returns true if an account has a connection
bool static account_has_connection(const bts::api::admin::BTSAccount& account, const std::string& connection_name);
// finds the external/clearing account associated
@@ -132,6 +137,8 @@ public:
// travels up the group tree until it finds a populated cme data
static const bts::api::admin::CMEUserData* get_group_cme_data(const bts::api::admin::BTSUserGroup& group,
const bts::api::admin::AdminData& data);
static const bts::api::admin::SIMUserData* get_group_sim_data(const bts::api::admin::BTSUserGroup& group,
const bts::api::admin::AdminData& data);
static const bts::api::admin::UserAccountPermissions* const get_group_user_permissions (const bts::api::admin::AdminData& admin_data,
uint32_t group_id);
////////////////////////////////////////////
@@ -153,6 +160,8 @@ public:
static uint32_t find_max_user_id (const bts::api::admin::AdminData& data);
static const bts::api::admin::CMEUserData* get_user_cme_data(const bts::api::admin::BTSUser& user,
const bts::api::admin::AdminData& data);
static const bts::api::admin::SIMUserData* get_user_sim_data(const bts::api::admin::BTSUser& user,
const bts::api::admin::AdminData& data);
// finds the "exchange user" (ex: "operator_id" at CME or "authorized_trader"
// at ICE) for a user
static std::string find_user_exchange_id (const bts::api::admin::AdminData& data,
@@ -243,4 +252,4 @@ public:
static bool is_symbol_set (const std::string& symbol);
static void bootstrap_data (bts::api::admin::AdminData& data);
};
}}
}}
+18
View File
@@ -190,11 +190,13 @@ message ExchangeUserMarketDataEntitlementData{
message ExchangeUserData{
CMEUserData cme_data = 10;
ICEUserData ice_data = 20;
SIMUserData sim_data = 30;
}
message ExchangeAccountData{
CMEAccountData cme_account_data = 10;
ICEAccountData ice_account_data = 20;
SIMAccountData sim_account_data = 30;
}
message MarketDataEntitlement{
@@ -207,6 +209,7 @@ message ExchangeConnection{
oneof connection{
CMEConnection cme_connection = 30;
ICEConnection ice_connection = 40;
SIMConnection sim_connection = 50;
}
}
@@ -229,6 +232,9 @@ message CMECGWConnection{
string backup_id = 70;
}
message SIMConnection {
}
message CMEMSGWConnection{
uint32 port = 10;
string session_and_firm_id = 20;
@@ -254,6 +260,12 @@ message CMEAccountData{
map<string, string> misc_tags = 120;
}
message SIMAccountData {
api.orderman.CTICode cti = 10;
api.orderman.CustomerOrFirm origin = 20;
string clearing_account = 40;
}
message CMEUserData{
string operator_id = 10;
string ats_operator_id = 20;
@@ -263,6 +275,12 @@ message CMEUserData{
map<string, string> misc_tags = 60;
string smp_instruction = 70;
string max_order_rate_per_connection = 80;
string location = 90;
}
message SIMUserData {
string operator_id = 10;
string location = 90;
}
message CMEUserMarketDataEntitlementData{
+63
View File
@@ -68,6 +68,7 @@ message CustomOrderDefinition
LeanOrderDefinition lean = 19;
JoinOrderDefinition join = 20;
TopOrderDefinition top = 21;
TriggerOrderDefinition trigger = 22;
}
}
@@ -84,6 +85,7 @@ message TickOrderDefinition {uint32 tick_offset_on_deploy = 1; spark.QuantityMe
message LeanOrderDefinition {spark.QuantityMeasure cross_threshold = 1; uint32 max_chase_ticks = 2; CustomOrderDefinition child = 3; }
message JoinOrderDefinition {uint32 tick_offset_on_deploy = 1; spark.QuantityMeasure join_threshold = 2; bool enable_hedge = 3; CustomOrderDefinition hedge = 4; int32 hedge_ticks = 5; CustomOrderDefinition child = 6;}
message TopOrderDefinition {uint32 watch_level_tick_offset = 1; spark.QuantityMeasure top_threshold = 2; bool enable_hedge = 3; CustomOrderDefinition hedge = 4; int32 hedge_ticks = 5; CustomOrderDefinition child = 6;}
message TriggerOrderDefinition {TriggerCondition trigger_condition=1; TriggerAction trigger_action=2; CustomOrderDefinition child=3;}
message OrderRecord {
uint32 key = 1 [(bts.api.key)=true];
@@ -119,6 +121,7 @@ message OrderState {
LeanOrderState lean = 18;
JoinOrderState join = 19;
TopOrderState top = 20;
TriggerOrderState trigger = 21;
}
}
@@ -133,6 +136,12 @@ message Fill {
double last_shares = 6;
string fill_key = 7 [(bts.api.key)=true];
google.protobuf.Timestamp timestamp = 8;
enum FillKind {
FILL_KIND_STANDARD = 0;
FILL_KIND_LEG = 1;
}
FillKind fill_kind = 10;
}
enum OrderSubType {
@@ -147,6 +156,9 @@ message RoutingRecord {
string operator_id = 3;
CTICode cti_code = 4;
CustomerOrFirm customer_or_firm = 5;
string smp_id = 6 ;
SelfMatchPreventionInstruction smp_instruction = 7;
string location = 8;
}
message NativeOrderState {
@@ -175,6 +187,7 @@ message TickOrderState {}
message LeanOrderState {}
message JoinOrderState {}
message TopOrderState {}
message TriggerOrderState {}
enum OrderStatus {
@@ -236,6 +249,13 @@ enum CTICode {
CTI_CODE_CTI_CODE4 = 52;
}
enum SelfMatchPreventionInstruction {
SELF_MATCH_PREVENTION_INSTRUCTION_UNDEFINED = 0;
SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_RESTING = 79;
SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_AGRESSING = 78;
}
enum NativeOrderStatus
{
NATIVE_ORDER_STATUS_UNACKNOWLEDGED = 0;
@@ -309,3 +329,46 @@ enum OrderError
ORDER_ERROR_OK = 9;
}
// Trigger Condition /////////////////////////////////
//////////////////////////////////////////////////////
enum TriggerType{
STOP = 0;
INVERSE_STOP = 1;
}
enum TriggerPriceType{
BID = 0;
ASK = 1;
LAST_TRADE_PRICE = 2;
}
message TriggerQuantity{
spark.QuantityMeasure quantity_measure = 10;
uint32 threshold_quantity = 20;
}
message AbsolutePriceTrigger{
// we can leave out qty for now
TriggerQuantity trigger_quantity = 10;
}
message TrailingTrigger{
TriggerType trigger_type = 10;
int32 number_of_ticks = 20;
}
message TriggerCondition{
TriggerPriceType price_type = 10;
oneof trigger{
AbsolutePriceTrigger absolute_price_trigger = 20;
TrailingTrigger trailing_trigger = 30;
}
}
// Trigger Action ////////////////////////////////////
//////////////////////////////////////////////////////
message TriggerPayupPrice{
TriggerPriceType price_type = 10;
int32 payup_ticks = 20;
}
message TriggerAction{
uint32 security_id = 10;
oneof orderprice{
TriggerPayupPrice payup = 20;
double price = 30;
}
}
@@ -0,0 +1,21 @@
syntax="proto3";
package bts.metrics;
option csharp_namespace="bts.metrics";
import "lib/api/apioptions.proto";
import "lib/api/apicore.proto";
message Tick {
int32 key = 1 [(bts.api.key)=true];
uint64 timestamp = 2;
}
message Report {
string host_name = 1;
string version = 2;
uint64 timestamp = 10;
}
service ShellLatencyService {
rpc clock(bts.api.VoidMessage) returns (stream Tick);
rpc report(Report) returns (bts.api.VoidMessage);
}
+2 -9
View File
@@ -4,12 +4,12 @@
MarketDataConfig {
value {
use_replay: true
use_replay: false
}
}
PlatformMap {
value {
glbx_platform: "PLATFORM_SIM"
glbx_platform: "PLATFORM_MDP3ONIXS"
}
}
@@ -48,10 +48,3 @@ CMEOptions {
}
}
}
RecordOptions {
rule: "System.platform == 'Windows'"
value {
output_path: "recording"
}
}
+21 -11
View File
@@ -1,22 +1,28 @@
SymbolList {
value {
symbol: "ES"
symbol: "EW"
symbol: "GC"
symbol: "GE"
symbol: "GF"
symbol: "GLB"
symbol: "HE"
symbol: "LE"
symbol: "NQ"
symbol: "CL"
symbol: "RB"
symbol: "HO"
symbol: "BZ"
symbol: "NG"
symbol: "GC"
symbol: "SI"
symbol: "SO"
symbol: "ZB"
symbol: "ZF"
symbol: "ZN"
symbol: "HG"
symbol: "ZW"
symbol: "ZC"
symbol: "ZS"
symbol: "ZM"
symbol: "ZL"
symbol: "ZO"
symbol: "ZR"
symbol: "ZB"
symbol: "ZN"
symbol: "ZF"
symbol: "ZT"
symbol: "UB"
}
}
@@ -26,5 +32,9 @@ Symbols {
future: @SymbolList.symbol
spread: @SymbolList.symbol
}
sim {
future: @SymbolList.symbol
spread: @SymbolList.symbol
}
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+4 -2
View File
@@ -2,7 +2,9 @@ syntax='proto2';
package bts.md;
message MarketDataConfig {
optional bool use_replay = 1 [default=true];
// Allows you to turn off in-proc market data entirely
optional bool use_md = 1 [default = true];
optional bool use_replay = 2 [default=false];
}
message PlatformMap
@@ -114,7 +116,7 @@ message CMEOptions
}
message RecordOptions {
optional string output_path = 2 [default = "/home/bts/md/today"];
// optional string output_path = 2 [default = "/home/bts/md/today"];
optional bool enable_depth = 3 [default = false];
optional bool enable_future_depth = 4 [default = true];
}
+1
View File
@@ -18,4 +18,5 @@ message SecurityExchangeList {
message Symbols {
optional SecurityExchangeList glbx = 10;
optional SecurityExchangeList sim = 20;
}
+6
View File
@@ -0,0 +1,6 @@
syntax='proto2';
package bts.config;
message CppConfig {
map<string, double> cpp_entry = 1;
}
Loaded 100 of 264 files, more files were not shown because too many files have changed in this diff. Show more