Compare commits

..
153 Commits
Author SHA1 Message Date
Pedro Pinto 575195c67a Updated release notes. 2018-11-20 20:51:52 +00:00
Jake Norbut 9c20fe892e Merged in tabs (pull request #106)
Tabs

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-11-20 20:37:09 +00:00
Taha Afzal 4b626a160d Merged in SPARK-1879_risk_check (pull request #104)
SPARK-1879 risk check

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-11-20 19:35:01 +00:00
Pedro Pinto bfef852de7 Merged in trim_account_name_fix (pull request #105)
Trimmed account name from fill record.
2018-11-20 19:16:57 +00:00
Jake Norbut dbb859cce9 Merge branch 'master' of bitbucket.org:bluetradesys/spark into tabs 2018-11-20 13:05:51 -06:00
Michael Harding abb451349c Merged in perf_trace_unique_sessions (pull request #108)
Fix unique sessions filter
2018-11-20 18:12:25 +00:00
Michael Harding e77ecaa48c Fix unique sessions filter 2018-11-20 13:08:51 -05:00
Eric Harding 1895a75bc7 Merged in journal_mode_off_is_stupid (pull request #107)
Switch journal mode to WAL instead of OFF because off is stupid
2018-11-19 22:19:36 +00:00
Eric Harding f9357d0860 Switch journal mode to WAL instead of OFF because off is stupid 2018-11-19 17:05:37 -05:00
Jake Norbut f7bd209efa Deleting the right 0-sized file this time; still no idea how this was created 2018-11-19 14:41:27 -06:00
Jake Norbut f794ffdace Git thought this file was 0 sized but it's not; i'm confused 2018-11-19 14:27:44 -06:00
Jake Norbut 504323d5b7 I don't know how this empty file was added 2018-11-19 14:18:58 -06:00
Jake Norbut 9450a952d4 CMakeDeps.cmake, of course 2018-11-19 13:55:12 -06:00
Jake Norbut 62394d0e71 Merge branch 'master' of bitbucket.org:bluetradesys/spark into tabs 2018-11-19 13:51:38 -06:00
Jake Norbut 8d9697d06d SPARK-1804 2018-11-19 13:46:05 -06:00
Taha Afzal d9e967610e static casting uint to int before passing it to a function that takes in ints 2018-11-19 10:44:03 -05:00
Pedro Pinto 2cfbd655cb Trimmed account name from fill record.
At some point in the past this was saved with spaces to the side of
it, and that messes up matching with the account names. The implicit
assumption is that we will never want account names with spaces at the
end.
2018-11-19 15:43:44 +00:00
Taha Afzal 3ed5e5275e Merge branch 'master' into SPARK-1879_risk_check 2018-11-19 10:22:45 -05:00
Pedro Pinto d997aeca1e Merged in QBConnection_fix (pull request #103)
Fixed bug in quickbus handling of broken connections.

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-11-19 14:12:04 +00:00
Pedro Pinto 3b371f3588 Fixed bug in quickbus handling of broken connections. 2018-11-19 13:54:48 +00:00
Pedro Pinto b68aaa0a00 Merged 2018-11-16 11:50:18 -05:00
Pedro Pinto 36e58378e4 Made DynamicIcebergOrder only available in dev. 2018-11-16 16:40:13 +00:00
Pedro Pinto 0ef2378dc9 Updated release notes. 2018-11-16 16:40:01 +00:00
Michael Harding d1d4a1bfb7 Update release notes 2018-11-16 11:38:55 -05:00
Pedro Pinto e45ee1730e Merged SPARK-1799-add-hedge-perf-traces into master 2018-11-16 11:00:11 -05:00
Pedro Pinto 6bd3c6b0ca Merge branch 'SPARK-1799-add-hedge-perf-traces' of bitbucket.org:bluetradesys/spark into SPARK-1799-add-hedge-perf-traces 2018-11-16 15:56:02 +00:00
Michael Harding 6f65a958ae Only show total for requotes in perf_trace UI 2018-11-16 10:25:25 -05:00
Pedro Pinto 53a4ab4974 Merge branch 'SPARK-1799-add-hedge-perf-traces' of bitbucket.org:bluetradesys/spark into SPARK-1799-add-hedge-perf-traces 2018-11-16 14:23:25 +00:00
Taha Afzal 6e4ed3f328 for the first time at start up, net pos is enforced 2018-11-15 17:25:10 -05:00
Taha Afzal 4a292c8030 limit enforcing logic added 2018-11-15 17:25:10 -05:00
Michael Harding 29fbfd4479 SPARK-1877: product not security 2018-11-15 16:35:14 -05:00
Michael Harding 500e5d508a SPARK-1879: riskLimitEnforcement radio buttons 2018-11-15 14:51:09 -05:00
Taha Afzal 396ac023c4 adding field to proto 2018-11-15 13:33:41 -05:00
Michael Harding e32f40caee Fix session handling in perf_trace ui 2018-11-15 10:34:56 -05:00
Pedro Pinto 47e7d69897 Merge branch 'SPARK-1799-add-hedge-perf-traces' of bitbucket.org:bluetradesys/spark into SPARK-1799-add-hedge-perf-traces 2018-11-14 22:06:54 +00:00
Pedro Pinto 4f830cdd40 Removed obsolete stats. 2018-11-14 22:06:39 +00:00
Michael Harding 00e9de3b1e Hedges only shows total 2018-11-14 17:03:31 -05:00
Pedro Pinto ddb4e55d13 Changed public to protected member access 2018-11-14 21:36:31 +00:00
Pedro Pinto c2dfcbe343 Made sql store a bit more tolerant to errors. 2018-11-14 20:56:41 +00:00
Pedro Pinto 582fffb1f4 Merged master 2018-11-14 11:02:56 -05:00
Pedro Pinto e67756d055 Merge branch 'prod' 2018-11-14 15:58:58 +00:00
Pedro Pinto 141e1a4ea0 Fixed build. 2018-11-13 22:41:54 +00:00
Pedro Pinto 097db2f340 Merged master 2018-11-13 17:17:18 -05:00
Pedro Pinto b49c764c54 Sped up hedging. 2018-11-13 22:15:30 +00:00
Eric Harding 87218962e1 Update md publisher SDK sample - FixedSpreadSecurity 2018-11-13 15:47:39 -05:00
Eric Harding 88d8248f63 Merged in SPARK-1798-md-benchmark (pull request #101)
SPARK-1798 md benchmark
2018-11-13 20:30:14 +00:00
Eric Harding ffbda10200 Fix capture of transient 'this' pointer 2018-11-13 14:14:42 -05:00
Eric Harding 7eb8b21cea Simplify SecurityIDRecorder api for 3rd party consumption 2018-11-12 16:58:06 -05:00
Michael Harding 986a791801 Update Release Notes 2018-11-12 14:29:35 -05:00
Eric Harding c834a6e840 Switch tbb::queueing_mutex to std::mutex 2018-11-12 13:57:23 -05:00
Michael Harding f14c36223f Merged in SPARK-1703-algo-tooltips (pull request #99)
SPARK-1703: Add tool-tips for lean, join, cover, top

Approved-by: Pedro Pinto <pedro.pinto+atlassian@bluetradesys.com>
2018-11-12 14:32:37 +00:00
Taha Afzal d66c8e0ad8 Added a line to Conditional Quoting help tooltips 2018-11-10 17:34:55 -05:00
Taha Afzal 8673f366d7 conditional quotes are not pulled until after 5 secs of the condition 2018-11-09 18:02:58 -05:00
Michael Harding 88e48ebcf7 SPARK-1703: Add tool-tips for lean, join, cover, top 2018-11-09 17:31:03 -05:00
Eric Harding 1a6a10a834 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-1798-md-benchmark 2018-11-09 13:09:58 -05:00
Eric Harding d780e2aab2 SPARK-1798 sqlite import does not want spaces after comma in csv 2018-11-09 12:29:15 -05:00
Eric Harding a4bc34e633 SPARK-1798 Add CSV output option (default) 2018-11-09 12:15:19 -05:00
Kevin Darby d9b37efbbd wrong enum 2018-11-09 10:42:51 -05:00
Taha Afzal d23b7942b6 removing the hard coded n_ticks used for testing 2018-11-08 19:53:25 -05:00
Kevin Darby f41c3a5334 ConditionalQuoteEnabledModel.cs edited online with Bitbucket 2018-11-09 00:08:29 +00:00
Kevin Darby d8e8f90131 something is smishing this UI, but I've asked John for help 2018-11-08 18:32:36 -05:00
Taha Afzal 0358afccf9 simplified the n ticks away logic 2018-11-08 17:49:05 -05:00
Michael Harding 9f58327d0a Merged in new-order-cancel (pull request #97)
SPARK-1678 Order Cancel in the Spark Admin

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2018-11-08 22:16:32 +00:00
Taha Afzal d3ed6e21e4 integrated with SingleLegquoter._compute_price_quantity 2018-11-08 14:45:28 -05:00
Eric Harding de83f19388 Adjust column widths 2018-11-08 13:33:23 -05:00
Eric Harding 4d993f2ef7 SPARK-1798 - Tool to benchmark market data by dumping to stdout
periodically
2018-11-08 13:19:05 -05:00
Michael Harding ca81be3865 Add manual test for canceling orders in the Spark Admin 2018-11-08 13:17:08 -05:00
Michael Harding 19ae05cb7d SPARK-1678: loading spinner (cherry picked) 2018-11-08 12:14:14 -05:00
Michael Harding 79ec41556e SPARK-1678: Refresh orders after one second (cherry picked) 2018-11-08 12:12:44 -05:00
Michael Harding 088377582d SPARK-1678: Small code tweaks based on Eric's suggestions 2018-11-08 12:02:13 -05:00
Michael Harding c8b3973b8f Remove Beta from SparkAdmin 2018-11-08 12:00:15 -05:00
Kevin Darby 82a2ba4a86 Merged in fix_algo_tests (pull request #96)
remove out of date test
2018-11-08 16:15:55 +00:00
Kevin Darby ccbe20eb57 Merge branch 'master' of bitbucket.org:bluetradesys/spark into fix_algo_tests 2018-11-08 11:07:11 -05:00
Kevin Darby 70a2feb92a remove out of date test 2018-11-08 11:06:41 -05:00
Pedro Pinto a4d542067a Benchmarked order state copy. Its slow. 2018-11-08 15:01:46 +00:00
Pedro Pinto efd222133e Better perf tools. 2018-11-08 15:01:36 +00:00
Pedro Pinto 361491ebd2 Removed obsolete test. 2018-11-08 15:01:23 +00:00
Eric Harding 6aeba4f599 Remove unused MarketData config field 2018-11-07 15:59:07 -05:00
Pedro Pinto b35dae9fab Improved logging of hedge trace. 2018-11-07 20:00:41 +00:00
Taha Afzal 9d96fcaa45 added proto 2018-11-07 01:52:23 -05:00
Jake Norbut 523f02b4c2 Merged in SPARK-1864 (pull request #95)
SOD tm_isdst bug regarding our defaults; new defaults

Approved-by: John Colvin <john.colvin@bluetradesys.com>
Approved-by: Taha Afzal <taha.afzal@bluetradesys.com>
2018-11-06 22:19:05 +00:00
Jake Norbut edb4ed02c9 Merged in xofy (pull request #94)
SPARK-1869 - x of y for all

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-06 21:01:20 +00:00
Jake Norbut 96da8e3276 SOD tm_isdst bug regarding our defaults; new defaults 2018-11-06 14:46:40 -06:00
John Colvin c056bfcf7a Merged in SPARK-1560 (pull request #93)
SPARK-1560 Synthetic Management browser maintain order

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-06 20:43:54 +00:00
John Colvin 2ddf1a1762 Merged in SPARK-1870 (pull request #91)
SPARK-1870

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-06 20:40:26 +00:00
Jake Norbut 8a9ddfded7 SPARK-1869 - x of y everywhere 2018-11-06 13:59:21 -06:00
John Colvin a83f97e1ef SPARK-1560 Synthetic Management browser maintain order 2018-11-06 13:54:25 -06:00
Michael Harding 3630bf5b9a Test updates 2018-11-06 14:51:49 -05:00
Pedro Pinto b9d3a4d04c Fixed typos in SocketConnection perf log. 2018-11-06 17:14:05 +00:00
Eric Harding c7fb636e49 Factor out MD argument parsing 2018-11-06 10:52:18 -05:00
Kevin Darby c7ea6c4ca4 Merged in fix_algo_tests (pull request #92)
clarified  algo tests for new functionality
2018-11-06 15:42:51 +00:00
Kevin Darby 1ffda9c413 clarified algo tests for new functionality 2018-11-06 10:39:55 -05:00
Pedro Pinto e2f314d007 Added optional pause between orders to iceberg. 2018-11-06 13:43:37 +00:00
Pedro Pinto 80d8307822 Cleaned up docs build. 2018-11-06 13:41:45 +00:00
John Colvin 1ca5ed4520 fix typeo in comment 2018-11-05 17:57:43 -06:00
John Colvin 8115f9df74 remove launchSettings.json (added by accident) 2018-11-05 17:52:23 -06:00
John Colvin 306cbaa837 SPARK-1870 / SPARK-1871 if an order's security arrives late, the security cell view doesn't automatically refresh 2018-11-05 17:48:40 -06:00
John Colvin efa31059d9 SPARK-1870 Orderbook - if an order's security arrives after the order, it is always displayed as 'unknown-security' 2018-11-05 17:12:27 -06:00
John Colvin 73089634f9 Merged in SPARK-1805 (pull request #90)
SPARK-1805 Removing a synthetic disables the remove menu for the life of the window

Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-05 18:44:08 +00:00
John Colvin a84e5e3460 SPARK-1805 Removing a synthetic disables the remove menu for the life of the window 2018-11-05 11:26:35 -06:00
Pedro Pinto eca11d0317 Merge branch 'master' into SPARK-1799-add-hedge-perf-traces 2018-11-05 15:18:18 +00:00
Pedro Pinto ced485856e Added some experimental perf related code. 2018-11-05 14:24:00 +00:00
Pedro Pinto 6c0cc76773 Significantly improved engine performance when updating multiple orders. 2018-11-04 22:14:14 +00:00
John Colvin 4d53bfb3f0 Merged in SPARK-1863 (pull request #89)
SPARK-1863

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
Approved-by: John Colvin <john.colvin@bluetradesys.com>
2018-11-04 19:19:47 +00:00
John Colvin 717391afb3 SPARK-1863 Fills Time Filter 2018-11-02 14:26:34 -05:00
John Colvin 38e0d40364 DateTimePicker - time textbox behavior should commit text on 'Enter' (i.e. update binding source) 2018-11-02 14:06:02 -05:00
John Colvin 7053c2ee03 DateTimePicker - internal view-Controller should update TimeTuple prop with SetCurrentValue 2018-11-02 14:02:23 -05:00
John Colvin cdde730fc5 TimeFilterControl - hyperlink commands should update props with SetCurrentValue (i.e. don't break bindings) 2018-11-02 13:59:27 -05:00
John Colvin 95b0119d22 DateTimePicker - make time-text input regex more flexible 2018-11-02 13:54:26 -05:00
John Colvin 07a965a3e0 DateTimePicker - remove 'delay' from text-input bindings 2018-11-02 13:52:36 -05:00
John Colvin eba153e289 DateTimePicker.xaml - change bad staticresource name (had '2' in it!) 2018-11-02 11:06:31 -05:00
John Colvin fc5238c56f move CompareUtils into bts.utils namespace 2018-11-02 10:51:17 -05:00
John Colvin bc367b9f56 rename ObjectHelper -> ConvertUtils 2018-11-02 10:44:21 -05:00
Eric Harding 8adcc98ff1 Merge branch 'master' of bitbucket.org:bluetradesys/spark into SPARK-1798-md-benchmark 2018-11-02 11:24:52 -04:00
Eric Harding ba07041c26 Avoid PriceSize_from_double in example code. (poor performance) 2018-11-02 11:17:48 -04:00
Eric Harding d3101b1cd8 Add PLATFORM_MDP3EMUMBA 2018-11-02 11:17:24 -04:00
Eric Harding 3a9a86020b Fix broken link in manual 2018-11-02 11:15:27 -04:00
Pedro Pinto 42afdc2994 Added a bit more ordermanager perf instrumentation. 2018-11-02 14:42:55 +00:00
John Colvin 6c93208e11 update SortedTreeTests 2018-11-01 17:48:33 -05:00
Eric Harding bf1ca4e5fa Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-11-01 16:55:04 -04:00
John Colvin 3f9e64016b Merge remote-tracking branch 'origin/orderbook-performance' 2018-11-01 15:09:43 -05:00
John Colvin c672967caf OrderBookView.xaml - fix status bar bindings 2018-11-01 14:59:26 -05:00
John Colvin 1e0dac0fbc SortedTree - include orphans in the main _tree as root-nodes 2018-11-01 13:40:56 -05:00
John Colvin aec8de54bd move some utils from spark -> spark_core 2018-11-01 13:40:56 -05:00
Jake Norbut a960093cf6 Workspaces animate fix - Becuase this is the fix Worksapce ovwerite animation deserves, but not the one it needs right now. 2018-11-01 12:49:05 -05:00
Jake Norbut 4601e49106 Workspaces - fix highlighting around shared vs nonshared workspaces 2018-11-01 12:49:05 -05:00
Jake Norbut 7e65db38cf Fixing log message typo; i before e, except after c, etc 2018-11-01 12:49:05 -05:00
Eric Harding d70bd63659 sparkd md dumpsecurity should dump synthetic metadata 2018-11-01 13:26:29 -04:00
Eric Harding 246f9240e7 Tweak md dump tool for easier human consumption 2018-11-01 11:53:47 -04:00
Michael Harding a8ebb9cdcb Manual test updates 2018-11-01 11:41:00 -04:00
Michael Harding bdb2d6cf9e SPARK-1862 2018-11-01 11:41:00 -04:00
Eric Harding 1966c8e0c6 always bypass entitlement take 2 2018-10-31 15:49:37 -04:00
Jake Norbut a418f47d1c Updating Sounds test 2018-10-31 14:36:57 -05:00
Jake Norbut df516fbd2d ICMP ping - increasing default logging threshold 2018-10-31 13:02:22 -05:00
John Colvin b8e298c524 Revert "OrderBookColumnCollection - add 'finally' block to try/catch in InsertItem()"
This reverts commit 4ddb8fe9c5.
2018-10-31 11:10:18 -05:00
Michael Harding 3233187fd9 Improve perf trace ui handling of incomplete data 2018-10-31 10:26:16 -04:00
Jake Norbut 02d1badf0d Adding vertical fudge factor so that no scrollbar is initally present on the SparkCommands; nice catch Eric 2018-10-30 16:17:08 -05:00
Michael Harding b69958007d Fixing typo that any other language would have called an error 2018-10-30 13:22:20 -04:00
Michael Harding f433a3163b Fix rows layout 2018-10-30 12:39:35 -04:00
Pedro Pinto bb82590e5e Added sim execution for globex securities, this should facilitate production testing 2018-10-29 20:17:04 +00:00
Pedro Pinto 1855fe3045 Merge branch 'prod' into SPARK-1799-add-hedge-perf-traces 2018-10-29 18:21:46 +00:00
Pedro Pinto e3dba306a7 Merge branch 'SPARK-1799-add-hedge-perf-traces' of bitbucket.org:bluetradesys/spark into SPARK-1799-add-hedge-perf-traces 2018-10-29 17:58:21 +00:00
Pedro Pinto 74f79a2f52 Added a few utilities for perf testing. 2018-10-29 17:56:44 +00:00
Michael Harding 2c384af157 SPARK-1800: Add Hedge Traces 2018-10-26 10:15:43 -04:00
Eric Harding 99fd77c1f1 Add emumba platform 2018-10-25 13:01:38 -04:00
Pedro Pinto 7b0e3edfcf Merged 2018-10-24 15:12:43 -04:00
Pedro Pinto 99b71ad2aa Merge branch 'grid-settings' into SPARK-1799-add-hedge-perf-traces 2018-10-24 08:51:24 -04:00
Pedro Pinto 58c5eeb6d1 Merge branch 'grid-settings' into SPARK-1799-add-hedge-perf-traces 2018-10-23 11:15:26 -04:00
Pedro Pinto cbb638ff7a More perf logging. 2018-10-20 18:26:54 +00:00
Pedro Pinto 9cd2518853 Added do_work loop data to perf trace. 2018-10-20 16:40:14 +00:00
Pedro Pinto 2db8d717d4 Added logging of mercury events to uncover performance problems. 2018-10-20 16:36:21 +00:00
Pedro Pinto df56c85e7b Added C string logging for fast logging of string literals. 2018-10-20 16:34:03 +00:00
Pedro Pinto fb12d429cb Added hedge traces to perf stats. 2018-10-19 20:52:53 +00:00
202 changed files with 3769 additions and 1374 deletions

No files matched your search

+2 -1
View File
@@ -209,7 +209,8 @@
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/ninja_debug/src/server/sparkd/sparkd",
"args": ["test","Co2RelayTests"],
// "args": ["test","Co2RelayTests"],
"args": ["md", "run", "10"],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/ninja_debug/src/server/sparkd/",
"environment": [],
+1
View File
@@ -0,0 +1 @@
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;/home/ppinto/Dropbox/bts/spark/doc/user/mkdocs.yml;bts-help-theme/404.html;bts-help-theme/base.html;bts-help-theme/content.html;bts-help-theme/css/base.css;bts-help-theme/css/bts.css;bts-help-theme/css/font-awesome.min.css;bts-help-theme/css/highlight.css;bts-help-theme/css/local.css;bts-help-theme/fonts/fontawesome-webfont.eot;bts-help-theme/fonts/glyphicons-halflings-regular.eot;bts-help-theme/img/favicon.ico;bts-help-theme/js/base.js;bts-help-theme/js/bootstrap-3.0.3.min.js;bts-help-theme/js/highlight.pack.js;bts-help-theme/js/jquery-1.10.2.min.js;bts-help-theme/keyboard-modal.html;bts-help-theme/main.html;bts-help-theme/nav-sub.html;bts-help-theme/nav.html;bts-help-theme/search-modal.html;bts-help-theme/toc.html;docs/Help.md;docs/Launcher.md;docs/Logging-In-to-the-System.md;docs/Market.md;docs/My-Windows.md;docs/Settings.md;docs/Spreader.md;docs/Trading.md;docs/images/image1.png;docs/images/image10.png;docs/images/image11.png;docs/images/image12.png;docs/images/image13.png;docs/images/image14.png;docs/images/image15.png;docs/images/image16.png;docs/images/image17.png;docs/images/image18.png;docs/images/image19.png;docs/images/image2.png;docs/images/image20.png;docs/images/image21.png;docs/images/image22.png;docs/images/image23.png;docs/images/image24.png;docs/images/image25.png;docs/images/image26.png;docs/images/image27.png;docs/images/image28.png;docs/images/image29.png;docs/images/image3.png;docs/images/image30.png;docs/images/image31.png;docs/images/image32.png;docs/images/image33.png;docs/images/image34.png;docs/images/image35.png;docs/images/image36.png;docs/images/image37.png;docs/images/image38.png;docs/images/image39.png;docs/images/image4.png;docs/images/image40.png;docs/images/image41.png;docs/images/image42.png;docs/images/image43.png;docs/images/image44.png;docs/images/image45.png;docs/images/image46.png;docs/images/image47.png;docs/images/image48.png;docs/images/image49.png;docs/images/image5.png;docs/images/image50.png;docs/images/image51.png;docs/images/image52.png;docs/images/image53.png;docs/images/image54.png;docs/images/image55.png;docs/images/image56.png;docs/images/image57.png;docs/images/image58.png;docs/images/image59.png;docs/images/image6.png;docs/images/image60.png;docs/images/image61.png;docs/images/image62.png;docs/images/image63.png;docs/images/image64.png;docs/images/image65.png;docs/images/image66.png;docs/images/image67.png;docs/images/image68.png;docs/images/image69.png;docs/images/image7.png;docs/images/image70.png;docs/images/image71.png;docs/images/image72.png;docs/images/image73.png;docs/images/image74.png;docs/images/image76.png;docs/images/image8.png;docs/images/image9.png;docs/index.md;docs/release_notes.md)
+1
View File
@@ -16,6 +16,7 @@ set(DOC_GLOBS
favicon.ico
)
file (GLOB_RECURSE All_Deps RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${DOC_GLOBS})
set (All_Deps ${All_Deps} ${CMAKE_CURRENT_SOURCE_DIR}/build.sh ${CMAKE_CURRENT_SOURCE_DIR}/mkdocs.yml)
update_deps_file("${All_Deps}")
# END DEPENDENCIES
+10 -1
View File
@@ -1,2 +1,11 @@
#!/bin/bash
python -m mkdocs build -d $1
tmpfile=$(mktemp /tmp/quiet_build_log.XXXX)
python -m mkdocs build -d $1 >$tmpfile 2>&1
if [ $? -eq 0 ]
then
rm $tmpfile
exit 0
else
cat $tmpfile
exit 1
fi
+1 -1
View File
@@ -2,7 +2,7 @@
The shortcut icon will appear on your desktop like below:
![](images/image64.png =45)
![](images/image64.png)
Simply double click or single click and hit enter to launch the
login screen:
+10 -17
View File
@@ -54,22 +54,22 @@
![](images/image4.png)
- ![](images/image48.png)][Fills -
- ![](images/image48.png)Fills -
clicking on this will launch a fills window
- ![](images/image57.png)][ Modify -
- ![](images/image57.png) Modify -
clicking on this will launch an order ticket for a live order
highlighted in the Order Book.
- ![](images/image73.png)][Cancel
- ![](images/image73.png)Cancel
selected - clicking on this will cancel the selected orders you have
highlighted in the order book.  You can choose more than one by
holding hold the Ctrl key when click on each row of orders
- ![](images/image74.png)][Cancel
- ![](images/image74.png)Cancel
buys - clicking on this will cancel all the buy orders you have in
the order book
- ![](images/image33.png)][Cancel
- ![](images/image33.png)Cancel
sells - clicking on this will cancel all the sell orders you have in
the order book
- ![](images/image2.png)][Cancel
- ![](images/image2.png)Cancel
all - clicking on this will cancel all working orders you have the
order book
@@ -168,10 +168,10 @@
- Iceberg
- This is a buy or sell order where the order is divided into a
visible or “show” portion which is seen at the exchange.  ][The
visible or “show” portion which is seen at the exchange.  The
rest of the order is hidden and will remain hidden until the show
quantity is filled, essentially reloading the order back to the show
quantity.  You will need to ][define what type of order that will
quantity.  You will need to define what type of order that will
be used for the child order to be sent.
![](images/image47.png)
@@ -198,7 +198,7 @@
         
![](images/image50.png)
- Tick
- Lean
- This is a specialized algo order which allows the user to
essentially watch the opposite-inside market size and cross when
@@ -207,16 +207,9 @@
buying into the asks.  This is often referred to as “paying up” as
well
![](images/image22.png)
![](images/image22.png?v2)
- Lean
- This is a specialized algo order which allows the user to enter an
order which will watch the opposite-inside market size and cross
when that size goes below the threshold defined in the
parameters
![](images/image45.png)
- Join
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+33 -1
View File
@@ -1,5 +1,37 @@
# Release Notes
## 1.4.1
### Tabs
- Tabs now shrink and expand to make better use of horizontal space
### Bug Fixes
- Fixed a bug that could cause excessive CPU usage when a connection broke
- Fixed a layout bug in hedging/requoting performance reports
- Fixed account name mismatching bug for old fills
### Absolute Position limits
- Positions can now be netted or absolute for purposes of limit enforcing
## 1.4.0
### Speed Enhancements
- Improved requoting and hedging performance
- Improved order book form performance
### Server Side SDK
- Added beta version of the server side SDK
### Front End
- Added status bars to all UI grids
### Reporting
- Added reporting of hedging and requoting performance
## 1.3.8
### Speed Enhancements
- Further speed enhancements
### Conditional (new quoting parameter)
- Reduces re-quoting updates by only quoting if N ticks away from the opposite inside market.
- Quotes are cancelled when it gets further away.
- "Price Type" dictates if this logic applies to the quoting market or to the synthetic spread market.
## 1.3.7
### Net Change pricing
@@ -130,4 +162,4 @@
- PnL report now has PnL columns calculated based on your start of day, current day, and total position
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
- Corrected cash per point values, used in pnl calculations, for a number of products
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
@@ -62,7 +62,7 @@ namespace bts.perf {
if (response.Status == IPStatus.Success) {
_current_missed_ping_count = 0;
if (response.RoundtripTime > config.logging_milliseconds_threshold) {
Log.Ui.warn($"Ping response not recieved in a timely manner:\n" +
Log.Ui.warn($"Ping response not received in a timely manner:\n" +
$"\tAddress:\t{response.Address}\n" +
$"\tRoundTrip:\t{response.RoundtripTime} ms");
}
@@ -48,14 +48,12 @@
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Securities: "/>
<TextBlock Grid.Column="1" Text="{Binding ElementName=_grid, Path=Rows.Count}"/>
</Grid>
<TextBlock>
<Run Text="Securities:"/>
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
@@ -58,7 +58,7 @@ namespace mbts.spark {
var update_securities_cmd = SparkCommands.change_securities_callback(_window_type_display_name);
_commands = new List<SparkCommand> {
SparkCommands.ShowLadders.complete(_vm.selected_security_mds_observable(), smds => smds.Count > 0),
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity.complete(_vm.current_security_md, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills.complete(_vm.selected_securities_observable(), _ => true,
secs => new FillsWindowFactory.StartupData(new FillsWindowFactory.FilterStates {
security = secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria
@@ -49,13 +49,7 @@
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text=" "/>
</Grid>
<TextBlock Text=" "/>
</StatusBarItem>
</StatusBar>
</Grid>
@@ -62,8 +62,8 @@ namespace mbts.spark {
var current_smd_source = connectable_builder.create(vm => vm.current_security_md_observable(_bus)).OnDispatcher();
var selected_secs_source = connectable_builder.create(vm => vm.selected_securities_observable()).OnDispatcher();
_commands = new SparkCommand[] {
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ObjectHelper.ValueOrDefault),
SparkCommands.ShowLadder.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowOrderTicketFromSecurity.complete(current_smd_source, md => md.HasValue, ConvertUtils.ValueOrDefault),
SparkCommands.ShowFills.complete(selected_secs_source, always_true, get_fills_startup_data),
SparkCommands.ShowMarketView.complete(selected_secs_source, always_true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria)),
@@ -56,6 +56,7 @@
UIntTemplate="{StaticResource SimpleValueTextTemplate}"
MarketDataTypeTemplate="{StaticResource SimpleValueTextTemplate}"
MarketDataCalculationTemplate="{StaticResource SimpleValueTextTemplate}"
ConditionalPriceTypeTemplate="{StaticResource SimpleValueTextTemplate}"
>
<usp:DataGridParameterCellTemplateConverter.BooleanTemplate>
<DataTemplate>
@@ -122,6 +123,11 @@
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:LegSpreaderParams.Types.LegMarketDataCalculation}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.MarketDataCalculationTemplate>
<usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding value}" ItemsSource="{u:EnumBindingSource {u:Nested sp:ConditionalQuoting.Types.PriceType}}" ItemTemplate="{StaticResource EnumDescriptionTemplate}"/>
</DataTemplate>
</usp:DataGridParameterCellTemplateConverter.ConditionalPriceTypeTemplate>
<usp:DataGridParameterCellTemplateConverter.QuantityMeasureTemplate>
<DataTemplate>
<Grid IsEnabled="{Binding enabled}">
@@ -304,6 +304,7 @@ namespace mbts.ui.spark {
public DataTemplate QuantityMeasureTemplate { get; set; }
public DataTemplate MarketDataTypeTemplate { get; set; }
public DataTemplate MarketDataCalculationTemplate { get; set; }
public DataTemplate ConditionalPriceTypeTemplate { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
Type t = (Type)value;
@@ -314,6 +315,7 @@ namespace mbts.ui.spark {
else if (t == typeof(QuantityMeasure)) return QuantityMeasureTemplate;
else if (t == typeof(LegSpreaderParams.Types.MarketDataType)) return MarketDataTypeTemplate;
else if (t == typeof(LegSpreaderParams.Types.LegMarketDataCalculation)) return MarketDataCalculationTemplate;
else if (t == typeof(ConditionalQuoting.Types.PriceType)) return ConditionalPriceTypeTemplate;
else return null;
}
@@ -103,6 +103,11 @@ namespace bts.shell {
OutsideTicks = 3,
Priority = RulePriority
};
public static ConditionalQuoting ConditionalQuoting => new ConditionalQuoting
{
NTicks = 2,
PriceType = ConditionalQuoting.Types.PriceType.Leg
};
public static QuoteInsideMarketDistanceWithTravel QuoteInsideMarketDistanceWithTravel => new QuoteInsideMarketDistanceWithTravel { Priority = RulePriority };
public static QuoteInsideMarketDistance QuoteInsideMarketDistance => new QuoteInsideMarketDistance { Priority = RulePriority };
#endregion
@@ -46,14 +46,12 @@
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Synthetics: "/>
<TextBlock Grid.Column="1" Text="{Binding ElementName=_grid, Path=Rows.Count}"/>
</Grid>
<TextBlock>
<Run Text="Synthetics:"/>
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
@@ -55,7 +55,7 @@ namespace mbts.spark {
current_row_connectable_builder.create(observe_row_strategy),
rs => rs.HasValue, select_row_strategy_tuple
), SparkCommands.ShowLadder.complete(
vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ObjectHelper.ValueOrDefault
vm_connectable_builder.create(vm => vm.current_security_md), md => md.HasValue, ConvertUtils.ValueOrDefault
), SparkCommands.ShowOrders.complete(() => new OrderBookWindowFactory.OrderBookStartupData {
filters = new OrderBookWindowFactory.FilterStates {
security_criteria = new SecurityCriteria(_vm.value.row_vms.Select(r => r.security))
@@ -0,0 +1,22 @@
using mbts.ui.spark;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark {
partial class SyntheticEditorVM {
class EnableHedgeModel : RowModelBase<bool> {
public override string display_name { get; } = "Enable Hedging";
public override string help_text { get; } = "Send a hedge for this leg?";
public override string man_page { get; } = string.Empty;
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.EnableHedging;
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) =>
leg_tup.leg_params.EnableHedging = value;
public override bool has_errors(IReadOnlyCollection<LegSpreaderParamsTuple> leg_tups) =>
!leg_tups.Any(lt => lt.leg_params.EnableHedging);
}
}
}
@@ -0,0 +1,18 @@
using mbts.ui.spark;
using System;
namespace mbts.spark {
partial class SyntheticEditorVM {
class MinLeanQtyModel : RowModelBase<System.UInt32> {
public override string display_name { get; } = "Min Lean Qty";
public override string help_text { get; } = "Minimum Quantity required at this leg to quote";
public override string man_page { get; } = string.Empty;
public override System.UInt32 get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.MinLeanQty;
public override void set_value(LegSpreaderParamsTuple leg_tup, System.UInt32 value) =>
leg_tup.leg_params.QuoteParams.MinLeanQty = value;
}
}
}
@@ -0,0 +1,39 @@
using bts.shell;
using mbts.ui.spark;
namespace mbts.spark {
partial class SyntheticEditorVM {
class ConditionalQuoteEnabledModel : RowModelBase<bool> {
readonly ConditionalQuotingTicksModel _ticks;
readonly ConditionalQuotingPriceTypeModel _price_type;
public ConditionalQuoteEnabledModel(ConditionalQuotingTicksModel ticks, ConditionalQuotingPriceTypeModel price_type)
{
_ticks = ticks;
_price_type = price_type;
}
public override string display_name { get; } = string.Empty;
public override string help_text { get; } = string.Empty;
public override string man_page { get; } = string.Empty;
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
public override bool get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.ConditionalQuoting != null;
public override void set_value(LegSpreaderParamsTuple leg_tup, bool value) {
if (value) {
if (leg_tup.leg_params.QuoteParams.ConditionalQuoting == null) {
leg_tup.leg_params.QuoteParams.ConditionalQuoting = SyntheticDataHelper.Defaults.ConditionalQuoting;
_ticks.apply_value(leg_tup);
_price_type.apply_value(leg_tup);
}
} else {
leg_tup.leg_params.QuoteParams.ConditionalQuoting = null;
}
_ticks.set_enabled(leg_tup, value);
_price_type.set_enabled(leg_tup, value);
}
}
}
}
@@ -0,0 +1,45 @@
using mbts.ui.spark;
using bts.spark;
using PriceType = bts.spark.ConditionalQuoting.Types.PriceType;
using mbts.collections.extensions;
using System.Collections.Generic;
using bts.shell;
namespace mbts.spark
{
partial class SyntheticEditorVM
{
class ConditionalQuotingPriceTypeModel : RowModelBase<PriceType>
{
IDictionary<uint, PriceType> _value_store = new Dictionary<uint, PriceType>();
public override string display_name { get; } = "Price Type";
public override string help_text { get; } = string.Empty;
public override string man_page { get; } = string.Empty;
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup).PriceType = _value_from_store(leg_tup);
public override PriceType get_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup) is ConditionalQuoting cond ?
cond.PriceType :
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, PriceType value)
{
_value_store[leg_tup.SecurityKey] = value;
if (_get_value(leg_tup) is ConditionalQuoting cond)
cond.PriceType = value;
}
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) => _get_value(leg_tup) != null; //TAHA_SPREADER
ConditionalQuoting _get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
PriceType _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.PriceType);
}
}
}
@@ -0,0 +1,41 @@
using bts.shell;
using bts.spark;
using mbts.collections.extensions;
using mbts.ui.spark;
using System.Collections.Generic;
namespace mbts.spark {
partial class SyntheticEditorVM {
class ConditionalQuotingTicksModel : RowModelBase<int> {
IDictionary<uint, int> _value_store = new Dictionary<uint, int>();
public override string display_name { get; } = "Ticks";
public override string help_text { get; } = string.Empty;
public override string man_page { get; } = string.Empty;
public override ParameterSection[] section_path { get; } = new[] { Sections.Quote, Subsections.Conditional };
public void apply_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup).NTicks = _value_from_store(leg_tup);
public override int get_value(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup) is ConditionalQuoting cond ?
cond.NTicks :
_value_from_store(leg_tup);
public override void set_value(LegSpreaderParamsTuple leg_tup, int value) {
_value_store[leg_tup.SecurityKey] = value;
if (_get_value(leg_tup) is ConditionalQuoting cond)
cond.NTicks = value;
}
protected override bool _get_enabled(LegSpreaderParamsTuple leg_tup) =>
_get_value(leg_tup) != null;
ConditionalQuoting _get_value(LegSpreaderParamsTuple leg_tup) =>
leg_tup.leg_params.QuoteParams.ConditionalQuoting;
int _value_from_store(LegSpreaderParamsTuple leg_tup) =>
_value_store.GetValueOrDefault(leg_tup.SecurityKey, _ => SyntheticDataHelper.Defaults.ConditionalQuoting.NTicks);
}
}
}
@@ -103,6 +103,13 @@ namespace mbts.spark {
"Reduces re-quoting updates by defining a region on either side " +
"of the working quoting order, inside which quote updates will not be sent.");
public static ParameterSection Conditional =>
new ParameterSection("Conditional", true,
"Reduces re-quoting updates by only quoting if N ticks away from the opposite " +
"inside market. Quotes are cancelled when it gets further away. 'Price Type' " +
"dictates if this logic applies to the quoting market or to the synthetic spread " +
"market");
public static ParameterSection InsideMarketThrottleThresholds =>
new ParameterSection("Inside Market Throttle Thresholds", true,
"Reduces re-quoting for quotes that are moving toward the inside market. " +
@@ -2,7 +2,6 @@
using bts.shell;
using bts.spark;
using bts.utils;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.shell;
using mbts.ui.spark;
@@ -110,7 +109,7 @@ namespace mbts.spark {
_validate_leg_count();
};
async Task commit_async(){
async Task commit_async() {
RequestUpdate?.Invoke();
bool valid = true;
if (string.IsNullOrWhiteSpace(name)) {
@@ -125,8 +124,7 @@ namespace mbts.spark {
valid = false;
}
if (valid) {
_synthetic.Synthetic.Legs.Clear();
_synthetic.Synthetic.Legs.AddRange(leg_tuples.Select(l => l.leg).OrderBy(l => l.Security, SecurityHelper.Comparer.Instance));
_synthetic.Synthetic.Legs.SetItems(leg_tuples.Select(l => l.leg));
_synthetic.Symbol = _deduce_symbol(_synthetic);
double tick_frac = _tick_factors.tick_unit / _tick_factors.tick_base;
_synthetic.ContractSpec.LowTick = tick_frac;
@@ -180,6 +178,9 @@ namespace mbts.spark {
var quote_throttle_outside_ticks = new QuoteThrottleOutsideTicksModel();
var quote_throttle_inside_ticks = new QuoteThrottleInsideTicksModel();
var conditional_quoting_ticks = new ConditionalQuotingTicksModel();
var conditional_quoting_price_type = new ConditionalQuotingPriceTypeModel();
var quote_inside_market_throttle_ticks = new QuoteInsideMarketThrottleTicksModel();
var quote_inside_market_throttle_min_move = new QuoteInsideMarketThrottleMinMoveModel();
@@ -201,13 +202,18 @@ namespace mbts.spark {
_quantity_model,
_price_mult_model,
new IsQuoteModel(),
//new EnableHedgeModel(), //TAHA_SPREADER
new HedgeRatioModel(),
//new MinLeanQtyModel(), //TAHA_SPREADER
new QuoteHiddenModel(),
new MarketDataTypeModel(),
new MarketDataCalculationModel(),
new QuoteThrottleEnabledModel(quote_throttle_inside_ticks, quote_throttle_outside_ticks),
quote_throttle_inside_ticks,
quote_throttle_outside_ticks,
new ConditionalQuoteEnabledModel(conditional_quoting_ticks, conditional_quoting_price_type),
conditional_quoting_ticks,
conditional_quoting_price_type,
//new QuoteInsideMarketThrottleEnabledModel(quote_inside_market_throttle_ticks, quote_inside_market_throttle_min_move),
//quote_inside_market_throttle_ticks,
//quote_inside_market_throttle_min_move,
@@ -296,7 +302,6 @@ namespace mbts.spark {
static IEnumerable<LegSpreaderParamsTuple> _get_legs(Security synthetic, ExecutionStrategyImmutable strategy) =>
synthetic.Synthetic.Legs
.OrderBy(l => l.Security, SecurityHelper.Comparer.Instance)
.Select(l => new LegSpreaderParamsTuple(l, strategy.LegParamsMap[l.Security.Id].Clone()));
void _close_new_leg_popup() {
@@ -339,4 +344,4 @@ namespace mbts.spark {
else clear_error(nameof(leg_count));
}
}
}
}
@@ -49,14 +49,12 @@
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Fills: "/>
<TextBlock Grid.Column="1" Text="{Binding ElementName=_grid, Path=Rows.Count}"/>
</Grid>
<TextBlock>
<Run Text="Fills:"/>
<Run Text="{Binding ElementName=_grid, Path=Rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding rows_view.SourceCollection.Count, Mode=OneWay}" />
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
@@ -423,7 +423,8 @@ namespace mbts.spark {
base.InsertItem(index, item);
} catch {
_unhook(col);
} finally { }
throw;
}
} else {
Log.Ui.warn($"{nameof(OrderBookColumnCollection)} added a column that is not of type {nameof(ICVOrderBookColumn)}");
base.InsertItem(index, item);
@@ -104,7 +104,7 @@ namespace mbts.spark {
if (old_order.order_type() != order.order_type())
RaisePropertyChanged(nameof(order_type), nameof(order_metadata));
if (!had_security && _security_md.HasValue) {
RaisePropertyChanged(nameof(security));
RaisePropertyChanged(nameof(security), nameof(security_text));
// need to raise invalidated to refresh the price cells (the security.ContractSpec determines how they are formatted - Ticks!)
invalidate = true;
}
@@ -155,18 +155,12 @@
</c:DIYGrid>
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
<StatusBarItem HorizontalAlignment="Right">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Orders: "/>
<TextBlock Grid.Column="1" Text="{Binding ElementName=_grid, Path=Rows.Count}"/>
<TextBlock Grid.Column="2" Text=" of "/>
<TextBlock Grid.Column="3" Text="{Binding rows_view.SourceCollection.Count}"/>
</Grid>
<TextBlock>
<Run Text="Orders:"/>
<Run Text="{Binding rows.Count, Mode=OneWay}" />
<Run Text="of"/>
<Run Text="{Binding rows.total_row_count, Mode=OneWay}" />
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
@@ -18,8 +18,8 @@ namespace mbts.spark {
// data cache
readonly Dictionary<uint, OrderRecordTuple> _state = new Dictionary<uint, OrderRecordTuple>();
readonly SetMap<uint, uint> _security_orders = new SetMap<uint, uint>();
readonly IDictionary<uint, SecurityMD> _security_md_cache = new Dictionary<uint, SecurityMD>(0);
IReadOnlyDictionary<uint, OrderRecordImmutable> _orders_cache = new Dictionary<uint, OrderRecordImmutable>(0);
IReadOnlyDictionary<uint, SecurityMD> _security_md_cache = new Dictionary<uint, SecurityMD>(0);
IDisposable _dispose;
public OrderRecordTupleSource(IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities, IObservable<TableUpdate<OrderRecordImmutable, uint>> orders) =>
@@ -92,11 +92,14 @@ namespace mbts.spark {
Dictionary<uint, OrderRecordTuple> added = new Dictionary<uint, OrderRecordTuple>(), removed = new Dictionary<uint, OrderRecordTuple>();
Dictionary<uint, SecurityMD> added_securities = new Dictionary<uint, SecurityMD>(), removed_securities = new Dictionary<uint, SecurityMD>();
_security_md_cache.Keys.difference(securities.Keys, out IEnumerable<uint> added_keys, out IEnumerable<uint> removed_keys);
/// Warning: <see cref="_security_md_cache"/> must NOT be reassigned with <see cref="securities"/>; the latter is always the same object.
/// This would mean that the <see cref="GlobalExtensions.difference{T}(IEnumerable{T}, IEnumerable{T}, out IEnumerable{T}, out IEnumerable{T})"/>
/// call above would literally be calculating the difference between the keys of the same dictionary!
foreach (var k in removed_keys) {
if (_security_orders.try_lookup(k, out ISet<uint> order_keys)) {
removed_securities.Add(k, _security_md_cache[k]);
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
SecurityMD old_sec_md = _security_md_cache[k];
removed_securities.Add(k, old_sec_md);
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
foreach (OrderRecordImmutable order in orders) {
OrderRecordTuple old_ft = new OrderRecordTuple(order, old_sec_md);
removed.Add(old_ft.order.Key, old_ft);
@@ -104,12 +107,13 @@ namespace mbts.spark {
added.Add(new_ft.order.Key, new_ft);
}
}
_security_md_cache.Remove(k);
}
foreach (var k in added_keys) {
SecurityMD new_sec_md = securities[k];
if (_security_orders.try_lookup(k, out ISet<uint> order_keys)) {
added_securities.Add(k, securities[k]);
added_securities.Add(k, new_sec_md);
IEnumerable<OrderRecordImmutable> orders = order_keys.Select(fk => _orders_cache[fk]);
SecurityMD new_sec_md = securities[k];
foreach (OrderRecordImmutable order in orders) {
OrderRecordTuple old_ft = new OrderRecordTuple(order, null);
removed.Add(old_ft.order.Key, old_ft);
@@ -117,8 +121,8 @@ namespace mbts.spark {
added.Add(new_ft.order.Key, new_ft);
}
}
_security_md_cache.Add(k, new_sec_md);
}
_security_md_cache = securities;
_apply_changes(added, removed, added_securities, removed_securities);
}
}
@@ -42,7 +42,7 @@ namespace mbts.ui {
public bool CanExecute(object parameter) {
if (!allow_concurrent_executions && _n_executing > 0)
return false;
else return _can_execute(ObjectHelper.try_convert<T>(parameter));
else return _can_execute(ConvertUtils.try_convert<T>(parameter));
}
public async Task Execute(object parameter) {
@@ -51,7 +51,7 @@ namespace mbts.ui {
if (!allow_concurrent_executions)
RaiseCanExecuteChanged();
}
await _execute(ObjectHelper.try_convert<T>(parameter));
await _execute(ConvertUtils.try_convert<T>(parameter));
if (--_n_executing == 0) {
_raise_is_executing_changed();
if (!allow_concurrent_executions)
@@ -27,13 +27,13 @@ namespace mbts.ui
if (_can_execute == null)
return true;
else
return _can_execute(ObjectHelper.try_convert<T>(parameter));
return _can_execute(ConvertUtils.try_convert<T>(parameter));
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) =>
_execute(ObjectHelper.try_convert<T>(parameter));
_execute(ConvertUtils.try_convert<T>(parameter));
}
public class DelegateCommand : DelegateCommand<object> {
@@ -12,7 +12,7 @@ namespace mbts.ui {
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => _can_execute;
public void Execute(object parameter) => _execute(ObjectHelper.try_convert<T>(parameter));
public void Execute(object parameter) => _execute(ConvertUtils.try_convert<T>(parameter));
public void OnCompleted() { }
public void OnError(Exception error) { }
@@ -364,7 +364,7 @@ namespace System.Collections.Generic {
}
/// <summary>
/// Uses <see cref="ObjectHelper.try_convert{TResult}(object)"/> to convert the elements of an <see cref="IEnumerable"/> to the specified type.
/// Uses <see cref="ConvertUtils.try_convert{TResult}(object)"/> to convert the elements of an <see cref="IEnumerable"/> to the specified type.
/// </summary>
/// <typeparam name="TResult">The type to convert the elements of source to.</typeparam>
/// <param name="source">The <see cref="IEnumerable"/> that contains the elements to be cast to type <typeparamref name="TResult"/></param>
@@ -373,7 +373,7 @@ namespace System.Collections.Generic {
public static IEnumerable<TResult> convert<TResult>(this IEnumerable source) {
ExceptionAssert.Argument.NotNull(source, nameof(source));
foreach (object obj in source)
yield return ObjectHelper.try_convert<TResult>(obj);
yield return ConvertUtils.try_convert<TResult>(obj);
}
}
@@ -131,12 +131,6 @@ namespace mbts
return -1;
}
public static int safe_compare<T>(this T a, T b, Func<T, T, int> compare) =>
a == null ? (b == null ? 0 : -1) : (b == null ? 1 : compare(a, b));
public static int safe_compare<T>(this T a, T b) where T : IComparable<T> =>
a == null ? (b == null ? 0 : -1) : (b == null ? 1 : a.CompareTo(b));
public static bool safe_equals<T>(this T a, T b, Func<T, T, bool> equals) =>
a == null ? b == null : b != null && equals(a, b);
@@ -1,4 +1,5 @@
using System;
using bts.utils;
using System;
using System.Collections.Generic;
namespace mbts.utils {
@@ -32,8 +32,11 @@ namespace mbts.utils {
// need to lock bc simultaneous callbacks on diff threads could generate 2 subscriptions at once,
// only 1 of which would get disposed in the next callback.
Disposable.dispose(ref _current_key_sub);
if (!_is_disposed)
_current_key_sub = _selector(key)?.Subscribe(_value_subject);
if (!_is_disposed) {
// IMPORTANT: do NOT subscribe _value_subject directly to the inner sequences. Subscribe with a callback delegate instead (e.g. _value_subject.OnNext).
// Reason: If a sequence 'completes' while _value_subject's subscription is active, it will enter a 'Terminated' state, and forever cease notifying observers of subsequent updates.
_current_key_sub = _selector(key)?.Subscribe(_value_subject.OnNext);
}
}
}
+1 -1
View File
@@ -348,7 +348,7 @@ namespace mbts.ui.controls {
class BadgeTextLinesConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
if (ObjectHelper.try_convert_args(values, out Side side, out uint quantity, out uint filled, out mbts.spark.LadderSettings.BadgeStyle style))
if (ConvertUtils.try_convert_args(values, out Side side, out uint quantity, out uint filled, out mbts.spark.LadderSettings.BadgeStyle style))
return BadgeRender.format(side, quantity, filled, style);
else return new string[0];
}
@@ -218,7 +218,7 @@ namespace mbts.ui.controls.color_picker {
public DataTemplate EditableTemplate { get; set; }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
if (ObjectHelper.try_convert_args(values, out ColorPaletteVM vm, out bool is_selected)) {
if (ConvertUtils.try_convert_args(values, out ColorPaletteVM vm, out bool is_selected)) {
if (vm.can_user_edit && is_selected)
return EditableTemplate;
}
@@ -38,7 +38,7 @@ namespace mbts.ui.controls.color_picker {
class NamedColorItemToolTipConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
ObjectHelper.try_convert_args(values, out string name, out bool can_user_edit) ?
ConvertUtils.try_convert_args(values, out string name, out bool can_user_edit) ?
$"{name} (Alt + Click for info{(can_user_edit ? ", Ctrl + Click to assign" : string.Empty)})" :
string.Empty;
@@ -109,6 +109,6 @@ namespace mbts.spark.filters {
protected virtual void _validate() { }
bool IDataGridColumnFilter.filter(object row) =>
filter(ObjectHelper.try_convert<TRow>(row));
filter(ConvertUtils.try_convert<TRow>(row));
}
}
@@ -70,6 +70,6 @@ namespace mbts.spark.filters {
bool IDataGridColumnFilter.filter(object row) =>
filter(ObjectHelper.try_convert<TRow>(row));
filter(ConvertUtils.try_convert<TRow>(row));
}
}
@@ -20,6 +20,7 @@
Content="Apply"
CommandParameter="{TemplateBinding ApplyCommandParameter}"
Command="{TemplateBinding ApplyCommand}"
CommandTarget="{TemplateBinding ApplyCommandTarget}"
IsDefault="True"
Focusable="False"
Style="{StaticResource CommonButton}"
@@ -24,6 +24,11 @@ namespace mbts.ui.spark {
public object ApplyCommandParameter { get => GetValue(ApplyCommandParameterProperty); set => SetValue(ApplyCommandParameterProperty, value); }
#endregion
#region ApplyCommandTarget
public static readonly DependencyProperty ApplyCommandTargetProperty = WPFHelper.create_dp<IInputElement, FilterContentControl>(nameof(ApplyCommandTarget));
public IInputElement ApplyCommandTarget { get => GetValue(ApplyCommandTargetProperty) as IInputElement; set => SetValue(ApplyCommandTargetProperty, value); }
#endregion
#region ClearCommand
public static readonly DependencyProperty ClearCommandProperty = DependencyProperty.Register(
nameof(ClearCommand), typeof(ICommand), typeof(FilterContentControl));
@@ -106,6 +106,7 @@
<usp:FilterContentControl
ApplyCommandParameter="{Binding}"
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
ApplyCommandTarget="{Binding ElementName=Filter}"
ClearCommandParameter="{Binding}"
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
>
@@ -115,6 +116,9 @@
Begin="{Binding begin}"
End="{Binding end}"
>
<c:TimeFilterControl.CommandBindings>
<CommandBinding Command="{x:Static c:DataGridFilterProperties.CommitCommand}" Executed="{x:Static c:TimeFilterControl.CommitExecuted}"/>
</c:TimeFilterControl.CommandBindings>
<u:Behavior.BindingUpdateGroups>
<u:BindingUpdateGroupCollection>
<u:BindingUpdateGroup Source="{Binding DataContext, Source={u:DirectReference Filter}}" Target="{Binding Source={u:DirectReference Filter}}">
@@ -7,6 +7,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace mbts.ui.controls {
@@ -14,6 +15,18 @@ namespace mbts.ui.controls {
/// Interaction logic for TimeFilterControl.xaml
/// </summary>
public class TimeFilterControl : Control {
public static readonly ExecutedRoutedEventHandler CommitExecuted = (s, e) => {
if (s is TimeFilterControl tfc) {
tfc.get_template_child<DateTimePicker>(_begin_dtp_name)?.flush_pending_changes();
tfc.get_template_child<DateTimePicker>(_end_dtp_name)?.flush_pending_changes();
(e.Parameter as IDataGridColumnFilter)?.apply.TryExecuteIfCan(null);
e.Handled = true;
}
};
const string
_begin_dtp_name = "PART_Begin_DateTimePicker",
_end_dtp_name = "PART_End_DateTimePicker";
static TimeFilterControl() =>
DefaultStyleKeyProperty.OverrideMetadata(typeof(TimeFilterControl), new FrameworkPropertyMetadata(typeof(TimeFilterControl)));
@@ -45,17 +58,17 @@ namespace mbts.ui.controls {
var clear_begin_time = this.assert_template_child<Hyperlink>("ClearBeginLink");
var clear_end_time = this.assert_template_child<Hyperlink>("ClearEndLink");
var set_last_hour = this.assert_template_child<Hyperlink>("SetLastHourLink");
clear_begin_time.Command = new DelegateCommand(() => Begin = default);
clear_end_time.Command = new DelegateCommand(() => End = default);
set_last_hour.Command = new DelegateCommand(() => Begin = TimeTextTuple.FromDateTime(DateTime.Now.AddHours(-1)));
clear_begin_time.Command = new DelegateCommand(() => SetCurrentValue(BeginProperty, default(TimeTextTuple)));
clear_end_time.Command = new DelegateCommand(() => SetCurrentValue(EndProperty, default(TimeTextTuple)));
set_last_hour.Command = new DelegateCommand(() => SetCurrentValue(BeginProperty, TimeTextTuple.FromDateTime(DateTime.Now.AddHours(-1))));
}
class TimeFilterErrorAdorner : NotifyDataErrorAdorner<TimeFilterControl, ITimeFilterVM>, IDisposable {
readonly ChildProperty<DateTimePicker> _begin_picker, _end_picker;
public TimeFilterErrorAdorner(TimeFilterControl view) : base(view) {
_begin_picker = child_property<DateTimePicker>("PART_Begin_DateTimePicker");
_end_picker = child_property<DateTimePicker>("PART_End_DateTimePicker");
_begin_picker = child_property<DateTimePicker>(_begin_dtp_name);
_end_picker = child_property<DateTimePicker>(_end_dtp_name);
}
protected override void OnRender(DrawingContext drawing_context) {
@@ -52,7 +52,7 @@ namespace mbts.ui.controls
protected DataGridExRow() { }
public abstract object get_cell(TColKey col_key);
public object get_cell(object col_key) { return get_cell(ObjectHelper.try_convert<TColKey>(col_key)); }
public object get_cell(object col_key) { return get_cell(ConvertUtils.try_convert<TColKey>(col_key)); }
}
@@ -60,6 +60,6 @@ namespace mbts.ui.controls
public abstract class DataGridExCell<TData> : ViewModelBase, IDataGridExCell<TData>, IDataGridExCell
{
public abstract void update(TData data);
public void update(object data) { update(ObjectHelper.try_convert<TData>(data)); }
public void update(object data) { update(ConvertUtils.try_convert<TData>(data)); }
}
}
@@ -1,6 +1,6 @@
using mbts.ui.utility;
using mbts.ui.utility.text;
using mbts.utils;
using bts.utils;
using System;
using System.Collections;
using System.Globalization;
@@ -77,7 +77,7 @@ namespace mbts.ui.controls {
class FontFamilySupportsWeightConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
if (ObjectHelper.try_convert_args(values, out FontFamily ff, out FontWeight fw))
if (ConvertUtils.try_convert_args(values, out FontFamily ff, out FontWeight fw))
return TextUtils.available_weights(ff).Contains(fw);
return false;
}
@@ -1,4 +1,4 @@
using mbts.utils;
using bts.utils;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -223,7 +223,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="4" />
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@@ -231,13 +231,37 @@
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Cross Thresh</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="1"
>
<TextBlock VerticalAlignment="Center">Cross Thresh</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="When the size of the opposite-inside market falls below this size threshold the order will payup." />
</StackPanel>
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
<TextBlock Grid.Row="3" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="3"
>
<TextBlock VerticalAlignment="Center">Max Chase Ticks</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="The max number of ticks the order will continue to pay up after the initial payup." />
</StackPanel>
<c:TickUpDownUInt Grid.Row="3" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
<TextBlock Grid.Row="5">On Toward</TextBlock>
<ComboBox Grid.Row="5" Grid.Column="2" ItemsSource="{Binding trade_toward_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding trade_toward_behavior}"/>
<TextBlock Grid.Row="7">Child</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="5"
>
<TextBlock VerticalAlignment="Center">On Toward</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="What should happen when the market moves toward the order." />
</StackPanel>
<ComboBox Grid.Row="5" Grid.Column="2" ItemsSource="{Binding trade_toward_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding trade_toward_behavior}"/>
<StackPanel
Orientation="Horizontal"
Grid.Row="7"
>
<TextBlock >Child</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered" />
</StackPanel>
<uag:OrderDetailsPopupControl
Grid.Row="7" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
@@ -266,9 +290,21 @@
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Join Thresh</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="1"
>
<TextBlock VerticalAlignment="Center">Join Thresh</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="The Threshold at which the order will join the market when the market flips (the event where the best offer becomes the best bid in the buy case)." />
</StackPanel>
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding join_threshold}"/>
<TextBlock Grid.Row="3">Child</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="3"
>
<TextBlock VerticalAlignment="Center">Child</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered" />
</StackPanel>
<uag:OrderDetailsPopupControl
Grid.Row="3" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
@@ -295,9 +331,22 @@
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" VerticalAlignment="Center">Top Thresh</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="1"
>
<TextBlock VerticalAlignment="Center">Top Thresh</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="If the order is at the top of book, the quantity remaining below which the order will be canceled." />
</StackPanel>
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding top_threshold}"/>
<TextBlock Grid.Row="3">Child</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="3"
>
<TextBlock VerticalAlignment="Center">Child</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered." />
</StackPanel>
<uag:OrderDetailsPopupControl
Grid.Row="3" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
@@ -326,9 +375,21 @@
<ColumnDefinition Width="12"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" VerticalAlignment="Center" >Hedge Ticks</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="1"
>
<TextBlock VerticalAlignment="Center" >Hedge Ticks</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks to offset the hedge." />
</StackPanel>
<c:TickUpDownInt Grid.Row="1" Grid.Column="2" Value="{Binding hedge_ticks}"/>
<TextBlock Grid.Row="3">Hedge</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="3"
>
<TextBlock VerticalAlignment="Center">Hedge</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of the hedge order to send for each fill of Child Order." />
</StackPanel>
<uag:OrderDetailsPopupControl
Grid.Row="3" Grid.Column="2"
IsOpen="{Binding hedge_order.editable_properties_open, FallbackValue=False}"
@@ -336,7 +397,14 @@
SelectedItem="{Binding hedge_order}"
HasEditableProperties="{Binding hedge_order.has_editable_properties, FallbackValue=False}"
/>
<TextBlock Grid.Row="5">Child</TextBlock>
<StackPanel
Orientation="Horizontal"
Grid.Row="5"
>
<TextBlock VerticalAlignment="Center">Child</TextBlock>
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the type of order to be placed when the algo is triggered." />
</StackPanel>
<uag:OrderDetailsPopupControl
Grid.Row="5" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
@@ -377,7 +445,7 @@
/>
</Grid>
</DataTemplate>
</usp:OrderModificationTemplateSelector.OffsetOrderTemplate>
</usp:OrderModificationTemplateSelector.OffsetOrderTemplate>
<usp:OrderModificationTemplateSelector.TriggerOrderTemplate>
<DataTemplate>
<Grid Grid.IsSharedSizeScope="True">
@@ -458,7 +526,7 @@
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="SemiBold" Margin="0,0,4,0">Price</TextBlock>
<c:HelpButton ToolTip="The child order, resulting from the trigger condition being met, can be sent either at a specified price specified price (Fixed) price or payup/less ticks from the Trigger Price" />
<c:HelpButton ToolTip="The child order, resulting from the trigger condition being met, can be sent either at a specified price (Fixed) price or payup/less ticks from the Trigger Price" />
</StackPanel>
</GroupBox.Header>
<Grid>
@@ -634,8 +702,8 @@
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding descriptor.ParamName}"/>
<CheckBox HorizontalAlignment="Stretch" Grid.Column="2" IsChecked="{Binding param.AsBool, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.BoolTemplate>
<usp:DynamicOrderParamTemplateSelector.DoubleTemplate>
<DataTemplate>
@@ -647,9 +715,9 @@
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding descriptor.ParamName}"/>
<c:TickUpDownDouble Grid.Column="2" Value="{Binding param.AsDouble, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.DoubleTemplate>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.DoubleTemplate>
<usp:DynamicOrderParamTemplateSelector.IntTemplate>
<DataTemplate>
<Grid>
@@ -660,9 +728,9 @@
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding descriptor.ParamName}"/>
<c:TickUpDownInt Grid.Column="2" Value="{Binding param.AsInt32, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.IntTemplate>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.IntTemplate>
<usp:DynamicOrderParamTemplateSelector.UIntTemplate>
<DataTemplate>
<Grid>
@@ -673,9 +741,9 @@
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding descriptor.ParamName}"/>
<c:TickUpDownUInt Grid.Column="2" Value="{Binding param.AsUint32, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.UIntTemplate>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.UIntTemplate>
<usp:DynamicOrderParamTemplateSelector.StringTemplate>
<DataTemplate>
<Grid>
@@ -686,9 +754,9 @@
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding descriptor.ParamName}"/>
<TextBox HorizontalAlignment="Stretch" Grid.Column="2" Text="{Binding param.AsString, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.StringTemplate>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.StringTemplate>
<usp:DynamicOrderParamTemplateSelector.EnumTemplate>
<DataTemplate>
<Grid>
@@ -699,11 +767,11 @@
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding descriptor.ParamName}"/>
<ComboBox Grid.Column="2" ItemsSource="{Binding descriptor.ParamType.Enumerated.Element}" SelectedItem="{Binding param.AsEnumerated.EnumValue}" />
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.EnumTemplate>
</Grid>
</DataTemplate>
</usp:DynamicOrderParamTemplateSelector.EnumTemplate>
</usp:DynamicOrderParamTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
<TextBlock Grid.Row="3">Child</TextBlock>
<uag:OrderDetailsPopupControl
@@ -12,7 +12,7 @@
<cv:KeyValue Key="{x:Null}" Value="{u:Visibility Visible}"/>
</cv:ValueMap>
<ControlTemplate x:Key="HoverButtonOverlayTemplate2" TargetType="c:TickUpDown">
<ControlTemplate x:Key="HoverButtonOverlayTemplate" TargetType="c:TickUpDown">
<Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<Grid.RowDefinitions>
<RowDefinition />
@@ -86,8 +86,8 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="38"/>
</Grid.ColumnDefinitions>
<c:TickUpDown x:Name="PART_time_tick" HorizontalAlignment="Stretch" EnableArrowKeyTick="False" Template="{StaticResource HoverButtonOverlayTemplate2}">
<TextBox x:Name="PART_time_text" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True">
<c:TickUpDown x:Name="PART_time_tick" HorizontalAlignment="Stretch" EnableArrowKeyTick="False" Template="{StaticResource HoverButtonOverlayTemplate}">
<TextBox x:Name="PART_time_text" u:TextBoxBehavior.CommitOnEnter="True" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True">
<TextBox.InputBindings>
<KeyBinding Key="Up" Command="{x:Static c:DateTimePicker.TickTimeComponentCommand}" CommandParameter="{u:Int 1}"/>
<KeyBinding Key="Down" Command="{x:Static c:DateTimePicker.TickTimeComponentCommand}" CommandParameter="{u:Int -1}"/>
@@ -95,15 +95,15 @@
</TextBox.InputBindings>
</TextBox>
</c:TickUpDown>
<c:TickUpDown x:Name="PART_month_tick" Margin="2,0,0,0" Grid.Column="1" Template="{StaticResource HoverButtonOverlayTemplate2}">
<c:TickUpDown x:Name="PART_month_tick" Margin="2,0,0,0" Grid.Column="1" Template="{StaticResource HoverButtonOverlayTemplate}">
<TextBox x:Name="PART_month_text" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True"/>
</c:TickUpDown>
<TextBlock Grid.Column="2" Margin="2,0">/</TextBlock>
<c:TickUpDown x:Name="PART_day_tick" Grid.Column="3" Template="{StaticResource HoverButtonOverlayTemplate2}">
<c:TickUpDown x:Name="PART_day_tick" Grid.Column="3" Template="{StaticResource HoverButtonOverlayTemplate}">
<TextBox x:Name="PART_day_text" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True"/>
</c:TickUpDown>
<TextBlock Grid.Column="4" Margin="2,0">/</TextBlock>
<c:TickUpDown x:Name="PART_year_tick" Grid.Column="5" Template="{StaticResource HoverButtonOverlayTemplate2}">
<c:TickUpDown x:Name="PART_year_tick" Grid.Column="5" Template="{StaticResource HoverButtonOverlayTemplate}">
<TextBox x:Name="PART_year_text" Height="20" u:TextBoxBehavior.EnableArrowKeyTraverse="True" u:Behavior.DisablePaste="True"/>
</c:TickUpDown>
<TextBlock Grid.Column="0" Text="hh:mm tt" Style="{StaticResource TextPromptStyle}" Visibility="{Binding Text, ElementName=PART_time_text, Converter={StaticResource NullOrEmptyVisibilityConverter}}"/>
@@ -18,6 +18,11 @@ namespace mbts.ui.controls {
public static readonly RoutedCommand TickTimeComponentCommand = new RoutedCommand();
DTPController _controller;
TextBox
_month_text,
_day_text,
_year_text,
_time_text;
bool _suspend_notify_vm;
static DateTimePicker() =>
@@ -34,18 +39,25 @@ namespace mbts.ui.controls {
IList<IDisposable> _hooks = new List<IDisposable>();
public void flush_pending_changes() {
_month_text?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
_day_text?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
_year_text?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
_time_text?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
}
public override void OnApplyTemplate() {
var month = this.assert_template_child<TickUpDown>("PART_month_tick");
var month_text = this.assert_template_child<TextBox>("PART_month_text");
_month_text = this.assert_template_child<TextBox>("PART_month_text");
var day = this.assert_template_child<TickUpDown>("PART_day_tick");
var day_text = this.assert_template_child<TextBox>("PART_day_text");
_day_text = this.assert_template_child<TextBox>("PART_day_text");
var year = this.assert_template_child<TickUpDown>("PART_year_tick");
var year_text = this.assert_template_child<TextBox>("PART_year_text");
_year_text = this.assert_template_child<TextBox>("PART_year_text");
var time = this.assert_template_child<TickUpDown>("PART_time_tick");
var time_text = this.assert_template_child<TextBox>("PART_time_text");
_time_text = this.assert_template_child<TextBox>("PART_time_text");
if (_controller != null) _controller.PropertyChanged -= _vm_PropertyChanged;
_controller = new DTPController(time_text);
_controller = new DTPController(_time_text);
_controller.result = TimeTuple;
_controller.PropertyChanged += _vm_PropertyChanged;
time.CommandBindings.Add(new CommandBinding(TickTimeComponentCommand, (s, e) => {
@@ -53,53 +65,52 @@ namespace mbts.ui.controls {
}));
time.CommandBindings.Add(new CommandBinding(TextBoxBehavior.SelectAllCommand, TextBoxBehavior.SelectAllCommandExecutedHandler));
const int delay = 500;
time.SetBinding(TickUpDown.TickCommandProperty, new Binding("tick_time") { Source = _controller });
time.TickUpParameter = 1;
time.TickDownParameter = -1;
time.TickUpAltParameter = 60;
time.TickDownAltParameter = -60;
time_text.SetBinding(TextBox.TextProperty, new Binding("time_text") { Source = _controller, Delay = delay, ValidatesOnExceptions = true });
_hooks.Add(time_text.SubscribePreviewTextInput((s, e) => {
string preview = time_text.PeekText(e);
_time_text.SetBinding(TextBox.TextProperty, new Binding("time_text") { Source = _controller, ValidatesOnExceptions = true });
_hooks.Add(_time_text.SubscribePreviewTextInput((s, e) => {
string preview = _time_text.PeekText(e);
if (RegexFilters.Time.StandardTimeOptionalSeconds.IsMatch(preview)) {
_controller.use_military_time = false;
} else if (RegexFilters.Time.MilitaryTimeOptionalSeconds.IsMatch(preview)) {
_controller.use_military_time = true;
} else {
e.Handled = true;
e.Handled = !RegexFilters.Time.FlexibleTime.IsMatch(preview);
}
}));
month.SetBinding(TickUpDown.TickCommandProperty, new Binding("tick_month") { Source = _controller });
month.TickUpParameter = 1;
month.TickDownParameter = -1;
month_text.SetBinding(TextBox.TextProperty, new Binding("month_text") { Source = _controller, Delay = delay });
_hooks.Add(month_text.SubscribePreviewTextInput((s, e) =>
e.Handled = !RegexFilters.Time.MonthAbbrev.IsMatch(month_text.PeekText(e))
_month_text.SetBinding(TextBox.TextProperty, new Binding("month_text") { Source = _controller });
_hooks.Add(_month_text.SubscribePreviewTextInput((s, e) =>
e.Handled = !RegexFilters.Time.MonthAbbrev.IsMatch(_month_text.PeekText(e))
));
day.SetBinding(TickUpDown.TickCommandProperty, new Binding("tick_day") { Source = _controller });
day.TickUpParameter = 1;
day.TickDownParameter = -1;
day_text.SetBinding(TextBox.TextProperty, new Binding("day_text") { Source = _controller, Delay = delay });
_hooks.Add(day_text.SubscribePreviewTextInput((s, e) =>
e.Handled = !RegexFilters.Time.DaysInMonth.IsMatch(day_text.PeekText(e))
_day_text.SetBinding(TextBox.TextProperty, new Binding("day_text") { Source = _controller });
_hooks.Add(_day_text.SubscribePreviewTextInput((s, e) =>
e.Handled = !RegexFilters.Time.DaysInMonth.IsMatch(_day_text.PeekText(e))
));
year.SetBinding(TickUpDown.TickCommandProperty, new Binding("tick_year") { Source = _controller });
year.TickUpParameter = 1;
year.TickDownParameter = -1;
year_text.SetBinding(TextBox.TextProperty, new Binding("year_text") { Source = _controller, Delay = delay });
_hooks.Add(year_text.SubscribePreviewTextInput((s, e) =>
e.Handled = !RegexFilters.Time.Year.IsMatch(year_text.PeekText(e))
_year_text.SetBinding(TextBox.TextProperty, new Binding("year_text") { Source = _controller });
_hooks.Add(_year_text.SubscribePreviewTextInput((s, e) =>
e.Handled = !RegexFilters.Time.Year.IsMatch(_year_text.PeekText(e))
));
}
void _vm_PropertyChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(DTPController.result)) {
_suspend_notify_vm = true;
TimeTuple = _controller.result;
SetCurrentValue(TimeTupleProperty, _controller.result);
_suspend_notify_vm = false;
}
}
@@ -0,0 +1,50 @@
using bts;
using mbts.spark.filters;
using mbts.ui.controls.adorners;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace mbts.ui.controls {
public class UniformTabGrid : UniformGrid {
public UniformTabGrid () {
this.IsItemsHost = true;
this.Rows = 1;
}
protected override Size MeasureOverride(Size constraint) {
var child_tabs = this.Children.OfType<TabItem>();
var child_tabs_max_width = child_tabs.Sum(t => t.MaxWidth);
// I noticed when debugging that this can sometimes be infinty,
// especially when a TabItem has not been set with a MaxWidth.
if (!double.IsInfinity(child_tabs_max_width)) {
// if the window width is greater than the total tab width, then
// there is more than enough space for all the tabs. If this
// is set to the default of Stretch and the window has only 1
// tab or few tabs, the tab appears in the middle, which is not good.
// i.e. we want tabs to added left to right, so align left
//
// When the window is less than the total tab width, then align
// can be set to stretch b/c the UniformGrid will take care of
// sizing each tab accordingly.
this.HorizontalAlignment = (constraint.Width > child_tabs_max_width)
? HorizontalAlignment.Left
: HorizontalAlignment.Stretch;
// this bit here is necessary because when the window is expanded
// beyond the maxwidth of all the tabs, we switch to Left and the
// headers snap to some smaller than MaxWidth size, which is not
// the behavior we want
foreach (var child in child_tabs) {
child.Width = this.HorizontalAlignment == HorizontalAlignment.Left
? child.MaxWidth
: double.NaN;
}
}
return base.MeasureOverride(constraint);
}
}
}
@@ -37,8 +37,8 @@ namespace mbts.ui.converters {
private readonly Func<TIn, TOut> _convert;
private readonly Func<TOut, TIn> _convert_back;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return _convert(ObjectHelper.try_convert<TIn>(value)); }
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return _convert_back(ObjectHelper.try_convert<TOut>(value)); }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return _convert(ConvertUtils.try_convert<TIn>(value)); }
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return _convert_back(ConvertUtils.try_convert<TOut>(value)); }
}
public class AnonymousMultiValueConverter<TIn1, TIn2, TOut> : IMultiValueConverter {
@@ -49,7 +49,7 @@ namespace mbts.ui.converters {
public TOut ErrorValue { get; set; }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
ObjectHelper.try_convert_args(values, out TIn1 a, out TIn2 b) ? _convert(a, b) : ErrorValue;
ConvertUtils.try_convert_args(values, out TIn1 a, out TIn2 b) ? _convert(a, b) : ErrorValue;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
@@ -62,7 +62,7 @@ namespace mbts.ui.converters {
public TOut ErrorValue { get; set; }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
ObjectHelper.try_convert_args(values, out TIn1 a, out TIn2 b, out TIn3 c) ? _convert(a, b, c) : ErrorValue;
ConvertUtils.try_convert_args(values, out TIn1 a, out TIn2 b, out TIn3 c) ? _convert(a, b, c) : ErrorValue;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
@@ -1,4 +1,4 @@
using mbts.utils;
using bts.utils;
using System;
using System.Globalization;
using System.Windows.Data;
@@ -19,7 +19,7 @@ namespace mbts.ui.converters
}
public class DynamicValueEquals<T> : DynamicValueEquals {
protected override bool ValuesEqual(object x, object y) { return ObjectHelper.try_convert(x, out T tx) && ObjectHelper.try_convert(y, out T ty) && ValuesEqual(tx, ty); }
protected override bool ValuesEqual(object x, object y) { return ConvertUtils.try_convert(x, out T tx) && ConvertUtils.try_convert(y, out T ty) && ValuesEqual(tx, ty); }
protected virtual bool ValuesEqual(T x, T y) { return CompareUtils.NullCheckEquals(x, y); }
}
}
@@ -12,7 +12,7 @@ namespace mbts.ui.converters
}
public class StaticValueEqualsConverter<T> : StaticValueEqualsConverter {
protected override bool ValueEquals(object value) { return ObjectHelper.try_convert(value, out T result) && ValueEquals(result); }
protected override bool ValueEquals(object value) { return ConvertUtils.try_convert(value, out T result) && ValueEquals(result); }
protected virtual bool ValueEquals(T value) { return base.ValueEquals(value); }
}
}
@@ -32,7 +32,7 @@ namespace mbts.ui
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) {
return !_is_executing && (_can_execute == null || _can_execute(ObjectHelper.try_convert<T>(parameter)));
return !_is_executing && (_can_execute == null || _can_execute(ConvertUtils.try_convert<T>(parameter)));
}
public void RaiseCanExecuteChanged() {
@@ -45,7 +45,7 @@ namespace mbts.ui
_is_executing = true;
RaiseCanExecuteChanged();
try {
var w = await _factory(ObjectHelper.try_convert<T>(parameter));
var w = await _factory(ConvertUtils.try_convert<T>(parameter));
if (w != null) {
if (_off_x.HasValue && _off_y.HasValue)
w.move_to_mouse(_off_x.Value, _off_x.Value);
@@ -28,12 +28,12 @@ namespace mbts.ui
private IDisposable _dialog;
public Predicate<T> can_execute { get; set; }
public bool CanExecute(object parameter) { return can_execute == null ? true : can_execute(ObjectHelper.try_convert<T>(parameter)); }
public bool CanExecute(object parameter) { return can_execute == null ? true : can_execute(ConvertUtils.try_convert<T>(parameter)); }
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) {
Disposable.dispose(ref _dialog);
Window w = _factory(ObjectHelper.try_convert<T>(parameter));
Window w = _factory(ConvertUtils.try_convert<T>(parameter));
if (w != null) {
if (_try_dispose_vm_on_close) {
w.Closed += (s, e) => {
@@ -101,10 +101,10 @@ namespace mbts.ui {
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => _can_execute(ObjectHelper.try_convert<TParam>(parameter));
public bool CanExecute(object parameter) => _can_execute(ConvertUtils.try_convert<TParam>(parameter));
public void Execute(object parameter) {
_safe_close_dialog();
TParam tparam = ObjectHelper.try_convert<TParam>(parameter);
TParam tparam = ConvertUtils.try_convert<TParam>(parameter);
_dialog = _dialog_factory(tparam);
if (_dialog.ShowDialog() == DialogResult.OK)
_commit(tparam, _dialog.FileName);
@@ -24,7 +24,7 @@ namespace mbts.ui {
public static T get_template_child<T>(this Control control, string name) {
object obj = get_template_child(control, name);
return ObjectHelper.try_convert<T>(obj);
return ConvertUtils.try_convert<T>(obj);
}
public static bool try_get_template_child(this Control control, string name, out object result) {
@@ -40,7 +40,7 @@ namespace mbts.ui {
public static bool try_get_template_child<T>(this Control control, string name, out T result) {
if (try_get_template_child(control, name, out object obj))
return ObjectHelper.try_convert(obj, out result);
return ConvertUtils.try_convert(obj, out result);
result = default;
return false;
}
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:ui="clr-namespace:mbts.ui"
xmlns:c="clr-namespace:mbts.ui.controls"
mc:Ignorable="d" AllowDrop="True"
Style="{StaticResource BTSFloatingWindow}"
d:DesignHeight="300" d:DesignWidth="300"
@@ -25,18 +26,25 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<WrapPanel x:Name="HeaderPanel" Orientation="Horizontal" Margin="0,0,100,0"
<!-- This stack panel serves as the area between the last tab and the widget control on right side of title bar (below).
It allows the region in between (if such a region exists) to be grabbed to move the window.
-->
<StackPanel Grid.Row="0" Margin="0,0,100,0" Background="Transparent" ui:WindowStyle.IsNonClientArea="True">
<c:UniformTabGrid x:Name="HeaderPanel"
Grid.Row="0"
Panel.ZIndex="1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent" ui:WindowStyle.IsNonClientArea="True">
</WrapPanel>
Background="Transparent"
ui:WindowStyle.IsNonClientArea="True"
>
</c:UniformTabGrid>
</StackPanel>
<Border Grid.Row="1" BorderThickness="0"
Padding="{Binding Path=Padding, RelativeSource={RelativeSource AncestorType=Window}}" ui:WindowStyle.IsNonClientArea="False" >
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
</Border>
<Border Grid.Row="0" Width="100" HorizontalAlignment="Right" VerticalAlignment="Stretch" Panel.ZIndex="2" Background="Transparent" ui:WindowStyle.IsNonClientArea="True">
<!-- Window widget control area-->
<Border Grid.Row="0" MinWidth="100" HorizontalAlignment="Right" Panel.ZIndex="2" Background="Transparent" ui:WindowStyle.IsNonClientArea="True">
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" Panel.ZIndex="2" Background="Transparent" Margin="5">
<Button Style="{StaticResource RoundMinimizeButton}" HorizontalAlignment="Right" Command="{x:Static SystemCommands.MinimizeWindowCommand}" />
<Button Style="{StaticResource RoundMaximizeButton}" HorizontalAlignment="Right" Command="{x:Static SystemCommands.MaximizeWindowCommand}" />
+2 -1
View File
@@ -45,7 +45,7 @@
</TabItem.RenderTransform>
<TabItem.Template>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border x:Name="bd" BorderBrush="Black" BorderThickness="0,0,0.5,0" Background="#d6d6d6" Width="{DynamicResource TabWidth}" Height="{DynamicResource TabHeight}" MinWidth="40" Margin="0" SnapsToDevicePixels="True" DockPanel.Dock="Left">
<Border x:Name="bd" BorderBrush="Black" BorderThickness="0,0,0.5,0" Background="#d6d6d6" Height="{DynamicResource TabHeight}" MinWidth="40" Margin="0" SnapsToDevicePixels="True" DockPanel.Dock="Left">
<Border.ToolTip>
<TextBlock Text="{Binding Title}" />
</Border.ToolTip>
@@ -90,6 +90,7 @@
</TabItem.Template>
<TabItem.Style>
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="MaxWidth" Value="{DynamicResource TabWidth}" />
<Setter Property="HeaderTemplate" Value="{StaticResource _template}" />
<Style.Triggers>
<DataTrigger Binding="{Binding EditMode}" Value="True">
@@ -9,6 +9,8 @@ namespace mbts.ui.shell
{
public class WorkspaceViewModel : ViewModelBase
{
private const string INITIAL_VISUAL_STATE = "Normal";
WorkspacesModel _parent;
public WorkspaceViewModel(WorkspacesModel parent, string name, WorkspaceDefinition definition) {
_parent = parent;
@@ -22,8 +24,23 @@ namespace mbts.ui.shell
get { return Icons.Window; }
}
private string _vstate = "Normal";
public string VisualState { get { return _vstate; } set { _vstate = value; RaisePropertyChanged("VisualState"); } }
private string _vstate = INITIAL_VISUAL_STATE;
public string VisualState {
get {
// this unconventional getter acts as a reset after the VisualStateManager has played
// i.e. if we overwrite a workspace then save, then close the window, when we open the
// window back up, we'll see item animate b/c it has never been reset from the animate
// state. This is necessary b/c the lifetime of Workspaces is not tied to the lifetime
// of the View, rather it lives in the Model and is only instantiated on startup via MEF
var return_val = _vstate;
_vstate = INITIAL_VISUAL_STATE;
return return_val;
}
set {
_vstate = value;
RaisePropertyChanged("VisualState");
}
}
internal WorkspaceDefinition Definition { get; private set; }
@@ -45,13 +45,13 @@ namespace mbts.ui.shell
{
clear_error("SaveName");
}
var save_candidate_already_exists = Workspaces.Any(x => x.Name == SaveName);
var save_candidate_already_exists = Workspaces.Any(x => x.Name.Replace(WorkspacesModel.SHARED_MONIKER, string.Empty) == SaveName && x.IsShared == IsPublicWorkspace);
// Save
_work_model.save_workspace(SaveName, ispublic: IsPublicWorkspace);
// if we just overwrote something, have the vm reflect it
if (save_candidate_already_exists)
Workspaces.Where(x => x.Name == SaveName).ForEach(x => x.VisualState = "Overridden");
Workspaces.Where(x => x.Name.Replace(WorkspacesModel.SHARED_MONIKER, string.Empty) == SaveName && x.IsShared == IsPublicWorkspace).ForEach(x => x.VisualState = "Overridden");
});
}
}
@@ -26,6 +26,7 @@ namespace mbts.ui.shell
{
internal const string WORKSPACE_NAMESPACE = "workspace";
internal const string ACTIVE_WORKSPACE_PREFIX = "Where I left off on ";
public const string SHARED_MONIKER = " (shared)";
public static string ACTIVE_WORKSPACE_NAME { get { return ACTIVE_WORKSPACE_PREFIX + Environment.MachineName; } }
private ISettingsCollector _settings_collector;
@@ -178,7 +179,7 @@ namespace mbts.ui.shell
public void save_workspace(string name, bool persist=true, bool ispublic=false) {
WorkspaceDefinition workspace = get_workspace();
workspace.is_shared = ispublic;
if (ispublic) { name += " (shared)"; }
if (ispublic) { name += SHARED_MONIKER; }
// Remove the old one if a workspace by this name exists already
var old = Workspaces.SingleOrDefault(w => w.Name == name && ispublic == w.IsShared);
@@ -140,7 +140,7 @@ namespace mbts.spark {
Comparer<double>.Create(_compare_unit);
public static IComparer<TickFactors> base_unit_comparer =>
Comparer<TickFactors>.Create(utils.CompareUtils.safe_comparison<TickFactors>(compare_base_unit));
Comparer<TickFactors>.Create(bts.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);
@@ -853,11 +853,11 @@ namespace mbts.spark {
LadderSettings _settings => _owner._settings;
public void refresh() {
bid_dirty = ObjectHelper.update_value_type(ref _bid, _settings.bid_break_line.show ? _owner._get_bid_market_break() : double.NaN);
ask_dirty = ObjectHelper.update_value_type(ref _ask, _settings.ask_break_line.show ? _owner._get_ask_market_break() : double.NaN);
mid_dirty = ObjectHelper.update_value_type(ref _mid, _settings.mid_break_line.show ? _owner._get_mid_market_break() : double.NaN);
session_low_dirty = ObjectHelper.update_value_type(ref _session_low, (_settings.session_low_line.show || _settings.session_range_fill.show) ? _owner._get_session_low() : double.NaN);
session_high_dirty = ObjectHelper.update_value_type(ref _session_high, (_settings.session_high_line.show || _settings.session_range_fill.show) ? _owner._get_session_high() : double.NaN);
bid_dirty = ConvertUtils.update_value_type(ref _bid, _settings.bid_break_line.show ? _owner._get_bid_market_break() : double.NaN);
ask_dirty = ConvertUtils.update_value_type(ref _ask, _settings.ask_break_line.show ? _owner._get_ask_market_break() : double.NaN);
mid_dirty = ConvertUtils.update_value_type(ref _mid, _settings.mid_break_line.show ? _owner._get_mid_market_break() : double.NaN);
session_low_dirty = ConvertUtils.update_value_type(ref _session_low, (_settings.session_low_line.show || _settings.session_range_fill.show) ? _owner._get_session_low() : double.NaN);
session_high_dirty = ConvertUtils.update_value_type(ref _session_high, (_settings.session_high_line.show || _settings.session_range_fill.show) ? _owner._get_session_high() : double.NaN);
if (session_low_dirty)
_low_index = null;
if (session_high_dirty)
@@ -871,7 +871,7 @@ namespace mbts.spark {
else new_origin = mid;
if (double.IsNaN(new_origin))
new_origin = _owner._get_bid_market_break();
origin_dirty = ObjectHelper.update_value_type(ref _origin, new_origin);
origin_dirty = ConvertUtils.update_value_type(ref _origin, new_origin);
}
}
}
@@ -144,6 +144,7 @@ namespace mbts.ui.utility.text {
StandardTimeOptionalSeconds = new Regex(@"^((\d?|0\d|1[0-2])|((\d|0\d|1[0-2])(:?|(:([0-5]?|[0-5]\d(( ?|( ([aApP]?|[aApP][mM])))(:?|(: ?)|(: [aApP]?[mM]?)|(:[0-5]?\d? ?[aApP]?[mM]?))|:([0-5]?|([0-5]\d( ?|( ([aApP]?|([aApP][mM]))))))))))))$"),
MilitaryTime = new Regex(@"^(([0-1]?[0-9])|(2?[0-3]?))(:?|:([0-5]?|[0-5][0-9]?))$"),
MilitaryTimeOptionalSeconds = new Regex(@"^((\d?|[0-1]\d|2[0-3])|((\d|[0-1]\d|2[0-3])(:?|(:([0-5]?|[0-5]\d(:?|:([0-5]?|[0-5]\d)))))))$"),
FlexibleTime = new Regex(@"^[0-9:aApPmM ]*$"),
DaysInMonth = new Regex(@"^([0-9]?|[0-2][0-9]|3[0-1])?$"),
MonthAbbrev = new Regex(@"^(?i)(a(ug?|pr?)?|d(e|ec)?|f(e?b)?|j(an?|u[nl]?)?|m(a[ry]?)?|(o(ct?)?)|s(ep?)?|n(ov?)?)?$"),
Year = new Regex(@"^\d{0,4}$"),
+3 -3
View File
@@ -259,13 +259,13 @@ namespace mbts.ui {
}
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));
(d, e) => callback(ConvertUtils.try_convert<TOwner>(d), ConvertUtils.try_convert<TValue>(e.OldValue), ConvertUtils.try_convert<TValue>(e.NewValue));
public static PropertyChangedCallback prop_changed_callback<TValue, TOwner>(Action<TOwner, TValue> callback) =>
(d, e) => callback(ObjectHelper.try_convert<TOwner>(d), ObjectHelper.try_convert<TValue>(e.NewValue));
(d, e) => callback(ConvertUtils.try_convert<TOwner>(d), ConvertUtils.try_convert<TValue>(e.NewValue));
public static CoerceValueCallback coerce_value_callback<TValue, TOwner>(Func<TOwner, TValue, TValue> coerce_value) =>
(d, obj) => coerce_value(ObjectHelper.try_convert<TOwner>(d), ObjectHelper.try_convert<TValue>(obj));
(d, obj) => coerce_value(ConvertUtils.try_convert<TOwner>(d), ConvertUtils.try_convert<TValue>(obj));
public static double control_base_width(Thickness border_thickness, Thickness padding) =>
border_thickness.Left + padding.Left + padding.Right + border_thickness.Right;
+49 -37
View File
@@ -1,4 +1,5 @@
using System;
using mbts.utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -57,8 +58,23 @@ namespace bts.collections {
_replace(node, item);
return false;
} else {
node = _init_node(item);
_insert_node(node);
if (_nursery.TryPopValue(k, out SortedList<Node> children)) {
// remove orphans from _tree (orphan-nodes must reside in the main _tree until parents arrive)
foreach (var n in children)
_tree.Remove(n);
node = new Node(item, children);
} else node = new Node(item, _comparer);
var pk = parent(item);
if (pk.Equals(_root_id))
_tree.Add(node);
else if (_index.TryGetValue(pk, out Node pnode))
pnode.children.Add(node);
else {
if (!_nursery.TryGetValue(pk, out SortedList<Node> l))
_nursery.Add(pk, l = new SortedList<Node>(_comparer));
l.Add(node);
_tree.Add(node);
}
_index.Add(k, node);
return true;
}
@@ -93,7 +109,10 @@ namespace bts.collections {
_remove_from_nursery(pk, node);
}
if (node.children.Count > 0) {
// any children left behind are orphans now, add them to the main _tree and _nursery
_nursery.Add(k, node.children);
foreach (var n in node.children)
_tree.Add(n);
}
}
@@ -115,17 +134,22 @@ namespace bts.collections {
// We are sure it is in the index because we just added it
var node = _index[id(item)];
if (_nursery.TryPopValue(id(item), out SortedList<Node> nl)) {
// remove orphans from _tree (orphan-nodes must reside in the main _tree until parents arrive)
foreach (var n in nl)
_tree.Remove(n);
if (node.children.Count == 0) {
node.children = nl;
} else if (node.children.Count > nl.Count) {
// merge nl into node.children
foreach (var cn in nl)
node.children.Add(cn);
} else {
// merge node.children into nl
foreach (var cn in node.children)
nl.Add(cn);
node.children = nl;
if (node.children.Count > nl.Count) {
// merge nl into node.children
foreach (var cn in nl)
node.children.Add(cn);
} else {
// merge node.children into nl
foreach (var cn in node.children)
nl.Add(cn);
node.children = nl;
}
}
}
TKey pk = parent(item);
@@ -137,6 +161,7 @@ namespace bts.collections {
if (!_nursery.TryGetValue(pk, out nl))
_nursery.Add(pk, nl = new SortedList<Node>(_comparer));
nl.Add(node);
_tree.Add(node);
}
}
}
@@ -172,7 +197,6 @@ namespace bts.collections {
} else if (!_nursery.TryGetValue(pk, out node_list)) {
throw new KeyNotFoundException($"Replace was called with a parent-key ({pk}) that was not found in _index or _nursery; item-key was ({id(newval)}).");
}
//var node_list = pk.Equals(_root_id) ? _tree : _index.TryGetValue(pk, out Node pnode) ? pnode.children : _nursery[pk];
node_list.Remove(node);
node.value = newval;
node_list.Add(node);
@@ -187,32 +211,24 @@ namespace bts.collections {
}
}
node.value = newval;
_insert_node(node);
}
}
Node _init_node(T item) =>
_nursery.TryPopValue(id(item), out SortedList<Node> nursery_list) ? new Node(item, nursery_list) : new Node(item, _comparer);
/// <summary>
/// Inserts the node into the appropriate SortedList (i.e. roots, parent.inner, or nursery), but does NOT add it to <see cref="_index"/>, NOR check the nursery for any child-nodes waiting on this item.
/// </summary>
void _insert_node(Node node) {
TKey pk = parent(node.value);
if (pk.Equals(_root_id)) {
_tree.Add(node);
} else if (_index.TryGetValue(pk, out Node pnode)) {
pnode.children.Add(node);
} else {
if (!_nursery.TryGetValue(pk, out SortedList<Node> nursery_list)) {
_nursery.Add(pk, nursery_list = new SortedList<Node>(_comparer));
if (pk.Equals(_root_id)) {
_tree.Add(node);
} else if (_index.TryGetValue(pk, out Node pnode)) {
pnode.children.Add(node);
} else {
if (!_nursery.TryGetValue(pk, out SortedList<Node> nursery_list)) {
_nursery.Add(pk, nursery_list = new SortedList<Node>(_comparer));
}
nursery_list.Add(node);
_tree.Add(node);
}
nursery_list.Add(node);
}
}
void _remove_from_nursery(TKey pk, Node node) =>
void _remove_from_nursery(TKey pk, Node node) {
_nursery.TryPopValueIf(pk, nl => nl.Remove(node) && nl.Count == 0);
_tree.Remove(node);
}
IEnumerable<T> iterate(SortedList<Node> nodes) {
foreach (var n in nodes) {
@@ -259,10 +275,6 @@ namespace bts.collections {
public IEnumerator<T> GetEnumerator() {
foreach (T item in iterate(_tree))
yield return item;
foreach (var kv in _nursery) {
foreach (T item in iterate(kv.Value))
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator() {
@@ -2,8 +2,14 @@
using System.Collections;
using System.Collections.Generic;
namespace mbts.utils {
namespace bts.utils {
public static class CompareUtils {
public static int safe_compare<T>(this T a, T b, Func<T, T, int> compare) =>
a == null ? (b == null ? 0 : -1) : (b == null ? 1 : compare(a, b));
public static int safe_compare<T>(this T a, T b) where T : IComparable<T> =>
a == null ? (b == null ? 0 : -1) : (b == null ? 1 : a.CompareTo(b));
#region Equality
public static bool NullCheckEquals<T>(T x, T y) =>
NullCheckEquals(x, y, (_x, _y) => _x.Equals(_y));
@@ -41,7 +47,7 @@ namespace mbts.utils {
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));
_comparer.Compare(ConvertUtils.try_convert<T>(x), ConvertUtils.try_convert<T>(y));
}
#endregion
}
@@ -1,9 +1,5 @@
using mbts.ui.utility;
namespace System
{
public static class ObjectHelper
{
namespace System {
public static class ConvertUtils {
public static object try_convert(Type type, object obj) {
try_convert(type, obj, out object result);
return result;
@@ -20,7 +16,7 @@ namespace System
result = obj;
return true;
} else if (type.IsEnum && obj is string) {
result = EnumHelper.try_parse(type, obj.ToString());
result = try_parse_enum(type, obj.ToString());
return true;
} else if (type.IsNullable()) {
return try_convert(type.GetGenericArguments()[0], obj, out result);
@@ -85,6 +81,17 @@ namespace System
return rv;
}
public static object try_parse_enum(Type enum_type, string value, bool ignore_case = true) {
if (!enum_type.IsEnum) throw new ArgumentException("Must an enum type.", nameof(enum_type));
Array vals = Enum.GetValues(enum_type);
var str_comp = ignore_case ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture;
foreach (object v in vals) {
if (string.Equals(v.ToString(), value, str_comp))
return v;
}
return Activator.CreateInstance(enum_type);
}
public static bool update_value_type<T>(ref T field, T newval) where T : struct, IEquatable<T> {
bool rv = field.Equals(newval);
field = newval;
@@ -1,5 +1,6 @@
using bts.collections;
using bts.test;
using bts.utils;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -81,21 +82,6 @@ namespace bts.tests {
int id(Order order) => order.id;
int pid(Order order) => order.parent_id;
/// <summary>
/// This test has been made obsolete by <see cref="test_sorted_tree_state_transitions"/>; leaving it in place until the latter has been code-reviewed.
/// </summary>
[Test]
public void test_sordid_tree() {
for (int x = 0; x < 10; ++x) {
var input = orders.ToList();
input.Shuffle();
var tree_asc = new SortedTree<Order, int>(Order.name_asc, id, pid, input);
var tree_desc = new SortedTree<Order, int>(Order.name_desc, id, pid, input);
Assert.IsTrue(tree_asc.SequenceEqual(orders));
Assert.IsTrue(tree_desc.SequenceEqual(orders_desc));
}
}
[Test]
public void test_enumerate_group() {
var comparer = Comparer<Order>.Create((l, r) => l.name.CompareTo(r.name));
@@ -151,17 +137,20 @@ namespace bts.tests {
// 1) Constructor Initialization
var tree_asc = new SortedTree<Order, int>(Order.name_asc, id, pid, input);
var tree_desc = new SortedTree<Order, int>(Order.name_desc, id, pid, input);
assert_populated_then_clear();
assert_state_then_clear(tree_asc, input, Order.name_asc);
assert_state_then_clear(tree_desc, input, Order.name_desc);
// 2) Insert
foreach (var o in input) {
tree_asc.Insert(o);
tree_desc.Insert(o);
}
assert_populated_then_clear();
assert_state_then_clear(tree_asc, input, Order.name_asc);
assert_state_then_clear(tree_desc, input, Order.name_desc);
// 3) Reset
tree_asc.Reset(input);
tree_desc.Reset(input);
assert_populated_then_clear();
assert_state_then_clear(tree_asc, input, Order.name_asc);
assert_state_then_clear(tree_desc, input, Order.name_desc);
// 4) Replace
tree_asc.Reset(input);
tree_desc.Reset(input);
@@ -170,28 +159,12 @@ namespace bts.tests {
Assert.IsTrue(tree_asc.TryReplace(id(v), _ => v));
Assert.IsTrue(tree_desc.TryReplace(id(v), _ => v));
}
// verify asc
var (expected_asc, expected_nursery_asc) = _build_tree(modified_input, Order.name_asc);
assert_sequence_then_clear(tree_asc, expected_asc, expected_nursery_asc);
// verify desc
var (expected_desc, expected_nursery_desc) = _build_tree(modified_input, Order.name_desc);
assert_sequence_then_clear(tree_desc, expected_desc, expected_nursery_desc);
// DONE!! (local methods)
void assert_populated_then_clear() {
Assert.IsTrue(tree_asc.SequenceEqual(orders));
Assert.IsTrue(tree_desc.SequenceEqual(orders_desc));
clear_action(tree_asc);
clear_action(tree_desc);
Assert.IsFalse(tree_asc.Any());
Assert.IsFalse(tree_desc.Any());
}
// verify asc/desc
assert_state_then_clear(tree_asc, modified_input, Order.name_asc);
assert_state_then_clear(tree_desc, modified_input, Order.name_desc);
void assert_sequence_then_clear(SortedTree<Order, int> tree, List<Order> expected_items, HashSet<int> expected_nursery) {
var tree_list = tree.ToList();
Assert.AreEqual(tree_list.Count, expected_items.Count + expected_nursery.Count);
Assert.IsTrue(tree_list.Take(expected_items.Count).SequenceEqual(expected_items));
// nursery order is non-deterministic, just make sure they are equivalent id-sets.
Assert.IsTrue(new HashSet<int>(tree_list.Skip(expected_items.Count).Select(id)).SetEquals(expected_nursery));
void assert_state_then_clear(SortedTree<Order, int> tree, IEnumerable<Order> vals, IComparer<Order> comp) {
Assert.IsTrue(tree.SequenceEqual(_hierarchically_sorted_list(vals, comp)));
clear_action(tree);
Assert.IsFalse(tree.Any());
}
@@ -199,6 +172,74 @@ namespace bts.tests {
}
}
[Test]
public void test_nursery() {
var tree = new SortedTree<Order, int>(Order.name_asc, id, pid);
int next_id = orders.Max(id) + 1;
var parent = new Order('a', next_id++, Order.no_parent);
// add 2 generations of orphans (in the test loop, these orphans will ALWAYS be added before there parent, so they aren't orphans persay, more just with a babysitter).
// gen 1:
var orphans = new List<Order> {
new Order('b', next_id++, parent.id),
new Order('c', next_id++, parent.id)
};
// gen 2: (Warning: there are some "magic" numbers here...this is a unit test, with fake data, so they are hard to avoid entirely).
orphans.Add(new Order('d', next_id++, id(orphans[0])));
orphans.Add(new Order('e', next_id++, id(orphans[1])));
// let's add 1 orphan who's parent will NEVER arrive, unlike the orphans added above
orphans.Add(new Order('x', next_id++, next_id++));
var expected_result = _hierarchically_sorted_list(orders.Concat(orphans), Order.name_asc);
// insert orphans at every position with respect to orders
int count = 0;
while (count++ < 10) {
for (int i = 0; i < orders.Length; i++) {
var input = orders.ToList();
input.Shuffle();
orphans.Shuffle();
for (int j = 0; j < input.Count; j++) {
if (j == i) {
foreach (var o in orphans)
tree.Insert(o);
}
tree.Insert(input[j]);
}
Assert.IsTrue(tree.SequenceEqual(expected_result));
tree.Clear();
Assert.IsFalse(tree.Any());
}
}
}
List<Order> _hierarchically_sorted_list(IEnumerable<Order> input, IComparer<Order> comp) {
var node_index = input.ToDictionary(id, v => new TestNode(v));
var tree_dict = new Dictionary<int, TestNode>();
foreach (var o in input) {
if (pid(o).Equals(Order.no_parent)) {
tree_dict.Add(id(o), node_index[id(o)]);
} else {
if (node_index.TryGetValue(pid(o), out TestNode pnode))
pnode.inner.Add(id(o), node_index[id(o)]);
else {
tree_dict.Add(id(o), node_index[id(o)]);
}
}
}
// build flat sorted list
var flat_sorted = new List<Order>();
var node_comp = comp.wrap<Order, TestNode>(n => n.value);
insert_vals(tree_dict);
return flat_sorted;
// local methods
void insert_vals(Dictionary<int, TestNode> map) {
var sorted = map.Values.ToList();
sorted.Sort(node_comp);
foreach (var n in sorted) {
flat_sorted.Add(n.value);
insert_vals(n.inner);
}
}
}
List<Order> _randomize_values(List<Order> input) {
// shuffle ids
var rand_ids = input.Select(id).ToList();
@@ -224,47 +265,18 @@ namespace bts.tests {
return reshuffled;
}
/// <summary>
/// Returns a list of Orders, hierarchically sorted, and a HashSet of Order-ids whose parent is not present. Parentless nodes are NOT included in the list of values.
/// </summary>
(List<Order> sorted_vals, HashSet<int> nursery) _build_tree(IEnumerable<Order> items, IComparer<Order> comp) {
var roots = new List<TestNode>();
var nursery_map = new Dictionary<int, Dictionary<int, TestNode>>();
var raw_item_map = items.ToDictionary(id);
var node_map = new Dictionary<int, TestNode>();
foreach (var v in items) {
int k = id(v);
int pk = pid(v);
TestNode node;
if (nursery_map.TryGetValue(k, out Dictionary<int, TestNode> inner)) {
// adopt orphans from nursery
node = new TestNode(v, inner);
nursery_map.Remove(k);
} else {
node = new TestNode(v);
}
node_map.Add(k, node);
if (pk.Equals(Order.no_parent)) {
roots.Add(node);
} else if (node_map.TryGetValue(pk, out TestNode pnode)) {
pnode.inner.Add(k, node);
} else {
if (!nursery_map.TryGetValue(pk, out Dictionary<int, TestNode> ndict)) {
nursery_map.Add(pk, ndict = new Dictionary<int, TestNode>());
}
ndict.Add(k, node);
}
void _write_tree_to_console(SortedTree<Order, int> tree) {
foreach (var str in _fmt_output(tree.RootNodes))
Console.WriteLine(str);
}
IEnumerable<string> _fmt_output(IEnumerable<INode<Order>> nodes) => _fmt_output(nodes, 0);
IEnumerable<string> _fmt_output(IEnumerable<INode<Order>> en, int depth) {
foreach (var curr in en) {
yield return new string('-', depth * 4) + curr.value.ToString();
foreach (var str in _fmt_output(curr.inner, depth + 1))
yield return str;
}
return (roots.OrderBy(n => n.value, comp)
.SelectMany(iterate_inner_vals).ToList(),
new HashSet<int>(nursery_map.Values
.SelectMany(d => d.Values.SelectMany(iterate_inner_vals))
.Select(id)));
// local methods
IEnumerable<Order> iterate_inner_vals(TestNode n) =>
new[] { n.value }.Concat(
n.inner.Values.OrderBy(cn => cn.value, comp)
.SelectMany(iterate_inner_vals));
}
class TestNode {
@@ -279,4 +291,4 @@ namespace bts.tests {
public readonly Dictionary<int, TestNode> inner;
}
}
}
}
+13 -3
View File
@@ -1,14 +1,14 @@
<html>
<head>
<title>BTS Spark Socket To Socket Requote Times</title>
<title>BTS Spark Socket To Socket Requote and Hedge Times</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/perf_trace/compiled.css">
<script src="https://cdn.polyfill.io/v2/polyfill.js?features=es6"></script>
<style type="text/css">
.summary-block {
margin: 2em 1em;
padding: 1em 1em;
}
.summary-block > ul {
margin: 0em 1em;
@@ -23,6 +23,16 @@
.clear {
clear:both;
}
.session-table tr {
padding-top: 1em;
}
.session-table th {
background-color: #4169AA;
color:#fff;
text-align: center;
padding: 1em;
border-left: 1px solid #f6f6f6;
}
</style>
</head>
@@ -47,7 +57,7 @@
</ul>
</div-->
<div style="margin:2em 0 0 2em;"><!--div class="is-tab-content" is-tab-content is shorter than the screen to accommodate tabs at the top -->
<h1 class="is-size-2">Socket To Socket Requote Times</h1>
<h1 class="is-size-2">Socket To Socket Latencies</h1>
<div id="output"></div>
</div>
</div>
+199 -68
View File
@@ -2,7 +2,9 @@ $( document ).ready(function (){
let urlParams = new URLSearchParams(window.location.search);
num_rows = urlParams.get('r');
let show_rows = urlParams.get('showrows');
let rows_of = urlParams.get('rowsof');
//let trace_data_url = "./perf_trace_data";
let trace_data_url = "/perf_trace_data";
if(num_rows != null) {
trace_data_url += "/" + num_rows;
@@ -15,14 +17,50 @@ $( document ).ready(function (){
error: ajax_err,
crossDomain: true
}).done(function(result) {
if(show_rows != null) {
list_rows(show_rows, result);
if(rows_of === "quot") {
list_rows(show_rows, rows_of, result.requotes);
}
else {
list_rows(show_rows, rows_of, result.hedges);
}
}
else {
if(result.length > 0){
let summary_info = "<h1 class='is-size-3'><b>Host:</b> " + result[0].host + "</h1>";
if((result.requotes.length + result.hedges.length) > 0) {
let useful_result = (result.requotes.length > 0 ? result.requotes : result.hedges);
let less_useful_result = (result.requotes.length === 0 ? result.requotes : result.hedges);
let summary_info = "<h1 class='is-size-3'><b>Host:</b> " + useful_result[0].host + "</h1><br>";
$("#output").html(summary_info);
$(result).each(display_session);
let whole_result = result.requotes.concat(result.hedges);
let unique_sessions = [];
whole_result.filter(function(value) {
if (unique_sessions.find(function(elm) {
return elm.session_name === value.session_name;
}) === undefined) {
unique_sessions.push(value);
}
});
$(unique_sessions).each(function (index, sess_val){
console.log(sess_val.session_name);
let r = result.requotes.find(function(elm){
return elm.session_name === sess_val.session_name;
});
let h = result.hedges.find(function(elm){
return elm.session_name === sess_val.session_name;
});
display_session(r, h);
});
}
else {
}
}
@@ -30,29 +68,30 @@ $( document ).ready(function (){
});
function list_rows(show_rows, data) {
function list_rows(show_rows, rows_of, data) {
$.each(data, function(index, value){
if(value.session_name === show_rows){
let summary_info = "<table class='table bts-data-table'>";
summary_info += "<thead><tr><th>Trace Time</th>" +
"<th>Order Key</th>" +
"<th>MD</th>" +
"<th>Spreader</th>" +
"<th>Fix</th>" +
"<th>Total</th></tr></thead><tbody>";
summary_info += "<thead><tr><th>Trace Time</th>"
$.each(value.rows[0], function(key, unval){
if(key !== "trace_time" && key !== "total")
{
summary_info +="<th>" + format_title(key) + "</th>";
}
});
summary_info += "<th>Total</th></tr></thead><tbody>";
$.each(value.rows, function(key, val) {
summary_info += "<tr>";
console.log(val);
summary_info += "<td>" + val.trace_time + "</td>" +
"<td>" + val.order_key + "</td>" +
"<td>" + val.md + "</td>" +
"<td>" + val.spreader + "</td>" +
"<td>" + val.fix + "</td>" +
"<td>" + val.total + "</td>";
summary_info += "<td>" + val.trace_time + "</td>";
$.each(val, function(key, uval){
if(key !== "trace_time" && key !== "total")
{
summary_info += "<td>" + uval + "</td>";
}
});
summary_info += "<td>" + val.total + "</td>";
summary_info += "</tr>";
});
summary_info += "</tbody></table>";
@@ -77,7 +116,26 @@ function ajax_err (jqXHR, textStatus,errorThrown) {
function first_upper (str)
{
let ret_str = str[0].toUpperCase() + str.substring(1, str.length);
return ret_str
return ret_str;
}
function format_title (str)
{
let ret_str = "";
for(let i=0; i < str.length; i++) {
if(i === 0 || str[i-1] === "_") {
ret_str += str[i].toUpperCase();
}
else if(str[i] === "_") {
ret_str += " ";
}
else {
ret_str += str[i];
}
}
return ret_str;
}
function generate_histogram(id, data)
@@ -95,70 +153,143 @@ function generate_histogram(id, data)
Plotly.newPlot(id, plot_data);
}
function display_session (index, value) {
//console.log(value);
let session_info = "<ul class=''>";
session_info += "<li id='" + value.session_name + "' class='is-size-5'><b>Session:</b> " + value.session_name + "</li>";
session_info += "<li><b>Build Type:</b> " + value.build_type + "</li>";
session_info += "<li><b>Version:</b> " + value.version + "</li>";
function write_summary (histo_id, v)
{
session_info = "<ul>";
session_info += "<li><b>" + format_title(v.value) + "</b></li>";
session_info += "<li>Min: " + v.min + "</li>";
session_info += "<li>Median: " + v.median + "</li>";
session_info += "<li>Max: " + v.max + "</li>";
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
session_info += "</ul>";
session_info += "<div id='" + histo_id + "' class='histogram-div'></div>"
return session_info;
}
function display_session (quot_val, hed_val) {
let safe_quot_val = (quot_val != undefined) ? quot_val : {
session_name: hed_val.session_name,
build_type: hed_val.build_type,
version: hed_val.version,
n_rows: 0,
total: 0,
summaries: []
};
let safe_hed_val = (hed_val != undefined) ? hed_val : {
session_name: quot_val.session_name,
build_type: quot_val.build_type,
version: quot_val.version,
n_rows: 0,
total: 0,
summaries: []
};
let session_info = "<table class='session-table'>";
session_info += "<tr><td colspan='2' id='" + safe_quot_val.session_name + "' class='is-size-5'><b>Session:</b> " + safe_quot_val.session_name + "<br>";
session_info += "<b>Build Type:</b> " + safe_quot_val.build_type + "<br>";
session_info += "<b>Version:</b> " + safe_quot_val.version + "<br><br></td></tr>";
//column headers
session_info += "<tr><th>Requotes</th><th>Hedges</th></tr>"
if(num_rows != null) {
session_info += "<li><a href='?showrows=" + value.session_name + "&r=" + num_rows + "'><b>Rows:</b> " + value.n_rows + "</a></li>";
}
else {
session_info += "<li><a href='?showrows=" + value.session_name + "'><b>Rows:</b> " + value.n_rows + "</a></li>";
}
session_info += "<tr><td ><a href='?showrows=" + safe_quot_val.session_name + "&rowsof=quot" + "&r=" + num_rows + "'><b>Rows:</b> " + safe_quot_val.n_rows + "</a></td>";
session_info += "<td><a href='?showrows=" + safe_hed_val.session_name + "&rowsof=hed" + "&r=" + num_rows + "'><b>Rows:</b> " + safe_hed_val.n_rows + "</a></td></tr>";
}
else {
session_info += "<tr><td><a href='?showrows=" + safe_quot_val.session_name + "&rowsof=quot" + "'><b>Rows:</b> " + safe_quot_val.n_rows + "</a></td>";
session_info += "<td><a href='?showrows=" + safe_hed_val.session_name + "&rowsof=hed" + "'><b>Rows:</b> " + safe_hed_val.n_rows + "</a></td></tr>";
}
$(value.summaries).each(function(i,v){
session_info += "<tr><td class='summary-block'>";
$(safe_quot_val.summaries).each(function(i,v){
if(v.value === "total") {
session_info += "<li class='summary-block'><ul>";
session_info += "<li><b>" + first_upper(v.value) + "</b></li>";
session_info += "<li>Min: " + v.min + "</li>";
session_info += "<li>Median: " + v.median + "</li>";
session_info += "<li>Max: " + v.max + "</li>";
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
session_info += "</ul>";
session_info += "<div id='" + value.session_name + "-" + v.value + "' class='histogram-div'></div>"
session_info += "</li>";
return false
session_info += write_summary(safe_quot_val.session_name + "-quot-" + v.value, v);
return false
}
});
});
$(value.summaries).each(function(i,v){
if(v.value !== "total") {
session_info += "<li class='summary-block'><ul>";
session_info += "<li><b>" + first_upper(v.value) + "</b></li>";
session_info += "<li>Min: " + v.min + "</li>";
session_info += "<li>Median: " + v.median + "</li>";
session_info += "<li>Max: " + v.max + "</li>";
session_info += "<li>99%: " + v.ninenty_nine + "</li>";
session_info += "<li>Standard Deviation: " + v.stdev + "</li>";
session_info += "</ul>";
session_info += "<div id='" + value.session_name + "-" + v.value + "' class='histogram-div'></div>"
session_info += "</li>";
session_info += "</td>";
session_info += "<td class='summary-block'>";
$(safe_hed_val.summaries).each(function(i,v){
if(v.value === "total") {
session_info += write_summary(safe_hed_val.session_name + "-hed-" + v.value, v);
return false
}
});
});
session_info += "</ul><hr class='clear'>";
session_info += "</td></tr>";
let loop_val = 0;
if(safe_quot_val.summaries.length >= safe_hed_val.summaries.length ) {
loop_val = safe_quot_val.summaries.length;
}
else {
loop_val = safe_hed_val.summaries.length;
}
/*
for(let i = 0; i < loop_val; i++) {
session_info += "<tr><td class='summary-block'>";
if (safe_quot_val.summaries[i] !== undefined && safe_quot_val.summaries[i].value !== "total") {
session_info += write_summary(safe_quot_val.session_name + "-quot-" + safe_quot_val.summaries[i].value, safe_quot_val.summaries[i]);
}
session_info += "</td><td class='summary-block'>";
if (safe_hed_val.summaries[i] !== undefined && safe_hed_val.summaries[i].value !== "total") {
session_info += write_summary(safe_hed_val.session_name + "-hed-" + safe_hed_val.summaries[i].value, safe_hed_val.summaries[i]);
}
session_info += "</td></tr>";
}
*/
session_info += "</table><hr class='clear'>";
$("#output").append(session_info);
let all_data = {};
let quot_val_data = {};
$(value.rows).each(function(i, row) {
$(safe_quot_val.rows).each(function(i, row) {
$.each(row, function(name, v) {
if(all_data[name] === undefined){
all_data[name] = [v];
if(quot_val_data[name] === undefined){
quot_val_data[name] = [v];
}
else {
all_data[name].push(v);
quot_val_data[name].push(v);
}
});
});
//console.log(all_data);
let hed_val_data = {};
$(value.summaries).each(function(i,v){
generate_histogram(value.session_name + "-" + v.value, all_data[v.value]);
$(safe_hed_val.rows).each(function(i, row) {
$.each(row, function(name, v) {
if(hed_val_data[name] === undefined){
hed_val_data[name] = [v];
}
else {
hed_val_data[name].push(v);
}
});
});
if(safe_quot_val.summaries.length > 0){
generate_histogram(safe_quot_val.session_name + "-quot-total", quot_val_data["total"]);
}
/*
$(safe_quot_val.summaries).each(function(i,v){
generate_histogram(safe_quot_val.session_name + "-quot-" + v.value, quot_val_data[v.value]);
});
*/
if(safe_hed_val.summaries.length > 0){
generate_histogram(safe_hed_val.session_name + "-hed-total", hed_val_data["total"]);
}
/*
$(safe_hed_val.summaries).each(function(i,v){
generate_histogram(safe_hed_val.session_name + "-hed-" + v.value, hed_val_data[v.value]);
});
*/
}
+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/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.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/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.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/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.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;domain_model/tests/domain_tests.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/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.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/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.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;utils/stats.cpp;utils/thread_manager.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/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.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/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.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/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.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;domain_model/tests/domain_tests.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/MDPeriodicDumper.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/cme/MDP3Utils.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.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/catch_test_runner.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.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;utils/stats.cpp;utils/thread_manager.cpp)
@@ -4,7 +4,10 @@
#include "lib/config/protos/cme_reference_data.pb.h"
#include "lib/config/protos/jetfix.pb.h"
#include "lib/config/config.h"
#include "lib/config/protos/system.pb.h"
#include <sstream>
#include <boost/algorithm/string/case_conv.hpp>
namespace bts{
namespace admin{
@@ -83,8 +86,7 @@ AdminServiceUtils::resolve_cme_connection(const bts::api::admin::AdminData& data
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);
const bts::api::Security& ){
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);
@@ -100,24 +102,30 @@ AdminServiceUtils::resolve_connection(const bts::api::admin::AdminData& data,
const bts::api::Security& sec){
const bts::api::admin::ExchangeConnection* rv = nullptr;
switch(sec.exchange()){
case api::Venue::VENUE_GLBX:
rv = resolve_cme_connection(data, account, sec);
break;
case api::Venue::VENUE_UNSET:
case api::Venue::VENUE_BTS:
case api::Venue::VENUE_SIM:
case api::Venue::VENUE_GLBX: {
auto deployment_type =
boost::to_lower_copy(config::get<config::DeploymentType>().type());
rv = resolve_cme_connection(data, account, sec);
if (rv == nullptr && (deployment_type == "dev" ||
deployment_type == "demo"))
rv = resolve_sim_connection(data, account, sec);
break;
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_:
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:
case api::Venue::Venue_INT_MIN_SENTINEL_DO_NOT_USE_:
case api::Venue::Venue_INT_MAX_SENTINEL_DO_NOT_USE_:
rv = nullptr;
break;
rv = nullptr;
break;
}
return rv;
}
}
////////////////////////////////////////////
// connection mutators /////////////////////
////////////////////////////////////////////
+6
View File
@@ -141,6 +141,12 @@ message BTSAccount{
AccountSecurityClipLimits security_clip_limits = 90;
AccountExchangeConnections connections = 100;
ExchangeAccountData exchange_account_data = 110;
// extra parameters for risk enforcement
RiskLimitEnforcement risk_limit_enforcement=120;
}
message RiskLimitEnforcement{
bool product_limit_in_net_terms = 10;
}
message AccountProductPositionLimitUnit{
+3 -11
View File
@@ -204,7 +204,7 @@ message OrderState {
CoverOrderState cover = 22;
OffsetOrderState offset = 24;
DynamicOrderState dynamic = 1000;
DynamicOrderState dynamic = 255; // do not use anything bigger than 255, or things will break
}
}
@@ -267,7 +267,7 @@ message LimitOrderState {NativeOrderState native_state = 1;}
message StopOrderState {NativeOrderState native_state = 1;}
message StopLimitOrderState {NativeOrderState native_state = 1;}
message IcebergOrderState {}
message SpreaderOrderState {SpreaderChildKind child_kind = 1;}
message SpreaderOrderState {}
message TimeBoxOrderState {}
message TickOrderState {}
message LeanOrderState {}
@@ -276,9 +276,7 @@ message TopOrderState {}
message TriggerOrderState {}
message CoverOrderState {}
message OffsetOrderState {}
message DynamicOrderState {
map<string,string> state = 1;
}
message DynamicOrderState {}
enum OrderStatus {
@@ -403,12 +401,6 @@ enum ExecType
EXEC_TYPE_TRADE_CANCEL = 72 ;
}
enum SpreaderChildKind {
SPREADER_CHILD_KIND_UNDEFINED = 0;
SPREADER_CHILD_KIND_SPREADER_HEDGING = 1;
SPREADER_CHILD_KIND_SPREADER_QUOTING = 2;
}
enum OrderError
{
ORDER_ERROR_UNDEFINED = 0;
-1
View File
@@ -76,7 +76,6 @@ message CMEOptions
// Defines amount of time Feed Engine keeps cycling before going to sleep when no useful activity can be done
optional uint32 spin_time = 4 [default = 1];
optional uint32 channels_per_thread = 5 [default = 3];
optional string thread_group = 10 [default=""]; // if present this will be used to retrieve affinity settings from Threads config key
optional uint32 udp_socket_buffer_size = 11 [default = 33554432]; //32mb
}
message CMEChannel {
+1 -1
View File
@@ -64,6 +64,6 @@ message ShellPingOptions {
// 4000 is default Windows ping timeout as specified here:
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490968(v=technet.10)
optional int32 timeout_milliseconds_threshold = 3 [default = 4000];
optional int32 logging_milliseconds_threshold = 4 [default = 25];
optional int32 logging_milliseconds_threshold = 4 [default = 50];
optional int32 maximum_backoff_milliseconds = 5 [default = 60000];
}
+1
View File
@@ -8,4 +8,5 @@ message OrdermanCfg {
repeated string hosted_actor = 3;
}
repeated Executor executor = 1;
optional uint32 default_iceberg_pause_secs = 2 [default = 0];
}
@@ -7,6 +7,6 @@ message OrdersSoftCleanupCutoff{
message SODTime{
// utc time
optional uint32 sod_hh = 20 [default=15];
optional uint32 sod_hh = 20 [default=16];
optional uint32 sod_mm = 30 [default=30];
}
@@ -163,6 +163,7 @@ void AsyncReader::dispatch ()
for (auto &msg : framed_msgs) {
msg.set_trace(trace);
LOG(Latency, Diag, "perf_event AsyncReader.on_fix");
tracepoint(bts_trace, trace_order, (char *)"AsyncReader.on_fix", 0,
msg.get_seq_num(), 0);
me->sink.in_msg_raw (msg);
+14 -11
View File
@@ -6,7 +6,6 @@
#include "lib/utils/TimeStamp.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/helper_macros.h"
#include "lib/orderman/orderman_utils/PerfTraceLog.h"
#include <arpa/inet.h>
#include <fstream>
@@ -84,6 +83,7 @@ int SocketConnection::read(char *to_buffer, size_t read_count) {
bts_assert(_fd >= 0);
auto result = ::read(_fd, to_buffer, read_count);
LOG(Latency, Diag, "perf_event after_socket_read");
if (_log && result > 0) {
_in_log.write(to_buffer, result);
_in_log.flush();
@@ -123,18 +123,20 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
trace.md_sn, s2s.microseconds());
}
LOG(Latency, Diag, "perf_event before_socket_write");
auto result = ::writev(_fd, iv, (int)vec_size);
if (trace.md.to_uint64()) {
LOG(Latency, Info,
"perf_event:tick_to_order s2s_us:{} fix_socket_r:{} md:{} "
"perf_event:tick_to_order s2s_us:{} do_work_begin:{} fix_socket_r:{} "
"md:{} "
"md_src:{} spreader_on_book:{} spreader_modify_order:{} "
"spreader_on_child_order_update:{} "
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{} order_key:{} "
"send_fix_buffer:{}",
s2s.microseconds(), trace_cpy.fix_socket_r.to_uint64(),
trace_cpy.md.to_uint64(), trace_cpy.md_src.to_uint64(),
trace_cpy.spreader_on_book.to_uint64(),
s2s.microseconds(), trace_cpy.do_work_begin.to_uint64(),
trace_cpy.fix_socket_r.to_uint64(), trace_cpy.md.to_uint64(),
trace_cpy.md_src.to_uint64(), trace_cpy.spreader_on_book.to_uint64(),
trace_cpy.spreader_modify_order.to_uint64(),
trace_cpy.spreader_on_child_order_update.to_uint64(),
trace_cpy.order_update.to_uint64(), trace_cpy.exe_gw.to_uint64(),
@@ -145,19 +147,20 @@ int SocketConnection::write(WriteVec const &vec, size_t vec_size,
if (trace.fix_socket_r.to_uint64()) {
LOG(Latency, Info,
"perf_event:fill_to_order s2s_us:{} fix_socket_r:{} md:{} md_src:{} "
"perf_event:fill_to_order s2s_us:{} do_work_begin:{} fix_socket_r:{} "
"md:{} md_src:{} "
"spreader_on_book:{} spreader_modify_order:{} "
"spreader_on_child_order_update:{} "
"order_update:{} exe_gw:{} fix_socket_w:{} md_sn:{} order_key:{} "
"send_fix_buffer:{} ",
s2s.microseconds(), trace_cpy.fix_socket_r.to_uint64(),
trace_cpy.md.to_uint64(), trace_cpy.md_src.to_uint64(),
trace_cpy.spreader_on_book.to_uint64(),
s2s.microseconds(), trace_cpy.do_work_begin.to_uint64(),
trace_cpy.fix_socket_r.to_uint64(), trace_cpy.md.to_uint64(),
trace_cpy.md_src.to_uint64(), trace_cpy.spreader_on_book.to_uint64(),
trace_cpy.spreader_modify_order.to_uint64(),
trace_cpy.spreader_on_child_order_update.to_uint64(),
trace_cpy.order_update.to_uint64(), trace_cpy.exe_gw.to_uint64(),
trace_cpy.fix_socket_w.to_uint64(), trace_cpy.md_sn, trace.order_key,
trace.send_fix_buffer.to_uint64());
trace_cpy.fix_socket_w.to_uint64(), trace_cpy.md_sn, trace_cpy.order_key,
trace_cpy.send_fix_buffer.to_uint64());
_perf_log.log(trace_cpy);
}
+2
View File
@@ -394,6 +394,7 @@ void CoreGateway::sync_reader_disconnected(EventData const &) {
}
void CoreGateway::sync_in_msg_raw(EventData const &ev) {
LOG(Latency, Diag, "perf_event sync_in_msg_raw");
switch (me->state) {
case State::OFF:
case State::PAUSED:
@@ -412,6 +413,7 @@ void CoreGateway::sync_in_msg_raw(EventData const &ev) {
}
void CoreGateway::sync_in_msg_sequenced(EventData const &ev) {
LOG(Latency, Diag, "perf_event sync_in_msg_sequenced");
switch (me->state) {
case State::OFF:
case State::PAUSED:
+2
View File
@@ -83,8 +83,10 @@ private:
void dispatch (InMsgBuffer const&in_msg) override
{
LOG(Latency, Diag, "perf_event JetFIXGateway::before_parse");
msg.read (in_msg.data (),in_msg.size ());
msg.set_trace(in_msg.trace());
LOG(Latency, Diag, "perf_event JetFIXGateway::after_parse");
handler (msg);
}
+1
View File
@@ -467,6 +467,7 @@ void Session::on_hb(InMsgBuffer const &msg) {
}
void Session::on_app(InMsgBuffer const &msg) {
LOG(Latency, Diag, "perf_event on_app");
switch (me->state) {
case State::WLOGON:
terminate_session(
+9
View File
@@ -54,6 +54,15 @@ struct LoggerHandle {
Logger *logger;
};
struct CharPtrWrapper
{const char* ptr;};
inline std::ostream& operator<<(std::ostream&s,CharPtrWrapper const&ptr)
{
s << ptr.ptr;
return s;
}
extern thread_local LoggerHandle _thread_logger;
void configure_logging(LoggingConfig const&cfg);
void configure_logging(bts::config::Logging const&cfg,bts::config::ThreadGroupCfg const&);
+188
View File
@@ -0,0 +1,188 @@
#include "MDPeriodicDumper.h"
#include "lib/md/MDConsumer.h"
#include "lib/utils/TimeStamp.h"
#include "lib/utils/Dispatcher.h"
#include "lib/utils/datetime.h"
#include <memory>
#include <string>
#include <iostream>
#include <iomanip>
namespace bts {
namespace md {
/*
* Design considerations
*
* - We want this test to be 'fair'
* - We want a manageable amount of data
* -
*/
struct Entry {
uint32_t seq;
MDConsumerHandle handle;
std::unique_ptr<api::Security> security;
};
std::string print_book(
PrintFormat format,
const Entry& e,
utils::TimeStamp twrite,
utils::TimeStamp tread,
uint32_t seq,
bool ambiguous,
const std::array<PriceSize,COCONUT_MAX_DEPTH>& bid,
const std::array<PriceSize,COCONUT_MAX_DEPTH>& ask) {
std::stringstream ss;
if(format == PrintFormat::Human) {
ss << std::endl;
ss << e.security->glbx().security_desc() << " (" << e.handle.key() << ")" << std::endl;
ss << "Sequence: " << seq;
if (ambiguous) ss << "*";
ss << std::endl;
ss << "TWrit: " << twrite.to_uint64() << " ns" << std::endl;
ss << "TRead: " << tread.to_uint64() << " ns" << std::endl;
char buffer[512];
snprintf(buffer, 512, "%-5s %-14s | %-14s %-5s\n", "sz", "bid", "ask", "sz");
ss << buffer;
ss << "-------------------- - -------------------- \n";
for (auto x = COCONUT_MAX_DEPTH; x != 0; --x) {
auto a = ask[x-1];
if (!os::is_nan(a.price())) {
snprintf(buffer, 512, "%5s %14s | %14.7f %5d\n", "", "", a.price(), a.size());
ss << buffer;
}
}
ss << "-------------------- - -------------------- \n";
for (uint32_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
auto b = bid[x];
if (!os::is_nan(b.price())) {
snprintf(buffer, 512, "%5d %14.7f | \n", b.size(), b.price());
ss << buffer;
}
}
}
else if (format == PrintFormat::CSV) {
// Symbol, Id, Sequence, Ambiguous, TWrite, TRead, BidBook, AskBook
ss << e.security->glbx().security_desc() << ","
<< e.security->id() << ","
<< seq << ","
<< (ambiguous ? "true," : "false,")
<< twrite.to_uint64() << ","
<< tread.to_uint64() << ",";
for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
if (!bid[x].is_nan()) {
ss << " " << std::setprecision(11) << bid[x].price() << "-";
if (bid[x].implied_size() > 0) { ss << "*"; }
ss << bid[x].size();
}
}
ss << ",";
for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
if (!ask[x].is_nan()) {
ss << " " << std::setprecision(11) << ask[x].price() << "-";
if (ask[x].implied_size() > 0) { ss << "*"; }
ss << ask[x].size();
}
}
ss << std::endl;
}
return ss.str();
}
datetime::Date expiration(const api::Security& sec) {
auto f = sec.future();
return { (uint16_t)f.expiration_year(), (uint8_t)f.expiration_month(), (uint8_t)f.expiration_day() };
}
std::vector<Entry> filter_entries(std::vector<Entry>& entries) {
// Choose the front month for all symbols
auto today = datetime::today();
std::map<UserSymbol, Entry*> map;
for (auto& e : entries) {
auto type = securitytype_of(*e.security);
// Only futures because front month is complicated
if (type != api::SECURITYTYPE_FUTURE) continue;
UserSymbol key { e.security->symbol(), e.security->exchange(), type };
auto& current_entry = map[key];
if (current_entry == nullptr) {
current_entry = &e;
}
else {
auto cur = expiration(*current_entry->security);
auto next = expiration(*e.security);
if (next > today && next < cur) {
current_entry = &e;
}
}
}
std::vector<Entry> result;
for (const auto& pair : map) {
result.push_back(std::move(*pair.second));
}
return result;
}
void write_header(PrintFormat format, long seq_mod, const std::vector<Entry>& entries) {
if (format == PrintFormat::Human) {
std::cout << "Dumping every sequence % " << seq_mod << " == 0 for\n";
std::cout << "Selected securities: " << std::endl;
for (const auto& e : entries) {
std::cout << e.security->glbx().security_desc() << std::endl;
}
}
else {
// CSV header
std::cout << "Symbol,Id,Sequence,Ambiguous,TWrite,TRead,BidBook,AskBook";
// for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
// std::cout << "," << "Bid" << x << ", DirectBidSize" << x << ", ImpliedBidSize" << x;
// }
// for (size_t x = 0; x < COCONUT_MAX_DEPTH; ++x) {
// std::cout << "," << "Ask" << x << ", AskSize" << x << ", ImpliedAskSize" << x;
// }
std::cout << std::endl;
}
}
void run_periodic_dumper(const std::set<UserSymbol>& symbols, uint32_t seq_mod, PrintFormat format) {
MDConsumer consumer(symbols, nullptr);
// read all securities into a vector
std::vector<Entry> entries;
consumer.walk_security_list([&](const api::Security& sec, MDConsumerHandle handle) {
entries.push_back({ 0, handle, std::make_unique<api::Security>(sec) });
});
entries = filter_entries(entries);
write_header(format, seq_mod, entries);
auto& disp = utils::Dispatcher::background();
// spin around on the vector detecting changes
std::array<PriceSize,COCONUT_MAX_DEPTH> bid,ask;
while(true) {
for(auto& e : entries) {
auto sequence = e.handle.sequence();
if (sequence != e.seq && sequence % seq_mod == 0) {
e.seq = sequence;
auto tread = TimeStamp::now();
utils::TimeStamp twrite;
e.handle.get_book(twrite, COCONUT_MAX_DEPTH, bid.data(), ask.data());
auto seq2 = e.handle.sequence();
// print book to stdout (async?)
disp.post([=,&e]() {
std::cout << print_book(format, e, twrite, tread, sequence, (sequence != seq2), bid, ask);
});
}
}
}
}
}}//ns
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "lib/md/UserSymbol.h"
#include <set>
namespace bts {
namespace md {
enum class PrintFormat {
Human,
CSV
};
void run_periodic_dumper(const std::set<UserSymbol>&, uint32_t seq_mod, PrintFormat format);
}}//ns
-69
View File
@@ -1,69 +0,0 @@
#ifndef NO_CPP0X
#include "MDPriceLantencyTester.h"
#include "../MDConsumer.h"
#include "lib/utils/Dispatcher.h"
#include <iostream>
#include <set>
#include <vector>
using namespace std;
namespace bts {
namespace md {
struct HandleData {
MDConsumerHandle handle;
utils::TimeStamp time;
utils::TimeStamp last_price_change;
float bid, ask;
};
void run_md_price_latency_test(UserSymbol symbol) {
set<UserSymbol> symbols;
symbols.insert(symbol);
std::shared_ptr<api::ISyntheticSecurityService> null;
MDConsumer consumer(symbols, null);
vector<HandleData> handles;
consumer.walk_security_list([&](const Security&, MDConsumerHandle handle) {
HandleData entry = { handle, TimeStamp(), TimeStamp(), 0, 0 };
if (!entry.handle.is_initialized()) {
cout << "Please start the md publisher before running this command." << endl;
return;
}
handles.push_back(entry);
});
cout << "key, sequence, handover, delta, bid price, ask price" << endl;
while (true) {
for(auto& entry : handles) {
PriceSize bid, ask;
if (entry.handle.get_direct_book(entry.time, 1, &bid, &ask) == HandleReadResult::SUCCESS) {
auto bid_price = bid.price();
auto ask_price = ask.price();
if (bid_price != entry.bid || ask_price != entry.ask) {
auto now = TimeStamp::now();
auto handover = (now - entry.time);
auto delta = (now - entry.last_price_change);
entry.last_price_change = entry.time;
entry.bid = bid_price;
entry.ask = ask_price;
utils::Dispatcher::background().post([=]() {
cout <<
entry.handle.key() << ','
<< entry.handle.sequence() << ','
<< handover.microseconds() << ','
<< delta.microseconds() << ','
<< bid_price << ',' << ask_price
<< endl;
});
}
}
}
}
}
}}//namespace
#endif
-15
View File
@@ -1,15 +0,0 @@
#pragma once
#include "lib/utils/bts_export.h"
#include "../common.h"
#include "../UserSymbol.h"
#include <string>
namespace bts {
namespace md {
BTS_EXPORT void run_md_price_latency_test(UserSymbol);
}}//namespace
+39 -28
View File
@@ -33,8 +33,11 @@ string dump(IntPrice price) {
string dump(PriceSize ps) {
stringstream res;
res << "{";
res << "'price': " << std::setprecision(11) << ps.price() << ", ";
res << "'size': " << ps.size() << " ";
res << "'price': " << std::setprecision(11) << ps.price();
if (!ps.is_nan()) {
res << " (^" << (int)ps.exponent() << ")";
}
res << ", 'size': " << ps.size() << " ";
// res << "'mant': " << ps.mantissa() << ", ";
// res << "'exp': " << (int)ps.exponent() << " ";
res << "}";
@@ -162,34 +165,42 @@ string dump_entry(const HotDepthEntry& entry) {
res << "{ ";
res << "'timestamp': " << dump(entry.time()) << ", ";
res << "'key': " << entry.key << ", ";
res << "\n'bid': [";
for (const auto& level : entry.direct) {
res << dump(level.bid()) << ", ";
}
res << "], ";
res << "\n'ask': [";
for (const auto& level : entry.direct) {
res << dump(level.ask()) << ", ";
}
res << "], ";
res << "'\nimplied_bid': [";
for (const auto& level : entry.implied) {
res << dump(level.bid()) << ", ";
}
res << "], ";
res << "'\nimplied_ask': [";
for (const auto& level : entry.implied) {
res << dump(level.ask()) << ", ";
}
res << "], ";
res << "\n'settlement': '" << dump(entry.settlement) << "', ";
res << "'settlement': '" << dump(entry.settlement) << "', ";
res << "'status': '" << CoconutSecurityStatus_Name((CoconutSecurityStatus)entry.status) << "', ";
res << "'sequence': '" << entry.sequence << "', ";
res << "\n 'bid': [";
for (const auto& level : entry.direct) {
if (!level.bid().is_nan()) {
res << dump(level.bid()) << ", ";
}
}
res << "], ";
res << "\n 'ask': [";
for (const auto& level : entry.direct) {
if (!level.ask().is_nan()) {
res << dump(level.ask()) << ", ";
}
}
res << "], ";
res << "\n 'implied_bid': [";
for (const auto& level : entry.implied) {
if (!level.bid().is_nan()) {
res << dump(level.bid()) << ", ";
}
}
res << "], ";
res << "\n 'implied_ask': [";
for (const auto& level : entry.implied) {
if (!level.ask().is_nan()) {
res << dump(level.ask()) << ", ";
}
}
res << "]";
res << " }";
return res.str();
}
-11
View File
@@ -305,17 +305,6 @@ CoconutSecurityStatus MDConsumerHandle::status() const {
}
never;
}
uint16_t MDConsumerHandle::sequence() const {
switch(_type) {
case HANDLETYPE_VOID: return 0;
case HANDLETYPE_DEPTH: return book.hot->sequence;
case HANDLETYPE_BBO: return bbo.hot->sequence;
case HANDLETYPE_SYNTHETIC:
bts_assert(!synthetic.def->legs.empty());
return sane::min_by<MDConsumerHandle, TimeStamp>(synthetic.def->legs, [](const MDConsumerHandle& h) { return h.last_market_update(); })->sequence();
}
never;
}
}}//namespace
+11 -1
View File
@@ -159,7 +159,17 @@ public:
bool is_heart_beating() const;
TimeStamp heartbeat() const;
CoconutSecurityStatus status() const;
uint16_t sequence() const;
uint32_t sequence() const {
switch(_type) {
case HANDLETYPE_VOID: return 0;
case HANDLETYPE_DEPTH: return book.hot->sequence;
case HANDLETYPE_BBO: return bbo.hot->sequence;
case HANDLETYPE_SYNTHETIC:
bts_assert(!synthetic.def->legs.empty());
return sane::min_by<MDConsumerHandle, TimeStamp>(synthetic.def->legs, [](const MDConsumerHandle& h) { return h.last_market_update(); })->sequence();
}
bts_assert(false);
}
security_key key() const {
switch(_type) {
case HANDLETYPE_VOID: return invalid_security_key;
Loaded 100 of 202 files, more files were not shown because too many files have changed in this diff. Show more