Compare commits

..
68 Commits
Author SHA1 Message Date
John Colvin 3b320a1075 MDConsumerHandle - don't convert LLDateTimes to System.DateTimes 2018-09-17 12:55:54 -04:00
John Colvin e838413541 minor refactoring in DispatcherUpdateMaster 2018-09-17 11:42:17 -04:00
John Colvin 7d6d72b423 mview - perform update work in parallel with new DispatcherUpdateMaster 2018-09-16 17:17:43 -04:00
John Colvin 891d674ca4 overload MDConsumerHandle.get_last_trade to avoid creating unused timestamp via ll_to_parts 2018-09-16 14:54:32 -04:00
Taha Afzal 92c1508b8c Put the VolumeAtPrice behind a toggle config 2018-09-14 14:58:35 -04:00
Jake Norbut 466915f6e9 Updating MICS conf to use unquoted enum string rather than enum value 2018-09-14 13:57:37 -05:00
Pedro Pinto 1c68cfa08e Fixed clang warning. 2018-09-13 20:44:28 +00:00
Pedro Pinto cc75872c04 Fixed crashing bug in order plugin. 2018-09-13 20:28:48 +00:00
Pedro Pinto 7e93866596 Tweaks to metrics tools. 2018-09-13 20:28:48 +00:00
Taha Afzal f4213eefe9 fixing header inclusions 2018-09-13 16:26:06 -04:00
Taha Afzal 72bfafb376 Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-09-13 15:24:58 -04:00
Taha Afzal 5dbacfddd5 SPARK-1672(Volume at price) 2018-09-13 15:24:49 -04:00
Pedro Pinto e29ea5d89b SPARK-1514, Server side SDK code complete. 2018-09-12 18:43:17 +00:00
Eric Harding 2885d929a8 Safe me from my own mistyping 2018-09-11 15:19:10 -04:00
Eric Harding a3e5bc4fba Prune unused script from tools 2018-09-11 12:50:20 -04:00
Eric Harding 7944338516 add test watching script (linux) 2018-09-11 16:15:39 +00:00
Taha Afzal bb19f737d5 SPARK-1672 hooked up the timer for polling 2018-09-10 22:28:48 -04:00
John Colvin 77854a3cf0 SPARK-1714 Ladder account - unable to pick a selection on server restart 2018-09-10 18:56:41 -04:00
Taha Afzal d29ec26beb SPARK-1672 scaffolding 2018-09-10 15:11:18 -04:00
Michael Harding d64810e234 Merged in SPARK-1678 (pull request #72)
SPARK-1678
2018-09-10 17:44:43 +00:00
Taha Afzal f3d2c9e5b9 SPARK-1672 VAP initial check in 2018-09-10 12:39:22 -04:00
Eric Harding 63ac084a17 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-10 11:24:37 -04:00
Eric Harding 29f7c24a00 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-10 10:40:33 -04:00
Taha Afzal 58c298fed8 Changing the AdminService -> OrderService call to async 2018-09-10 10:18:05 -04:00
Taha Afzal ee825f76c7 SPARK-1696 completed 2018-09-09 21:27:54 -04:00
Taha Afzal dfb33efd45 SPARK-1696 util functions added 2018-09-09 11:00:51 -04:00
Taha Afzal dd7ebc54a2 Permissions functions in AminServiceUtils 2018-09-07 19:22:32 -04:00
Kevin Darby 8f99d8d9ca Merge branch 'master' of bitbucket.org:bluetradesys/spark 2018-09-07 17:37:57 -04:00
Kevin Darby c411a0c792 Merge branch 'order-def-templates' of bitbucket.org:bluetradesys/spark 2018-09-07 17:37:17 -04:00
Michael Harding bc8513778b Move Account controls into tab contents 2018-09-07 17:33:20 -04:00
Michael Harding 501dc37e94 F# type system is less resilient than it thinks it is 2018-09-07 17:17:42 -04:00
Michael Harding c58b5fb0e6 SPARK-1678: Cancel orders for account 2018-09-07 17:10:19 -04:00
Michael Harding f89f2b8ad0 Remove Beta from SparkAdmin 2018-09-07 17:10:18 -04:00
Pedro Pinto 5e3e85641d Merged from prod 2018-09-07 21:00:35 +00:00
John Colvin 4a5f71e7f2 fix build 2018-09-07 16:37:23 -04:00
Kevin Darby 92d7503528 cmake? 2018-09-07 16:18:46 -04:00
Kevin Darby 773f3db976 Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2018-09-07 16:17:47 -04:00
Eric Harding dd5b489e8f SPARK-1659 Update producer api to handle implied markets (push early for emumba) 2018-09-07 09:29:41 -04:00
Taha Afzal 7bda1cc1cc Permissions proto and service member added 2018-09-06 21:51:10 -04:00
Eric Harding 58e88812b1 SPARK-1693 - Code quota reached. Have to delete some. 2018-09-06 13:54:02 -04:00
Eric Harding db80631901 Fix "some projects had trouble loading" in vscode 2018-09-06 12:21:05 -04:00
Taha Afzal 593944266d merging 2018-09-06 10:47:29 -04:00
Taha Afzal b8883fc8ae SPARK-1668 reporting stream is done 2018-09-06 10:31:33 -04:00
Taha Afzal efac4a6077 SPARK-1668 foundation 2018-09-06 10:31:33 -04:00
Jake Norbut cb22d21f70 Merged parsing_mics into master 2018-09-06 07:57:15 -05:00
Jake Norbut f6b17d9541 Merge branch 'master' of bitbucket.org:bluetradesys/spark into parsing_mics 2018-09-06 07:56:18 -05:00
Pedro Pinto 7a1298e015 Merge from QA 2018-09-05 18:36:49 -04:00
Jake Norbut 0a81521e6e changing to Taha's proto, not int32, silly man 2018-09-05 13:56:27 -05:00
Jake Norbut 8771281954 Updating script to parse and dump mics 2018-09-05 13:26:50 -05:00
Jake Norbut b50381d386 adding an unset enum state to CMEMics 2018-09-05 12:38:12 -05:00
Jake Norbut 91345cf9d2 renaming cpp_config proto and conf to cme_reference_data 2018-09-05 12:37:25 -05:00
Taha Afzal 19b3ba6615 SPARK-1668 MIC enums added 2018-09-05 12:41:03 -04:00
Taha Afzal 6b68029b6d adding comments to SPARK-1679 protos 2018-09-05 10:03:38 -04:00
Taha Afzal 15c8a8bf28 SPARK-1679 done ! 2018-09-05 09:55:05 -04:00
Taha Afzal 2b2fe02315 SPARK-1679 get_order_summary done 2018-09-04 22:13:25 -04:00
Taha Afzal 1f4c91a344 SPARK-1679 2018-09-04 10:37:45 -04:00
Taha Afzal 92f8f74d8f SPARK-1679 2018-09-04 08:51:50 -04:00
John Colvin ad2869ea6f OrderPropertiesEditor - use id and order-type to match selected orderdef in inlineprops 2018-09-02 13:06:29 -04:00
John Colvin 979506d5be OrderTemplateKey/Header - override ToString 2018-09-02 12:38:13 -04:00
John Colvin bc6e792855 SPARK-1684 ladder/ticket - infrastructure for order definition templates 2018-09-01 21:25:20 -04:00
Eric Harding 5df90f27c8 Update onixs license 2018-08-29 10:47:07 -04:00
Pedro Pinto d571642454 Removed test code from SDK example 2018-08-28 10:21:16 -04:00
Pedro Pinto 0695ddc33e Moved subrepo ptr 2018-08-28 13:24:08 +00:00
Pedro Pinto c97e7675fd Testing SDK 2018-08-28 13:20:56 +00:00
Pedro Pinto 526805680a Updated cmake files 2018-08-24 18:14:24 +00:00
Pedro Pinto 03e3549895 Merged 2018-08-24 13:56:35 -04:00
Pedro Pinto ebbc48771b Merge 2018-08-19 13:26:56 -04:00
Pedro Pinto 2d6fb5c15c cleaned up build 2018-08-19 17:24:40 +00:00
323 changed files with 8068 additions and 7810 deletions

No files matched your search

-1
View File
@@ -25,4 +25,3 @@ src/server/sparkd/orderman/perf_analysis/data
src/server/sparkd/orderman/perf_analysis/logs/
src/server/sparkd/orderman/perf_analysis/trace_data/
src/lib/orderman/perf_analysis/logs/latency_data/
trace_data/
+1 -12
View File
@@ -42,18 +42,7 @@
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
{
"name": "Client Tests",
"type": "clr",
"request": "launch",
"program": "${workspaceRoot}/build/Debug/bin/net471/sparkcmd.exe",
"args": ["test","test_filter"],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"externalConsole": true,
"requireExactSource": false
},
},
{
"name": "Client Attach",
"type": "clr",
+3 -1
View File
@@ -40,7 +40,7 @@ else ()
-fPIC -Wall -pedantic -Wextra -Werror \
-Wno-missing-braces -Wno-ignored-qualifiers \
--std=c++17 -fdiagnostics-color")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -DNDEBUG -O3")
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG -O3")
add_subdirectory(src/server/sparkd)
add_subdirectory(src/lib)
add_subdirectory(src/web)
@@ -54,6 +54,8 @@ else ()
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/web ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/server/plugins
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/sdk/examples/order_plugin_example.so ${CMAKE_BINARY_DIR}/server/plugins/
COMMENT "Creating server runtime layout"
DEPENDS sparkd data_files_dir)
-301
View File
@@ -1,301 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<!-- 2018-10-08 Mon 18:50 -->
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>&lrm;</title>
<meta name="generator" content="Org mode" />
<meta name="author" content="ppinto" />
<style type="text/css">
<!--/*--><![CDATA[/*><!--*/
.title { text-align: center;
margin-bottom: .2em; }
.subtitle { text-align: center;
font-size: medium;
font-weight: bold;
margin-top:0; }
.todo { font-family: monospace; color: red; }
.done { font-family: monospace; color: green; }
.priority { font-family: monospace; color: orange; }
.tag { background-color: #eee; font-family: monospace;
padding: 2px; font-size: 80%; font-weight: normal; }
.timestamp { color: #bebebe; }
.timestamp-kwd { color: #5f9ea0; }
.org-right { margin-left: auto; margin-right: 0px; text-align: right; }
.org-left { margin-left: 0px; margin-right: auto; text-align: left; }
.org-center { margin-left: auto; margin-right: auto; text-align: center; }
.underline { text-decoration: underline; }
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
p.verse { margin-left: 3%; }
pre {
border: 1px solid #ccc;
box-shadow: 3px 3px 3px #eee;
padding: 8pt;
font-family: monospace;
overflow: auto;
margin: 1.2em;
}
pre.src {
position: relative;
overflow: visible;
padding-top: 1.2em;
}
pre.src:before {
display: none;
position: absolute;
background-color: white;
top: -10px;
right: 10px;
padding: 3px;
border: 1px solid black;
}
pre.src:hover:before { display: inline;}
/* Languages per Org manual */
pre.src-asymptote:before { content: 'Asymptote'; }
pre.src-awk:before { content: 'Awk'; }
pre.src-C:before { content: 'C'; }
/* pre.src-C++ doesn't work in CSS */
pre.src-clojure:before { content: 'Clojure'; }
pre.src-css:before { content: 'CSS'; }
pre.src-D:before { content: 'D'; }
pre.src-ditaa:before { content: 'ditaa'; }
pre.src-dot:before { content: 'Graphviz'; }
pre.src-calc:before { content: 'Emacs Calc'; }
pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
pre.src-fortran:before { content: 'Fortran'; }
pre.src-gnuplot:before { content: 'gnuplot'; }
pre.src-haskell:before { content: 'Haskell'; }
pre.src-hledger:before { content: 'hledger'; }
pre.src-java:before { content: 'Java'; }
pre.src-js:before { content: 'Javascript'; }
pre.src-latex:before { content: 'LaTeX'; }
pre.src-ledger:before { content: 'Ledger'; }
pre.src-lisp:before { content: 'Lisp'; }
pre.src-lilypond:before { content: 'Lilypond'; }
pre.src-lua:before { content: 'Lua'; }
pre.src-matlab:before { content: 'MATLAB'; }
pre.src-mscgen:before { content: 'Mscgen'; }
pre.src-ocaml:before { content: 'Objective Caml'; }
pre.src-octave:before { content: 'Octave'; }
pre.src-org:before { content: 'Org mode'; }
pre.src-oz:before { content: 'OZ'; }
pre.src-plantuml:before { content: 'Plantuml'; }
pre.src-processing:before { content: 'Processing.js'; }
pre.src-python:before { content: 'Python'; }
pre.src-R:before { content: 'R'; }
pre.src-ruby:before { content: 'Ruby'; }
pre.src-sass:before { content: 'Sass'; }
pre.src-scheme:before { content: 'Scheme'; }
pre.src-screen:before { content: 'Gnu Screen'; }
pre.src-sed:before { content: 'Sed'; }
pre.src-sh:before { content: 'shell'; }
pre.src-sql:before { content: 'SQL'; }
pre.src-sqlite:before { content: 'SQLite'; }
/* additional languages in org.el's org-babel-load-languages alist */
pre.src-forth:before { content: 'Forth'; }
pre.src-io:before { content: 'IO'; }
pre.src-J:before { content: 'J'; }
pre.src-makefile:before { content: 'Makefile'; }
pre.src-maxima:before { content: 'Maxima'; }
pre.src-perl:before { content: 'Perl'; }
pre.src-picolisp:before { content: 'Pico Lisp'; }
pre.src-scala:before { content: 'Scala'; }
pre.src-shell:before { content: 'Shell Script'; }
pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
/* additional language identifiers per "defun org-babel-execute"
in ob-*.el */
pre.src-cpp:before { content: 'C++'; }
pre.src-abc:before { content: 'ABC'; }
pre.src-coq:before { content: 'Coq'; }
pre.src-groovy:before { content: 'Groovy'; }
/* additional language identifiers from org-babel-shell-names in
ob-shell.el: ob-shell is the only babel language using a lambda to put
the execution function name together. */
pre.src-bash:before { content: 'bash'; }
pre.src-csh:before { content: 'csh'; }
pre.src-ash:before { content: 'ash'; }
pre.src-dash:before { content: 'dash'; }
pre.src-ksh:before { content: 'ksh'; }
pre.src-mksh:before { content: 'mksh'; }
pre.src-posh:before { content: 'posh'; }
/* Additional Emacs modes also supported by the LaTeX listings package */
pre.src-ada:before { content: 'Ada'; }
pre.src-asm:before { content: 'Assembler'; }
pre.src-caml:before { content: 'Caml'; }
pre.src-delphi:before { content: 'Delphi'; }
pre.src-html:before { content: 'HTML'; }
pre.src-idl:before { content: 'IDL'; }
pre.src-mercury:before { content: 'Mercury'; }
pre.src-metapost:before { content: 'MetaPost'; }
pre.src-modula-2:before { content: 'Modula-2'; }
pre.src-pascal:before { content: 'Pascal'; }
pre.src-ps:before { content: 'PostScript'; }
pre.src-prolog:before { content: 'Prolog'; }
pre.src-simula:before { content: 'Simula'; }
pre.src-tcl:before { content: 'tcl'; }
pre.src-tex:before { content: 'TeX'; }
pre.src-plain-tex:before { content: 'Plain TeX'; }
pre.src-verilog:before { content: 'Verilog'; }
pre.src-vhdl:before { content: 'VHDL'; }
pre.src-xml:before { content: 'XML'; }
pre.src-nxml:before { content: 'XML'; }
/* add a generic configuration mode; LaTeX export needs an additional
(add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
pre.src-conf:before { content: 'Configuration File'; }
table { border-collapse:collapse; }
caption.t-above { caption-side: top; }
caption.t-bottom { caption-side: bottom; }
td, th { vertical-align:top; }
th.org-right { text-align: center; }
th.org-left { text-align: center; }
th.org-center { text-align: center; }
td.org-right { text-align: right; }
td.org-left { text-align: left; }
td.org-center { text-align: center; }
dt { font-weight: bold; }
.footpara { display: inline; }
.footdef { margin-bottom: 1em; }
.figure { padding: 1em; }
.figure p { text-align: center; }
.inlinetask {
padding: 10px;
border: 2px solid gray;
margin: 10px;
background: #ffffcc;
}
#org-div-home-and-up
{ text-align: right; font-size: 70%; white-space: nowrap; }
textarea { overflow-x: auto; }
.linenr { font-size: smaller }
.code-highlighted { background-color: #ffff00; }
.org-info-js_info-navigation { border-style: none; }
#org-info-js_console-label
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
.org-info-js_search-highlight
{ background-color: #ffff00; color: #000000; font-weight: bold; }
.org-svg { width: 90%; }
/*]]>*/-->
</style>
<script type="text/javascript">
/*
@licstart The following is the entire license notice for the
JavaScript code in this tag.
Copyright (C) 2012-2018 Free Software Foundation, Inc.
The JavaScript code in this tag is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
As additional permission under GNU GPL version 3 section 7, you
may distribute non-source (e.g., minimized or compacted) forms of
that code without the copy of the GNU GPL normally required by
section 4, provided you include this license notice and a URL
through which recipients can access the Corresponding Source.
@licend The above is the entire license notice
for the JavaScript code in this tag.
*/
<!--/*--><![CDATA[/*><!--*/
function CodeHighlightOn(elem, id)
{
var target = document.getElementById(id);
if(null != target) {
elem.cacheClassElem = elem.className;
elem.cacheClassTarget = target.className;
target.className = "code-highlighted";
elem.className = "code-highlighted";
}
}
function CodeHighlightOff(elem, id)
{
var target = document.getElementById(id);
if(elem.cacheClassElem)
elem.className = elem.cacheClassElem;
if(elem.cacheClassTarget)
target.className = elem.cacheClassTarget;
}
/*]]>*///-->
</script>
</head>
<body>
<div id="content">
<p>
We are going to experiment with our development process, getting away
from cycles and towards more continuous development. The main idea is
to get rid of the prod and QA branches and instead deploy directly
from master, which we keep deployment ready at all times. Development
occurs on private branches, each branch mapped to a separately
deploy-able project. Each of projects will be managed more or less
independently and will include specification, development and
validation stages. At any particular time we might have anywhere from
1 to 6 projects in flight each of which continuously pulls from master,
but does not push back to master until the code is ready to be
deployed to production. The point at which a project is ready to be
deployed then takes roughly the place of the QA phase of our old cycle
mode. At this time, we will do a PR, and go through a series of
validation steps, to make sure the work meets our standards. This
validation includes:
</p>
<ul class="org-ul">
<li>Code Reviews</li>
<li>Acceptance Testing (including UI and business tests)</li>
<li>Automated System testing (to be developed)</li>
<li>A mini-qa, focused on the feature, by someone not directly involved
in its implementation</li>
<li>If possible, deployment to a beta customer.</li>
</ul>
<p>
Once these QA gates are passed, we will update the release notes, tag
a new version, integrate to master and then ideally immediately deploy
to a customer.
</p>
<p>
The main goal with this change is to reduce the latency from feature
specification to production roll out. We also hope to improve
throughput by making more efficient use of QA time and reducing idle
cycles. We expect people to participate in more than one project
depending on their area of expertise and work load. This does not mean
people will work on two things at the same time, rather that people
come into and leave projects as they free up and not when the project
starts or ends.
</p>
<p>
This new scheme will come into full effect when we finish 1.3
although to some degree 1.3 was already setup like this. At the
moment, we have 3 major projects, vap/settlments/implied, grid
settings and speed work, which are in branches of prod. We will merge
these back to prod and then master this week. We have 3 other projects
that are in master, the excel API, the server side SDK and the MIC
administration and enforcement components. These we hope to stabilize
as soon as possible so that we can merge them to prod and deploy them
along with the rest. Once we have all this, prod and master should be
identical and we will delete prod and operate exclusively out of
master. Please avoid committing anything new to master so we can
complete this transition as soon as possible.
</p>
<p>
Let me know if you have any question
</p>
</div>
<div id="postamble" class="status">
<p class="author">Author: ppinto</p>
<p class="date">Created: 2018-10-08 Mon 18:50</p>
<p class="validation"><a href="http://validator.w3.org/check?uri=referer">Validate</a></p>
</div>
</body>
</html>
-55
View File
@@ -1,55 +0,0 @@
We are going to experiment with our development process, getting away
from cycles and towards more continuous development. The main idea is
to get rid of the prod and QA branches and instead deploy directly
from master, which we keep deployment ready at all times. Development
occurs on private branches, each branch mapped to a separately
deploy-able project. Each of projects will be managed more or less
independently and will include specification, development and
validation stages. At any particular time we might have anywhere from
1 to 6 projects in flight each of which continuously pulls from master,
but does not push back to master until the code is ready to be
deployed to production. The point at which a project is ready to be
deployed then takes roughly the place of the QA phase of our old cycle
mode. At this time, we will do a PR, and go through a series of
validation steps, to make sure the work meets our standards. This
validation includes:
- Code Reviews
- Acceptance Testing (including UI and business tests)
- Automated System testing (to be developed)
- A mini-qa, focused on the feature, by someone not directly involved
in its implementation
- If possible, deployment to a beta customer.
Once these QA gates are passed, we will update the release notes, tag
a new version, integrate to master and then ideally immediately deploy
to a customer.
The main goal with this change is to reduce the latency from feature
specification to production roll out. We also hope to improve
throughput by making more efficient use of QA time and reducing idle
cycles. We expect people to participate in more than one project
depending on their area of expertise and work load. This does not mean
people will work on two things at the same time, rather that people
come into and leave projects as they free up and not when the project
starts or ends.
This new scheme will come into full effect when we finish 1.3
although to some degree 1.3 was already setup like this. At the
moment, we have 3 major projects, vap/settlments/implied, grid
settings and speed work, which are in branches of prod. We will merge
these back to prod and then master this week. We have 3 other projects
that are in master, the excel API, the server side SDK and the MIC
administration and enforcement components. These we hope to stabilize
as soon as possible so that we can merge them to prod and deploy them
along with the rest. Once we have all this, prod and master should be
identical and we will delete prod and operate exclusively out of
master. Please avoid committing anything new to master so we can
complete this transition as soon as possible.
Let me know if you have any question
-3
View File
@@ -1,6 +1,3 @@
1.3.4
- Changed default logging config for Windows. If Logging config is overridden for a system it should be adjusted to override only linux.
1.1.0
- With this release we dropped wild cards from limits, so any user
that has limits expressed in terms of wild cards will have to have
-21
View File
@@ -1,24 +1,3 @@
1.3.4
- Significantly reduced client memory use
- Improved OrderBook performance
- Improved workspace load time
1.3.3
- Reduced server memory usage
1.3.2
- Improved OrderBook performance
- Fix some memory leaks when switching workspaces
1.3.1
- Improved server side logging
- Fixed leaking of market data subscriptions on market view form closing
1.3.0
- Significantly improved order execution latency
- Added latency monitoring tools
- Provided core binding controls to system admins so as to reduce latency jitter
1.1.22
- Orderbook: Orders in Error status can now be recreated
- Orderbook: Orders containing child orders will now be collapsed by default.
+59 -55
View File
@@ -4,7 +4,7 @@ VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
@@ -12,11 +12,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sparkcmd", "src\client\spar
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\spark_core\spark_core.csproj", "{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{9AD1A867-93A9-301D-BF77-0113E18E9E39}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
ProjectSection(ProjectDependencies) = postProject
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3} = {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C} = {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{ECD78CC5-138F-4809-A764-2358C2AC2E82}"
@@ -57,22 +57,22 @@ Global
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x86.Build.0 = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|Any CPU.ActiveCfg = Debug|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x64.ActiveCfg = Debug|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x64.Build.0 = Debug|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Debug|x86.ActiveCfg = Debug|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|Any CPU.ActiveCfg = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x64.ActiveCfg = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x64.Build.0 = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.Release|x86.ActiveCfg = Release|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|Any CPU.ActiveCfg = Debug|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x64.ActiveCfg = Debug|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x64.Build.0 = Debug|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Debug|x86.ActiveCfg = Debug|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|Any CPU.ActiveCfg = Release|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x64.ActiveCfg = Release|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x64.Build.0 = Release|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.Release|x86.ActiveCfg = Release|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|Any CPU.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.Build.0 = Debug|x64
@@ -93,26 +93,30 @@ Global
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x64.Build.0 = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.RelWithDebInfo|x86.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.ActiveCfg = Debug|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.Build.0 = Release|x64
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|Any CPU.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x64.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Debug|x86.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.ActiveCfg = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.MinSizeRel|x86.Build.0 = Debug|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|Any CPU.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x64.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.Release|x86.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU
{387566C6-C6F6-412C-A30C-9855BBCB4816}.RelWithDebInfo|x86.Build.0 = Release|Any CPU
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|Any CPU.ActiveCfg = Debug|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.ActiveCfg = Debug|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.Debug|x64.Build.0 = Debug|x64
@@ -133,22 +137,22 @@ Global
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x64.Build.0 = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.ActiveCfg = Release|x64
{6E449EB6-20AC-4FA6-B8D9-10F4BB7E1FEE}.RelWithDebInfo|x86.Build.0 = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|Any CPU.ActiveCfg = Debug|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x64.ActiveCfg = Debug|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x64.Build.0 = Debug|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Debug|x86.ActiveCfg = Debug|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|Any CPU.ActiveCfg = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x64.ActiveCfg = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x64.Build.0 = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.Release|x86.ActiveCfg = Release|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{9AD1A867-93A9-301D-BF77-0113E18E9E39}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|Any CPU.ActiveCfg = Debug|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x64.ActiveCfg = Debug|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x64.Build.0 = Debug|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Debug|x86.ActiveCfg = Debug|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|Any CPU.ActiveCfg = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x64.ActiveCfg = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x64.Build.0 = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.Release|x86.ActiveCfg = Release|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|Any CPU.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.ActiveCfg = Debug|x64
{6963C314-83C4-4667-B0D0-C2F7AE208C29}.Debug|x64.Build.0 = Debug|x64
+3 -8
View File
@@ -11,22 +11,18 @@ namespace mbts.spark {
[Export]
class AdminModel : IDisposable {
readonly string _user_name;
public string user_name => _user_name;
readonly BehaviorSubject<IReadOnlyDictionary<uint, string>>
_accounts_subject = new BehaviorSubject<IReadOnlyDictionary<uint, string>>(new Dictionary<uint, string>()),
_users_subject = new BehaviorSubject<IReadOnlyDictionary<uint, string>>(new Dictionary<uint, string>());
_users_subject = new BehaviorSubject<IReadOnlyDictionary<uint, string>>(new Dictionary<uint, string>());
IReadOnlyDictionary<uint, string>
_account_map = new Dictionary<uint, string>(),
_user_map = new Dictionary<uint, string>();
readonly BehaviorSubject<AdminModelWrapper> _wrapper_subject = new BehaviorSubject<AdminModelWrapper>(null);
IDisposable _dispose;
IDisposable _dispose;
[ImportingConstructor]
AdminModel(AdminService service, [Import("username")]string user_name) {
_user_name = user_name;
_dispose = service.subscribei_admin_data().Select(update => {
_wrapper_subject.OnNext(new AdminModelWrapper(update.all.FirstOrDefault(), _user_name));
IEnumerable<BTSUserImmutable> active_users =
from admin_data in update.all
from bts_user in admin_data.Users
@@ -56,10 +52,9 @@ namespace mbts.spark {
}
public IObservable<IReadOnlyDictionary<uint, string>> on_users => _users_subject.AsObservable();
public IObservable<IReadOnlyDictionary<uint, string>> on_accounts => _accounts_subject.AsObservable();
public IObservable<IReadOnlyDictionary<uint, string>> on_accounts => _accounts_subject.AsObservable();
public IObservable<IReadOnlyList<UserTuple>> on_user_tuples => on_users.Select(d => d.Select(kv => new UserTuple(kv.Key, kv.Value)).ToList());
public IObservable<IReadOnlyList<AccountTuple>> on_account_tuples => on_accounts.Select(d => d.Select(kv => new AccountTuple(kv.Key, kv.Value)).ToList());
public IObservable<AdminModelWrapper> on_admin_data => _wrapper_subject.AsObservable();
public UserTuple? get_current_user() {
foreach (var kv in _user_map) {
@@ -1,65 +0,0 @@
using bts;
using bts.api.admin;
using System;
using System.Collections.Generic;
using System.Linq;
namespace mbts.spark
{
class AdminModelWrapper
{
readonly AdminDataImmutable? _admin_data;
readonly string _user_name;
readonly List<BTSAccountImmutable> _allowed_accounts;
readonly Dictionary<string, string> _allowed_account_dict = new Dictionary<string, string>();
public AdminModelWrapper(AdminDataImmutable? admin_data = null, string user_name = null)
{
_admin_data = admin_data;
_user_name = user_name;
_allowed_accounts = search_admin(u => u.Name == _user_name).Select(x => x.Item1).ToList();
foreach(var val in _allowed_accounts)
_allowed_account_dict[val.Name] = val.Name;
}
public IEnumerable<Tuple<BTSAccountImmutable, TradingPermissionUnitImmutable>> get_trading_permissions(UInt32 user_id)
{
return search_admin(u => u.Id == user_id);
}
public IEnumerable<BTSAccountImmutable> viewable_accounts()
{
return _allowed_accounts;
}
public bool can_view_account(string account)
{
return _allowed_account_dict.ContainsKey(account);
}
IEnumerable<Tuple<BTSAccountImmutable, TradingPermissionUnitImmutable>> search_admin(Func<BTSUserImmutable, bool> predicate)
{
var rv = new List<Tuple<BTSAccountImmutable, TradingPermissionUnitImmutable>>();
if (_admin_data.HasValue)
{
var users = _admin_data.Value.Users.Where(predicate);
if (users != null && users.Count() == 1)
{
var user = users.ElementAt(0);
if (user.HasUserData && user.UserData.HasAccountPermissions)
{
foreach (var kv in user.UserData.AccountPermissions.AccountPermissions)
{
var account_id = kv.Key;
var accounts = _admin_data.Value.Accounts.Where(a => a.Id == account_id);
if (accounts != null && accounts.Count() == 1)
{
var account = accounts.ElementAt(0);
var tp = kv.Value.TradingPermission;
var rv_val = new TradingPermissionUnitImmutable(tp);
rv.Add(Tuple.Create(account, rv_val));
}
}
}
}
}
return rv;
}
}
}
@@ -1,66 +0,0 @@
using bts;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.api.position;
using bts.spark;
using mbts.collections;
using mbts.spark;
using mbts.spark.utilities;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
namespace bts.api.core
{
static class PermissionFilters
{
public static IObservable<Update<PositionUnitImmutable, int>> PermissionFilter(this IObservable<Update<PositionUnitImmutable, int>> source, AdminModel am)
{
var pred = Observable.Create<Func<PositionUnitImmutable, bool>>( observer =>
{
return am.on_admin_data.Subscribe(x =>
{
Func<PositionUnitImmutable, bool> filter = (z => x.can_view_account(z.Account));
observer.OnNext(filter);
});
});
return source.Filter(pred);
}
public static IObservable<Update<OrderRecordImmutable, uint>> PermissionFilter(this IObservable<Update<OrderRecordImmutable, uint>> source, AdminModel am)
{
var pred = Observable.Create<Func<OrderRecordImmutable, bool>>(observer =>
{
return am.on_admin_data.Subscribe(x =>
{
Func<OrderRecordImmutable, bool> filter = (z => x.can_view_account(z.account()));
observer.OnNext(filter);
});
});
return source.Filter(pred);
}
public static IObservable<Update<FillImmutable, string>> PermissionFilter(this IObservable<Update<FillImmutable, string>> source, AdminModel am)
{
var pred = Observable.Create<Func<FillImmutable, bool>>(observer =>
{
return am.on_admin_data.Subscribe(x =>
{
Func<FillImmutable, bool> filter = (z => x.can_view_account(z.Account));
observer.OnNext(filter);
});
});
return source.Filter(pred);
}
}
}
@@ -53,8 +53,8 @@ namespace bts {
}
//
void handle_bad_icon(string icon_error_desc) {
Log.Api.warn($"{nameof(SecurityPickerDaemon)} received a message '{message.title}' with {icon_error_desc}, will use {nameof(Icons.Admin)}.");
icon = Icons.Admin;
Log.Api.warn($"{nameof(SecurityPickerDaemon)} received a message '{message.title}' with {icon_error_desc}, will use {nameof(Icons.AdminFrozen)}.");
icon = Icons.AdminFrozen;
}
//
var cpv = new ContractPickerView(vm);
+63 -61
View File
@@ -16,6 +16,11 @@ namespace mbts.ui {
ToolsGeometryKey = nameof(ToolsGeometryKey),
DraggableGeometryKey = nameof(DraggableGeometryKey);
static Icons() {
// TODO: probably do this for all icons, only reason for holding back is the uncertainity of any unexpected impacts
AdminFrozen = Admin.frozen();
}
public static Geometry PlayGeometry => Geometry.Parse(_get_geometric_path(PlayGeometryKey).FirstOrDefault() ?? string.Empty);
static readonly string base_uri = "pack://application:,,,/Images/Icons/";
@@ -27,43 +32,40 @@ namespace mbts.ui {
[Obsolete("Please only add new vector icons using icons.xaml")]
private static ImageSource load_image(string filename) {
return new BitmapImage(uri(filename)).frozen();
}
private static ImageSource load_resource_and_freeze(string resource_name) {
return ((DrawingImage)_resources[resource_name]).frozen();
return new BitmapImage(uri(filename));
}
#pragma warning disable CS0618 // Type or member is obsolete
public static ImageSource Logo { get { return load_image("logo.ico"); } }
public static ImageSource Admin { get { return load_resource_and_freeze("Admin"); } }
public static ImageSource Analytics { get { return load_resource_and_freeze("Analytics"); } }
public static ImageSource Sheets { get { return load_resource_and_freeze("Sheets"); } }
public static ImageSource Trades { get { return load_resource_and_freeze("Trades"); } }
public static ImageSource Depth { get { return load_resource_and_freeze("Depth"); } }
public static ImageSource Ladder { get { return load_resource_and_freeze("Ladder"); } }
public static ImageSource Admin { get { return (DrawingImage)_resources["Admin"]; } }
public static ImageSource AdminFrozen { get; }
public static ImageSource Analytics { get { return (DrawingImage)_resources["Analytics"]; } }
public static ImageSource Sheets { get { return (DrawingImage)_resources["Sheets"]; } }
public static ImageSource Trades { get { return (DrawingImage)_resources["Trades"]; } }
public static ImageSource Depth { get { return (DrawingImage)_resources["Depth"]; } }
public static ImageSource Ladder { get { return (DrawingImage)_resources["Ladder"]; } }
public static ImageSource SheetsRed { get { return load_image("SheetsRed.png"); } }
public static ImageSource SheetsGreen { get { return load_image("SheetsGreen.png"); } }
public static ImageSource Spreads { get { return load_resource_and_freeze("Spreads"); } }
public static ImageSource Spreads { get { return (DrawingImage)_resources["Spreads"]; } }
public static ImageSource BlueSpreads { get { return load_image("BlueSpread.ico"); } }
public static ImageSource Reports { get { return load_resource_and_freeze("Reports"); } }
public static ImageSource Risk { get { return load_resource_and_freeze("Risk"); } }
public static ImageSource Eye { get { return load_resource_and_freeze("Eye"); } }
public static ImageSource Coins { get { return load_resource_and_freeze("Coins"); } }
public static ImageSource Reports { get { return (DrawingImage)_resources["Reports"]; } }
public static ImageSource Risk { get { return (DrawingImage)_resources["Risk"]; } }
public static ImageSource Eye { get { return (DrawingImage)_resources["Eye"]; } }
public static ImageSource Coins { get { return (DrawingImage)_resources["Coins"]; } }
public static ImageSource DataCopy { get { return load_image("DataCopy.ico"); } }
public static ImageSource Refresh { get { return load_image("Refresh.png"); } }
public static ImageSource FavoriteWindow { get { return load_image("FavoriteWindow.png"); } }
public static ImageSource Browser => load_resource_and_freeze("Browser");
public static ImageSource Browser => (DrawingImage)_resources["Browser"];
public static ImageSource Date { get { return load_image("Date.png"); } }
public static ImageSource Cube3d { get { return load_resource_and_freeze("Cube"); } }
public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; } }
public static ImageSource Algo { get { return Cube3d; } }
public static ImageSource Quote { get { return Math; } }
public static ImageSource Math { get { return load_resource_and_freeze("Surface"); } }
public static ImageSource Math { get { return (DrawingImage)_resources["Surface"]; } }
public static ImageSource Abacus { get { return load_image("Abacus.png"); } }
public static ImageSource Delete { get { return load_image("Delete.png"); } }
@@ -75,58 +77,58 @@ namespace mbts.ui {
#pragma warning restore CS0618
// Vector icons
public static ImageSource Bookmarks { get { return load_resource_and_freeze("Bookmarks"); } }
public static ImageSource Window { get { return load_resource_and_freeze("Window"); } }
public static ImageSource ActiveWindow { get { return load_resource_and_freeze("ActiveWindow"); } }
public static ImageSource Trash { get { return load_resource_and_freeze("Trash"); } }
public static ImageSource Trash2 { get { return load_resource_and_freeze("Trash2"); } }
public static ImageSource Crucible { get { return load_resource_and_freeze("Crucible"); } }
public static ImageSource Bookmarks { get { return (DrawingImage)_resources["Bookmarks"]; } }
public static ImageSource Window { get { return (DrawingImage)_resources["Window"]; } }
public static ImageSource ActiveWindow { get { return (DrawingImage)_resources["ActiveWindow"]; } }
public static ImageSource Trash { get { return (DrawingImage)_resources["Trash"]; } }
public static ImageSource Trash2 { get { return (DrawingImage)_resources["Trash2"]; } }
public static ImageSource Crucible { get { return (DrawingImage)_resources["Crucible"]; } }
public static ImageSource Accounting { get { return load_resource_and_freeze("Accounting"); } }
public static ImageSource Add { get { return load_resource_and_freeze("Add"); } }
public static ImageSource AddEncircled { get { return load_resource_and_freeze("AddEncircled"); } }
public static ImageSource Beaker { get { return load_resource_and_freeze("Beaker"); } }
public static ImageSource Book { get { return load_resource_and_freeze("Book"); } }
public static ImageSource Accounting { get { return (DrawingImage)_resources["Accounting"]; } }
public static ImageSource Add { get { return (DrawingImage)_resources["Add"]; } }
public static ImageSource AddEncircled { get { return (DrawingImage)_resources["AddEncircled"]; } }
public static ImageSource Beaker { get { return (DrawingImage)_resources["Beaker"]; } }
public static ImageSource Book { get { return (DrawingImage)_resources["Book"]; } }
public static ImageSource Orders => create_icon(OrdersGeometryKey);
public static ImageSource AddOrders { get { return load_resource_and_freeze("AddOrders"); } }
public static ImageSource CancelOrders { get { return load_resource_and_freeze("CancelOrders"); } }
public static ImageSource CancelBuys { get { return load_resource_and_freeze("CancelBuys"); } }
public static ImageSource CancelSells { get { return load_resource_and_freeze("CancelSells"); } }
public static ImageSource CancelSelected { get { return load_resource_and_freeze("CancelSelected"); } }
public static ImageSource AddOrders { get { return (DrawingImage)_resources["AddOrders"]; } }
public static ImageSource CancelOrders { get { return (DrawingImage)_resources["CancelOrders"]; } }
public static ImageSource CancelBuys { get { return (DrawingImage)_resources["CancelBuys"]; } }
public static ImageSource CancelSells { get { return (DrawingImage)_resources["CancelSells"]; } }
public static ImageSource CancelSelected { get { return (DrawingImage)_resources["CancelSelected"]; } }
public static ImageSource LadderCancelOrders { get { return load_resource_and_freeze("LadderCancelOrders"); } }
public static ImageSource LadderCancelBuys { get { return load_resource_and_freeze("LadderCancelBuys"); } }
public static ImageSource LadderCancelSells { get { return load_resource_and_freeze("LadderCancelSells"); } }
public static ImageSource LadderCancelOrders { get { return (DrawingImage)_resources["LadderCancelOrders"]; } }
public static ImageSource LadderCancelBuys { get { return (DrawingImage)_resources["LadderCancelBuys"]; } }
public static ImageSource LadderCancelSells { get { return (DrawingImage)_resources["LadderCancelSells"]; } }
public static ImageSource Synthetics { get { return load_resource_and_freeze("Synthetics"); } }
public static ImageSource AddSynthetic { get { return load_resource_and_freeze("AddSynthetic"); } }
public static ImageSource RemoveSynthetic { get { return load_resource_and_freeze("RemoveSynthetic"); } }
public static ImageSource ModifySynthetic { get { return load_resource_and_freeze("ModifySynthetic"); } }
public static ImageSource CloneSynthetic { get { return load_resource_and_freeze("CloneSynthetic"); } }
public static ImageSource Synthetics { get { return (DrawingImage)_resources["Synthetics"]; } }
public static ImageSource AddSynthetic { get { return (DrawingImage)_resources["AddSynthetic"]; } }
public static ImageSource RemoveSynthetic { get { return (DrawingImage)_resources["RemoveSynthetic"]; } }
public static ImageSource ModifySynthetic { get { return (DrawingImage)_resources["ModifySynthetic"]; } }
public static ImageSource CloneSynthetic { get { return (DrawingImage)_resources["CloneSynthetic"]; } }
public static ImageSource ModifyOrder { get { return load_resource_and_freeze("ModifyOrder"); } }
public static ImageSource Clock { get { return load_resource_and_freeze("Clock"); } }
public static ImageSource Defrag { get { return load_resource_and_freeze("Defrag"); } }
public static ImageSource Display { get { return load_resource_and_freeze("Display"); } }
public static ImageSource ModifyOrder { get { return (DrawingImage)_resources["ModifyOrder"]; } }
public static ImageSource Clock { get { return (DrawingImage)_resources["Clock"]; } }
public static ImageSource Defrag { get { return (DrawingImage)_resources["Defrag"]; } }
public static ImageSource Display { get { return (DrawingImage)_resources["Display"]; } }
public static ImageSource Draggable => create_icon(DraggableGeometryKey);
public static ImageSource Export => load_resource_and_freeze("Export");
public static ImageSource Filter { get { return load_resource_and_freeze("Filter"); } }
public static ImageSource FilterActive { get { return load_resource_and_freeze("FilterActive"); } }
public static ImageSource FilterError { get { return load_resource_and_freeze("FilterError"); } }
public static ImageSource Export => (DrawingImage)_resources["Export"];
public static ImageSource Filter { get { return (DrawingImage)_resources["Filter"]; } }
public static ImageSource FilterActive { get { return (DrawingImage)_resources["FilterActive"]; } }
public static ImageSource FilterError { get { return (DrawingImage)_resources["FilterError"]; } }
public static ImageSource Gear => create_icon(GearGeometryKey);
public static ImageSource Import => load_resource_and_freeze("Import");
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
public static ImageSource Import => (DrawingImage)_resources["Import"];
public static ImageSource Knight { get { return (DrawingImage)_resources["Knight"]; } }
public static ImageSource Payment => create_icon(PaymentGeometryKey);
public static ImageSource Spawn { get { return load_resource_and_freeze("Spawn"); } }
public static ImageSource Target { get { return load_resource_and_freeze("Target"); } }
public static ImageSource Tetris { get { return load_resource_and_freeze("Tetris"); } }
public static ImageSource Spawn { get { return (DrawingImage)_resources["Spawn"]; } }
public static ImageSource Target { get { return (DrawingImage)_resources["Target"]; } }
public static ImageSource Tetris { get { return (DrawingImage)_resources["Tetris"]; } }
public static ImageSource Tools => create_icon(ToolsGeometryKey);
public static ImageSource ViewSettings => load_resource_and_freeze("ViewSettings");
public static ImageSource Wrench { get { return load_resource_and_freeze("Wrench"); } }
public static ImageSource RightArrow { get { return (load_resource_and_freeze("RightArrow")); } }
public static ImageSource DownArrow { get { return (load_resource_and_freeze("DownArrow")); } }
public static ImageSource Support => load_resource_and_freeze("SupportIcon");
public static ImageSource ViewSettings => (DrawingImage)_resources["ViewSettings"];
public static ImageSource Wrench { get { return (DrawingImage)_resources["Wrench"]; } }
public static ImageSource RightArrow { get { return ((DrawingImage)_resources["RightArrow"]); } }
public static ImageSource DownArrow { get { return ((DrawingImage)_resources["DownArrow"]); } }
public static ImageSource Support => (DrawingImage)_resources["SupportIcon"];
public static ImageSource create_icon(string geometric_path_key) =>
create_icon(geometric_path_key, Brushes.Black);
@@ -5,11 +5,9 @@ using mbts.collections;
using mbts.spark.utilities;
using mbts.ui;
using mbts.ui.controls;
using mbts.ui.settings;
using mbts.ui.spark;
using mbts.ui.utility;
using mbts.utils;
using bts.utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -18,7 +16,6 @@ using System.Linq;
using System.Reactive.Linq;
using System.Windows.Input;
namespace mbts.spark {
partial class MarketViewVM {
public class Row : SecurityMDRow, IDisposable, IEquatable<Row>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
@@ -78,16 +75,29 @@ namespace mbts.spark {
ask_price_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.Ask));
ask_size_click = new DelegateCommand(() => _show_order_ticket(MarketViewView.Columns.AskSize));
working_asks_click = new DelegateCommand(() => _show_order_book(MarketViewView.Columns.WorkingAsks));
_position.value_set += position_value_set;
_inside_bids.value_set += inside_bids_value_set;
_inside_asks.value_set += inside_asks_value_set;
_day_change.Changed += day_change_Changed;
_animation.AnimationsChanged += on_animations_changed;
void on_animations_changed() => RaisePropertyChanged(nameof(animations));
_dispose = Disposable.Create(
_orders_model.on_security_root_orders(security.Id).OnDispatcher().Subscribe(_on_orders),
_position.OnDispatcher().Subscribe(v => _animation.animate_position(v.newval > v.oldval)),
_inside_bids.OnDispatcher().Subscribe(v => _animation.animate_working_bids_inside(v.newval)),
_inside_asks.OnDispatcher().Subscribe(v => _animation.animate_working_asks_inside(v.newval)),
_day_change.OnDispatcher().Subscribe(v => _animation.animate_day_change(v.newval > v.oldval)),
Disposable.Create(() => _animation.AnimationsChanged -= on_animations_changed)
Disposable.Create(() => {
_position.value_set -= position_value_set;
_inside_bids.value_set -= inside_bids_value_set;
_inside_asks.value_set -= inside_asks_value_set;
_day_change.Changed -= day_change_Changed;
_animation.AnimationsChanged -= on_animations_changed;
})
);
void position_value_set((int oldval, int newval) v) => _animation.animate_position(v.newval > v.oldval);
void inside_bids_value_set((bool oldval, bool newval) v) => _animation.animate_working_bids_inside(v.newval);
void inside_asks_value_set((bool oldval, bool newval) v) => _animation.animate_working_asks_inside(v.newval);
void day_change_Changed(double oldval, double newval) => _animation.animate_day_change(newval > oldval);
void on_animations_changed() => RaisePropertyChanged(nameof(animations));
}
public event Action invalidated;
@@ -129,27 +139,52 @@ namespace mbts.spark {
public void raise_invalidated() => invalidated?.Invoke();
public void refresh() {
/// <summary>
/// Returns a list of IDisposable 'update jobs.' Each item represents a job that will be executed when disposed; all jobs should be executed on the dispatcher thread.
/// </summary>
// Note: not guarenteed to be on the dispatcher thread, so all view-notifying actions should be wrapped in a IDisposable.
// Having said that, perform as much work as possible locally.
public List<IDisposable> refresh() {
var update_jobs = new List<IDisposable>();
bool dirty = false;
if (md.md.get_bbo(ref _bbo_ref, out NativePriceSize b, out NativePriceSize a)) {
_bid_price.set(b.price);
_bid_size.set(b.size);
_ask_price.set(a.price);
_ask_size.set(a.size);
_inside_bids.on_next_distinct(_bid_price.Value >= _working_best_bid.value, ref dirty);
_inside_asks.on_next_distinct(_ask_price.Value <= _working_best_ask.value, ref dirty);
prop_job(_bid_price, b.price);
prop_job(_bid_size, b.size);
prop_job(_ask_price, a.price);
prop_job(_ask_size, a.size);
cell_prop_job(_inside_bids, b.price >= _working_best_bid.value);
cell_prop_job(_inside_asks, a.price >= _working_best_ask.value);
}
if (md.md.get_last_trade(ref _last_ref, out NativePriceSize ps, out DateTime ts, out ulong v)) {
_last_price.set(ps.price);
_last_size.on_next_distinct(ps.size, ref dirty);
_volume.on_next_distinct(v, ref dirty);
double new_last_price = last_price;
if (md.md.get_last_trade(ref _last_ref, out NativePriceSize ps, out ulong v)) {
new_last_price = ps.price;
prop_job(_last_price, new_last_price);
cell_prop_job(_last_size, ps.size);
cell_prop_job(_volume, v);
}
double new_settlement = md.md.settlement_price;
prop_job(_settlement, new_settlement);
prop_job(_session_low, md.md.session_low);
prop_job(_session_high, md.md.session_high);
prop_job(_day_change, new_last_price - new_settlement);
if (dirty) {
update_jobs.Add(Disposable.Create(() => {
invalidated?.Invoke();
}));
}
return update_jobs;
void prop_job<T>(Property<T> p, T newval) {
var uj = p.set_as_job(newval);
if (uj != null)
update_jobs.Add(uj);
}
void cell_prop_job<T>(CellProperty<T> p, T newval) {
var uj = p.on_next_distinct_as_job(newval, ref dirty);
if (uj != null)
update_jobs.Add(uj);
}
_settlement.set(md.md.settlement_price);
_session_low.set(md.md.session_low);
_session_high.set(md.md.session_high);
_day_change.set(last_price - settlement);
if (dirty)
invalidated?.Invoke();
}
public bool Equals(Row other) =>
@@ -3,10 +3,13 @@ using mbts.spark.filters;
using mbts.ui;
using mbts.ui.utility;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;
using static bts.shell.MarketViewWindowFactory;
@@ -14,7 +17,7 @@ using static mbts.ui.controls.DIYGrid;
using static mbts.ui.spark.MarketViewView;
namespace mbts.spark {
partial class MarketViewVM : SecurityMDGrid<MarketViewVM.Row, SecurityMD> {
partial class MarketViewVM : SecurityMDGrid<MarketViewVM.Row, SecurityMD>, IPrepareUpdateForDispatcher {
readonly IBus _bus;
readonly OrdersModel _orders_model;
@@ -112,8 +115,7 @@ namespace mbts.spark {
}), DataGridFilterHelper.hook_filters(rows_view,
security_filter, bid_price_filter, bid_size_filter, ask_price_filter, ask_size_filter, last_price_filter, last_size_filter, day_change_filter, settlement_filter,
session_low_filter, session_high_filter, position_filter, working_bid_volume_filter, working_ask_volume_filter, volume_filter, security_key_filter),
securities_connectable.Connect(),
WorkLoop.Start("MarketView", _refresh)
securities_connectable.Connect()
);
}
@@ -159,6 +161,25 @@ namespace mbts.spark {
}
#endregion
public IDisposable prepare_update() {
ConcurrentBag<List<IDisposable>> to_update = new ConcurrentBag<List<IDisposable>>();
try {
Parallel.For(0, _row_vms.Count, i => {
var r = _row_vms[i];
var update_jobs = r.refresh();
if (update_jobs.Count > 0)
to_update.Add(update_jobs);
});
} catch (IndexOutOfRangeException) {
// since rows (securities) will only be added/removed through rare user intervention,
// it is worth catching the occassional exception in exchange for lock-free code.
// if a row misses an update, or gets updated more than once, that's okay too;
// in 100ms it will tick again, so the data should never be more than a few 100 ms old, and rarely older than 100.
}
// return null, rather than an empty IDisposable; this signals to the UpdateMaster that it may skip the call to Dispatcher.InvokeAsync() as there is nothing to dispose.
return to_update.Count == 0 ? null : Disposable.Create(() => to_update.SelectMany(_ => _).DisposeAll());
}
protected override Row row_factory(SecurityMD smd) => new Row(_bus, smd, _orders_model);
protected override void on_row_removed(Row row) => row.Dispose();
protected override IEnumerable<SortingCriteria> get_default_sorting() { yield return new SortingCriteria(nameof(Row.security), true); }
@@ -167,4 +188,4 @@ namespace mbts.spark {
foreach (var r in _row_vms) r.refresh();
}
}
}
}
@@ -14,16 +14,18 @@ using System.Windows;
namespace mbts.spark {
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>, ISharedWindow {
const string _window_type = "MarketView", _thread_id = "MarketView", _window_type_display_name = "Market View";
readonly DispatcherUpdateMaster _dispatcher_update_master;
readonly OrdersModel _orders_model;
readonly AllowOnce _init_guard = new AllowOnce("MarketView");
MarketViewVM _vm;
MarketViewView _view;
MarketViewWindowFactory.StartupData _state;
IReadOnlyList<SparkCommand> _commands;
IDisposable _subscriptions;
IDisposable _resources;
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state)
public MarketViewWindow(MarketViewWindowFactory factory, DispatcherUpdateMaster dispatcher_update_master, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state)
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
_dispatcher_update_master = dispatcher_update_master;
_orders_model = orders_model;
_state = state;
Icon.Freeze();
@@ -57,12 +59,14 @@ namespace mbts.spark {
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs)),
SparkCommands.change_securities_command(() => (update_securities_cmd, _state.security_criteria))
};
_subscriptions = _bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(c =>
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states())));
_resources = Disposable.Create(
_bus.subscribe(update_securities_cmd.tag).OnDispatcher().Subscribe(
c => _state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states()))),
_dispatcher_update_master.attach(_vm));
}
public override void Dispose() {
Disposable.dispose(ref _subscriptions);
Disposable.dispose(ref _resources);
_vm.Dispose();
Commands.OfType<IDisposable>().DisposeAll();
}
@@ -12,6 +12,7 @@ namespace bts.shell {
class MarketViewWindowFactory : ShellWindowFactory<MarketViewWindowFactory.StartupData> {
readonly IBus _bus;
readonly OrdersModel _orders_model;
readonly DispatcherUpdateMaster _dispatcher_update_master = new DispatcherUpdateMaster("MarketView DispatcherUpdateMaster");
public MarketViewWindowFactory(MarketShellModule module, IBus bus, OrdersModel orders_model) : base(module, "MarketView") {
_bus = bus;
@@ -25,7 +26,7 @@ namespace bts.shell {
void _show(SecurityCriteria criteria) =>
_bus.post(SparkTags.ShowWindow, _create(new StartupData(criteria, new List<SortingCriteria> { new SortingCriteria(nameof(MarketViewVM.Row.security), true) }, null)));
IShellWindow _create(StartupData state) => new MarketViewWindow(this, _bus, _orders_model, state);
IShellWindow _create(StartupData state) => new MarketViewWindow(this, _dispatcher_update_master, _bus, _orders_model, state);
[ProtoContract]
public class StartupData {
@@ -121,7 +121,7 @@ namespace mbts.spark {
public Row(TimeAndSalesVM parent, SecurityImmutable security, TradeMessageImmutable trade) : base(security) {
_trade_key = trade.Key;
time = ((ulong)trade.Time).lldatetime().ToLocalTime();
time = trade.Time.lldatetime();
price = trade.Mantissa * Math.Pow(10, trade.Exponent);
size = trade.Size;
aggressor_side = trade.AggressorSide;
@@ -42,7 +42,7 @@ namespace bts.ui {
_bus = bus;
}
public ImageSource icon => Icons.Admin;
public ImageSource icon => Icons.AdminFrozen;
public string name => "Settings";
@@ -47,7 +47,7 @@ namespace mbts.spark {
where T : struct =>
new OrderBookCellStore<T?>(row, column, prop_name, get_value, select_other, format, CompareUtils.nullable_comparison(compare), EqualityComparer.CreateNullable<T>(EqualityComparer<T>.Default.Equals));
class OrderBookCellStore<T> : IOrderBookCellStore<T>, INotifyPropertyChanged {
class OrderBookCellStore<T> : IOrderBookCellStore<T> {
readonly OrderBookRowVM _row;
readonly ICVOrderBookColumn _column;
readonly string _prop_name;
@@ -58,10 +58,6 @@ namespace mbts.spark {
readonly IEqualityComparer<T> _eq_comparer;
OrderBookCell<T> _current;
// Property changed handler to avoid WPF binding leak
// https://blog.jetbrains.com/dotnet/2014/09/04/fighting-common-wpf-memory-leaks-with-dotmemory/
public event PropertyChangedEventHandler PropertyChanged = (_,__) => { };
public OrderBookCellStore(
OrderBookRowVM row,
ICVOrderBookColumn column,
@@ -15,7 +15,7 @@ using OrderStatus = bts.api.orderman.OrderStatus;
namespace mbts.spark {
partial class OrderBookRowVM : DisposableViewModelBase, IEquatable<OrderBookRowVM>, DIYGrid.IRowVM<OrderRecordTuple>, DIYGrid.IActionableRowVM {
//public readonly PredicateAnimator<OrderBookRowVM, OrderRecordTuple> animator;
public readonly PredicateAnimator<OrderBookRowVM, OrderRecordTuple> animator;
const string _unknown_security_price_format_string = "0.#########";
const int _unknown_security_price_num_fractional_digits = 9;
@@ -30,66 +30,31 @@ namespace mbts.spark {
string _symbol;
OrderBookRowVM _parent;
OrderBookVM.BranchFilterSource _branch_filter_source;
IDisposable _branch_filter_sub;
// Core
IOrderBookCellStore<uint> _order_key_store_backing;
IOrderBookCellStore<uint> _parent_order_key_store_backing;
IOrderBookCellStore<uint> _security_key_store_backing;
IOrderBookCellStore<SecurityImmutable?> _security_store_backing;
IOrderBookCellStore<Venue> _exchange_store_backing;
IOrderBookCellStore<string> _symbol_store_backing;
IOrderBookCellStore<uint> _order_key_store;
IOrderBookCellStore<uint> _parent_order_key_store;
IOrderBookCellStore<uint> _security_key_store;
IOrderBookCellStore<SecurityImmutable?> _security_store;
IOrderBookCellStore<Venue> _exchange_store;
IOrderBookCellStore<string> _symbol_store;
// Def
IOrderBookCellStore<CustomOrderDefinition.CustomOneofCase> _order_type_store_backing;
IOrderBookCellStore<Side> _side_store_backing;
IOrderBookCellStore<string> _user_store_backing;
IOrderBookCellStore<string> _account_store_backing;
IOrderBookCellStore<TimeInForce> _time_in_force_store_backing;
IOrderBookCellStore<DateTime> _time_created_store_backing;
IOrderBookCellStore<CustomOrderDefinition.CustomOneofCase> _order_type_store;
IOrderBookCellStore<Side> _side_store;
IOrderBookCellStore<string> _user_store;
IOrderBookCellStore<string> _account_store;
IOrderBookCellStore<TimeInForce> _time_in_force_store;
IOrderBookCellStore<DateTime> _time_created_store;
// State
IOrderBookCellStore<DateTime> _last_change_store_backing;
IOrderBookCellStore<OrderStatus> _status_store_backing;
IOrderBookCellStore<double> _price_store_backing;
IOrderBookCellStore<uint> _quantity_store_backing;
IOrderBookCellStore<uint> _filled_quantity_store_backing;
IOrderBookCellStore<double> _last_price_store_backing;
IOrderBookCellStore<double> _avg_price_store_backing;
IOrderBookCellStore<string> _last_msg_store_backing;
IOrderBookCellStore<OrderSubType> _order_sub_type_store_backing;
// Core
IOrderBookCellStore<uint> _order_key_store => _order_key_store_backing ?? (_order_key_store_backing = CellFactory.create(this, _book_vm.columns.order_key_column, nameof(order_key), r => r._order.Key, r => r._order_key_store));
IOrderBookCellStore<uint> _parent_order_key_store => _parent_order_key_store_backing ?? (_parent_order_key_store_backing = CellFactory.create(this, _book_vm.columns.parent_key_column, nameof(parent_order_key), r => r._order.ParentKey, r => r._parent_order_key_store));
IOrderBookCellStore<uint> _security_key_store => _security_key_store_backing ?? (_security_key_store_backing = CellFactory.create(this, _book_vm.columns.security_key_column, nameof(security_key), r => r._order.security_key(), r => r._security_key_store));
IOrderBookCellStore<SecurityImmutable?> _security_store => _security_store_backing ?? (_security_store_backing = CellFactory.create_nullable(this, _book_vm.columns.security_column, nameof(security), r => r._security, r => r._security_store, v => v.display_name()));
IOrderBookCellStore<Venue> _exchange_store => _exchange_store_backing ?? (_exchange_store_backing = CellFactory.create(this, _book_vm.columns.exchange_column, nameof(exchange), r => r._security.HasValue ? _security.Value.Exchange : Venue.Unset, r => r._exchange_store));
IOrderBookCellStore<string> _symbol_store => _symbol_store_backing ?? (_symbol_store_backing = CellFactory.create(this, _book_vm.columns.symbol_column, nameof(symbol), r => r._symbol, r => r._symbol_store, v => !String.IsNullOrEmpty(v) ? v : "unknown symbol"));
// Def
IOrderBookCellStore<CustomOrderDefinition.CustomOneofCase> _order_type_store => _order_type_store_backing ?? (_order_type_store_backing = CellFactory.create(this, _book_vm.columns.order_type_column, nameof(order_type), r => r._order.order_type(), r => r._order_type_store));
IOrderBookCellStore<Side> _side_store => _side_store_backing ?? (_side_store_backing = CellFactory.create(this, _book_vm.columns.side_column, nameof(side), r => r._order.side(), r => r._side_store));
IOrderBookCellStore<string> _user_store => _user_store_backing ?? (_user_store_backing = CellFactory.create(this, _book_vm.columns.user_column, nameof(user), r => _order.user(), r => r._user_store));
IOrderBookCellStore<string> _account_store => _account_store_backing ?? (_account_store_backing = CellFactory.create(this, _book_vm.columns.account_column, nameof(account), r => r._order.account(), r => r._account_store));
IOrderBookCellStore<TimeInForce> _time_in_force_store => _time_in_force_store_backing ?? (_time_in_force_store_backing = CellFactory.create(this, _book_vm.columns.time_in_force_column, nameof(time_in_force), r => r._order.time_in_force(), r => r._time_in_force_store));
IOrderBookCellStore<DateTime> _time_created_store => _time_created_store_backing ?? (_time_created_store_backing = CellFactory.create(this, _book_vm.columns.time_created_column, nameof(time_created), r => r._order.created(), r => r._time_created_store, format_date));
// State
IOrderBookCellStore<DateTime> _last_change_store => _last_change_store_backing ?? (_last_change_store_backing = CellFactory.create(this, _book_vm.columns.last_change_column, nameof(last_change), r => _order.last_change(), r => r._last_change_store, format_date));
IOrderBookCellStore<OrderStatus> _status_store => _status_store_backing ?? (_status_store_backing = CellFactory.create(this, _book_vm.columns.status_column, nameof(status), r => _order.status(), r => r._status_store));
IOrderBookCellStore<double> _price_store => _price_store_backing ?? (_price_store_backing = CellFactory.create(this, _book_vm.columns.price_column, nameof(price), r => _order.price(), r => r._price_store, format_price));
IOrderBookCellStore<uint> _quantity_store => _quantity_store_backing ?? (_quantity_store_backing = CellFactory.create(this, _book_vm.columns.qty_column, nameof(quantity), r => _order.quantity(), r => r._quantity_store));
IOrderBookCellStore<uint> _filled_quantity_store => _filled_quantity_store_backing ?? (_filled_quantity_store_backing = CellFactory.create(this, _book_vm.columns.filled_qty_column, nameof(filled_quantity), r => _order.filled(), r => r._filled_quantity_store));
IOrderBookCellStore<double> _last_price_store => _last_price_store_backing ?? (_last_price_store_backing = CellFactory.create(this, _book_vm.columns.last_price_column, nameof(last_price), r => _order.last_price(), r => r._last_price_store, format_price));
IOrderBookCellStore<double> _avg_price_store => _avg_price_store_backing ?? (_avg_price_store_backing = CellFactory.create(this, _book_vm.columns.avg_price_column, nameof(avg_price), r => _order.avg_price(), r => r._avg_price_store, format_price));
IOrderBookCellStore<string> _last_msg_store => _last_msg_store_backing ?? (_last_msg_store_backing = CellFactory.create(this, _book_vm.columns.last_message_column, nameof(last_msg), r => _order.last_msg(), r => r._last_msg_store));
IOrderBookCellStore<OrderSubType> _order_sub_type_store => _order_sub_type_store_backing ?? (_order_sub_type_store_backing = CellFactory.create(this, _book_vm.columns.sub_type_column, nameof(order_sub_type), r => r._order.sub_type(), r => r._order_sub_type_store, format_sub_type));
static string format_date(DateTime v) => $"{v:M/d} {v:h:mm:ss.fff tt}";
string format_price(double v) => _security.HasValue ?
_security.Value.ContractSpec.format(v) :
Math.Round(v, _unknown_security_price_num_fractional_digits, MidpointRounding.AwayFromZero).ToString(_unknown_security_price_format_string);
static string format_sub_type(OrderSubType v) {
switch (v) {
case OrderSubType.HedgingLeg: return "H";
case OrderSubType.QuotingLeg: return "Q";
default: return string.Empty;
}
}
IOrderBookCellStore<DateTime> _last_change_store;
IOrderBookCellStore<OrderStatus> _status_store;
IOrderBookCellStore<double> _price_store;
IOrderBookCellStore<uint> _quantity_store;
IOrderBookCellStore<uint> _filled_quantity_store;
IOrderBookCellStore<double> _last_price_store;
IOrderBookCellStore<double> _avg_price_store;
IOrderBookCellStore<string> _last_msg_store;
IOrderBookCellStore<OrderSubType> _order_sub_type_store;
public OrderBookRowVM(IBus bus, OrderBookVM source_vm, OrdersModel model, OrderRecordImmutable record, SecurityMD? s) {
_book_vm = source_vm;
@@ -97,7 +62,43 @@ namespace mbts.spark {
_order = record;
_security_md = s;
_symbol = _security_md.HasValue ? _security_md.Value.security.Symbol : null;
//animator = new PredicateAnimator<OrderBookRowVM,OrderRecordTuple>(this);
animator = new PredicateAnimator<OrderBookRowVM,OrderRecordTuple>(this);
// cells
cell_stores = new DIYGrid.IGridCellStore[] {
_security_store = CellFactory.create_nullable(this, _book_vm.columns.security_column, nameof(security), r => r._security, r => r._security_store, v => v.display_name()),
_security_key_store = CellFactory.create(this, _book_vm.columns.security_key_column, nameof(security_key), r => r._order.security_key(), r => r._security_key_store),
_parent_order_key_store = CellFactory.create(this, _book_vm.columns.parent_key_column, nameof(parent_order_key), r => r._order.ParentKey, r => r._parent_order_key_store),
_order_key_store = CellFactory.create(this, _book_vm.columns.order_key_column, nameof(order_key), r => r._order.Key, r => r._order_key_store),
_symbol_store = CellFactory.create(this, _book_vm.columns.symbol_column, nameof(symbol), r => r._symbol, r => r._symbol_store, v => !String.IsNullOrEmpty(v) ? v : "unknown symbol"),
_time_created_store = CellFactory.create(this, _book_vm.columns.time_created_column, nameof(time_created), r => r._order.created(), r => r._time_created_store, format_date),
_user_store = CellFactory.create(this, _book_vm.columns.user_column, nameof(user), r => _order.user(), r => r._user_store),
_account_store = CellFactory.create(this, _book_vm.columns.account_column, nameof(account), r => r._order.account(), r => r._account_store),
_exchange_store = CellFactory.create(this, _book_vm.columns.exchange_column, nameof(exchange), r => r._security.HasValue ? _security.Value.Exchange : Venue.Unset, r => r._exchange_store),
_time_in_force_store = CellFactory.create(this, _book_vm.columns.time_in_force_column, nameof(time_in_force), r => r._order.time_in_force(), r => r._time_in_force_store),
_order_type_store = CellFactory.create(this, _book_vm.columns.order_type_column, nameof(order_type), r => r._order.order_type(), r => r._order_type_store),
_order_sub_type_store = CellFactory.create(this, _book_vm.columns.sub_type_column, nameof(order_sub_type), r => r._order.sub_type(), r => r._order_sub_type_store, format_sub_type),
_side_store = CellFactory.create(this, _book_vm.columns.side_column, nameof(side), r => r._order.side(), r => r._side_store),
_last_change_store = CellFactory.create(this, _book_vm.columns.last_change_column, nameof(last_change), r => _order.last_change(), r => r._last_change_store, format_date),
_price_store = CellFactory.create(this, _book_vm.columns.price_column, nameof(price), r => _order.price(), r => r._price_store, format_price),
_quantity_store = CellFactory.create(this, _book_vm.columns.qty_column, nameof(quantity), r => _order.quantity(), r => r._quantity_store),
_filled_quantity_store = CellFactory.create(this, _book_vm.columns.filled_qty_column, nameof(filled_quantity), r => _order.filled(), r => r._filled_quantity_store),
_avg_price_store = CellFactory.create(this, _book_vm.columns.avg_price_column, nameof(avg_price), r => _order.avg_price(), r => r._avg_price_store, format_price),
_last_price_store = CellFactory.create(this, _book_vm.columns.last_price_column, nameof(last_price), r => _order.last_price(), r => r._last_price_store, format_price),
_status_store = CellFactory.create(this, _book_vm.columns.status_column, nameof(status), r => _order.status(), r => r._status_store),
_last_msg_store = CellFactory.create(this, _book_vm.columns.last_message_column, nameof(last_msg), r => _order.last_msg(), r => r._last_msg_store),
};
//
string format_price(double v) => _security.HasValue ? _security.Value.ContractSpec.format(v) :
Math.Round(v, _unknown_security_price_num_fractional_digits, MidpointRounding.AwayFromZero).ToString(_unknown_security_price_format_string);
string format_date(DateTime v) => $"{v:M/d} {v:h:mm:ss.fff tt}";
string format_sub_type(OrderSubType v) {
switch (v) {
case OrderSubType.HedgingLeg: return "H";
case OrderSubType.QuotingLeg: return "Q";
default: return string.Empty;
}
}
//
_visible = backing(nameof(is_visible), true);
_expanded = backing(nameof(is_expanded), false);
_depth_level = backing(nameof(depth_level), () => {
@@ -113,11 +114,15 @@ namespace mbts.spark {
_show_order_modify_cmd = new ObservantCommand(() => bus.post(SparkTags.ShowModifyOrderTag, _order.Key));
_show_order_modify_cmd.OnNext(status.value == OrderStatus.Working);
row_passes_filter = _book_vm.columns.filter(this);
_passes_filter = backing(nameof(passes_filter), row_passes_filter);
_passes_filter = backing(nameof(passes_filter),
Observable.FromEvent<Action<IObservable<bool>>, IObservable<bool>>(
cb => _branch_filter_source_changed += cb, cb => _branch_filter_source_changed -= cb)
.SelectMany(o => o));
}
public event Action invalidated;
public event Action<(OrderRecordTuple old_val, OrderRecordTuple new_val)> updated;
event Action<IObservable<bool>> _branch_filter_source_changed;
public OrderRecordImmutable order => _order;
public OrderMetadata order_metadata => _model.factory.get_metadata(order_type.value);
@@ -212,31 +217,7 @@ namespace mbts.spark {
}
public ObservableCollection<RowAnimation> row_animations { get; } = new ObservableCollection<RowAnimation>();
DIYGrid.IGridCellStore[] _cell_stores_backing;
public IReadOnlyList<DIYGrid.IGridCellStore> cell_stores => _cell_stores_backing ?? (_cell_stores_backing = new DIYGrid.IGridCellStore[] {
_security_store,
_security_key_store,
_parent_order_key_store,
_order_key_store,
_symbol_store,
_time_created_store,
_user_store,
_account_store,
_exchange_store,
_time_in_force_store,
_order_type_store,
_order_sub_type_store,
_side_store,
_last_change_store,
_price_store,
_quantity_store,
_filled_quantity_store,
_avg_price_store,
_last_price_store,
_status_store,
_last_msg_store
});
public IReadOnlyList<DIYGrid.IGridCellStore> cell_stores { get; }
public static Stack<OrderBookRowVM> lineage(OrderBookRowVM node) {
var rv = new Stack<OrderBookRowVM>();
@@ -247,15 +228,11 @@ namespace mbts.spark {
}
public void join_branch_filter_source(OrderBookVM.BranchFilterSource new_branch_filter_source) {
if (_branch_filter_source != null) {
_branch_filter_source.passes_filter_changed -= _passes_filter.set;
}
_branch_filter_source = new_branch_filter_source;
_branch_filter_source.set_row_passes_filter(_order.Key, row_passes_filter);
_branch_filter_source_changed?.Invoke(_branch_filter_source);
foreach (var child_row in _child_rows)
child_row.join_branch_filter_source(_branch_filter_source);
_passes_filter.set(_branch_filter_source.passes_filter);
_branch_filter_source.passes_filter_changed += _passes_filter.set;
}
/// <summary>
@@ -334,13 +311,7 @@ namespace mbts.spark {
public override int GetHashCode() => (int)_order.Key;
public override string ToString() => $"{order_key} {last_change} {parent_order_key}";
protected override void Dispose(bool disposing) {
// idempotent
if (_branch_filter_source != null) {
_branch_filter_source.passes_filter_changed -= _passes_filter.set;
_branch_filter_source = null;
}
}
protected override void Dispose(bool disposing) => Disposable.dispose(ref _branch_filter_sub);
static IEnumerable<OrderBookRowVM> _descendents(OrderBookRowVM root) {
foreach (var child in root.child_orders) {
@@ -1,5 +1,4 @@
using bts;
using bts.api;
using bts.api;
using bts.api.core;
using bts.api.orderman;
using bts.shell;
@@ -28,7 +27,10 @@ namespace mbts.spark {
partial class OrderBookVM : DisposableViewModelBase, IHandleRowSelection {
readonly LiveSortingColumKeys _live_sorting_columns;
readonly Property<OrderBookRowVM> _current_row;
readonly Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)> _order_securities_subject =
new Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)>();
OrderBookStartupData _state;
Dictionary<uint, OrderBookRowVM> _order_rows_cache = new Dictionary<uint, OrderBookRowVM>();
Dictionary<uint, BranchFilterSource> _branch_filter_roots = new Dictionary<uint, BranchFilterSource>();
ObservableCollection<OrderBookRowVM> _order_rows = new ObservableCollection<OrderBookRowVM>();
@@ -37,8 +39,7 @@ namespace mbts.spark {
SetMap<uint, uint> _security_orders = new SetMap<uint, uint>();
public OrderBookVM(IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model) {
var order_securities_subject = new Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)>();
columns = new OrderBookColumnCollection(bus, model, order_securities_subject);
columns = new OrderBookColumnCollection(bus, model, _order_securities_subject);
rows_view = CollectionViewSource.GetDefaultView(_order_rows);
rows_view.Filter = obj => obj is OrderBookRowVM vm && vm.is_visible && vm.passes_filter;
columns.filter_changed += () => {
@@ -70,12 +71,11 @@ namespace mbts.spark {
_dispose = Disposable.Create(
bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(on_securities),
model.on_orders.OnDispatcher().Subscribe(on_orders),
model.on_orders.OnDispatcher().AddRemoveOnly().Subscribe(on_orders),
grid_settings_manager,
columns,
order_securities_subject);
columns);
void on_orders(TableUpdate<OrderRecordImmutable, uint> table) {
void on_orders(LegacyTableUpdate<OrderRecordImmutable, uint> table) {
// clear everything (most likely the server restarted)
if (table.removed.Count() == _order_rows.Count && !table.added.Any()) {
_selected_rows.Clear();
@@ -86,14 +86,18 @@ namespace mbts.spark {
_parent_to_children.clear();
_security_orders.clear();
missing_security_orders.clear();
Log.Ui.warn("OrderBook received reset message.");
return;
}
var changed_keys = new HashSet<uint>();
var added_securities = new List<SecurityImmutable>(1);
var removed_securities = new List<SecurityImmutable>(1);
foreach (var o in table.removed) {
if (_order_rows_cache.TryPopValue(o.Key, out OrderBookRowVM row)) {
// if state has it, then it wasn't actually removed, just changed
if (table.state.TryGetValue(o.Key, out OrderRecordImmutable changed_order)) {
changed_keys.Add(o.Key);
_order_rows_cache[o.Key].update_order(changed_order);
} else if (_order_rows_cache.TryPopValue(o.Key, out OrderBookRowVM row)) {
row.Dispose();
_order_rows.Remove(row);
if (_security_orders.removes_key(o.Def.SecurityKey, o.Key)) {
@@ -120,10 +124,10 @@ namespace mbts.spark {
missing_security_orders.remove(o.Key);
}
}
foreach (var ch in table.changes)
_order_rows_cache[ch.New.Key].update_order(ch.New);
var orphans = new Dictionary<uint, OrderBookRowVM>();
foreach (var o in table.added) {
if (changed_keys.Contains(o.Key))
continue;
SecurityMD? security_md;
if (security_dict.TryGetValue(o.Def.SecurityKey, out SecurityMD smd)) {
security_md = smd;
@@ -166,7 +170,7 @@ namespace mbts.spark {
_order_rows.Add(row);
}
if (added_securities.Count > 0 || removed_securities.Count > 0)
order_securities_subject.OnNext((added_securities, removed_securities));
_order_securities_subject.OnNext((added_securities, removed_securities));
void try_add_children(OrderBookRowVM row) {
if (_parent_to_children.try_lookup(row.order.Key, out ISet<uint> children))
@@ -202,7 +206,7 @@ namespace mbts.spark {
}
}
if (added_securities.Count > 0)
order_securities_subject.OnNext((added_securities, new List<SecurityImmutable>(0)));
_order_securities_subject.OnNext((added_securities, new List<SecurityImmutable>(0)));
}
}
@@ -239,9 +243,6 @@ namespace mbts.spark {
internal void remove_branch_filter_root(uint key) =>
_branch_filter_roots.Remove(key);
protected override void Dispose(bool disposing) =>
Disposable.dispose(_order_rows);
static GridSettingsManager<OrderBookRowVM, OrderRecordTuple, INotificationServiceDataCache, OrderRecordTuplePredicate> _create_grid_settings_manager(
IObservable<string> owner_title, IBus bus, INotificationService notification_service, OrdersModel model,
ObservableCollection<OrderBookRowVM> rows, ICollectionView rows_view, ObservableCollection<DIYGrid.ColumnBase> columns) {
@@ -296,21 +297,24 @@ namespace mbts.spark {
}
}
internal class BranchFilterSource {
internal class BranchFilterSource : IObservable<bool> {
readonly HashSet<uint> _orders_pass_filter = new HashSet<uint>();
public event Action<bool> passes_filter_changed;
public bool passes_filter => _orders_pass_filter.Count > 0;
event Action<bool> _passes_filter_changed;
public void set_row_passes_filter(uint key, bool value) {
if (value) {
if (_orders_pass_filter.Add(key) && _orders_pass_filter.Count == 1)
passes_filter_changed?.Invoke(value);
_passes_filter_changed?.Invoke(value);
} else if (_orders_pass_filter.Remove(key) && _orders_pass_filter.Count == 0) {
passes_filter_changed?.Invoke(value);
_passes_filter_changed?.Invoke(value);
}
}
public IDisposable Subscribe(IObserver<bool> observer) {
_passes_filter_changed += observer.OnNext;
observer.OnNext(_orders_pass_filter.Count > 0);
return Disposable.Create(() => _passes_filter_changed -= observer.OnNext);
}
}
class LiveSortingColumKeys : HashSet<Columns> {
@@ -15,9 +15,11 @@ namespace mbts.spark {
CVRowTemplateKey = "CVRowTemplateKey";
public const double RowHeight = 17;
OrderBookVM _vm;
public OrderBookView(OrderBookVM vm) {
InitializeComponent();
_vm = vm;
DataContext = vm;
}
}
@@ -1,6 +1,7 @@
using bts;
using bts.api;
using bts.api.orderman;
using bts.utils;
using mbts.ui;
using mbts.ui.shell;
using System;
@@ -11,6 +12,60 @@ using System.Linq;
using System.Reactive.Linq;
namespace mbts.spark {
struct OrderTemplateKey : IEquatable<OrderTemplateKey> {
public readonly CustomOrderDefinition.CustomOneofCase type;
public readonly string name;
public OrderTemplateKey(CustomOrderDefinition.CustomOneofCase type)
: this(type, OrderUtils.order_type_display_name(type)) { }
public OrderTemplateKey(CustomOrderDefinition.CustomOneofCase type, string name) {
this.type = type;
this.name = name;
}
public bool Equals(OrderTemplateKey other) =>
type == other.type &&
name == other.name;
public override bool Equals(object obj) =>
obj is OrderTemplateKey other && Equals(other);
public override int GetHashCode() =>
EqualityComparer.aggregate_hash(
type.GetHashCode(),
name == null ? 0 : name.GetHashCode());
public override string ToString() => $"{name}:{type}";
public static bool operator ==(OrderTemplateKey a, OrderTemplateKey b) => a.Equals(b);
public static bool operator !=(OrderTemplateKey a, OrderTemplateKey b) => !a.Equals(b);
}
struct OrderTemplateHeader : IEquatable<OrderTemplateHeader> {
public readonly OrderTemplateKey key;
// Note: display_name is NOT part of EQUALITY
public readonly string display_name;
public OrderTemplateHeader(CustomOrderDefinition.CustomOneofCase type)
: this(new OrderTemplateKey(type), OrderUtils.order_type_display_name(type)) { }
public OrderTemplateHeader(OrderTemplateKey key, string display_name) {
this.key = key;
this.display_name = display_name;
}
public bool Equals(OrderTemplateHeader other) =>
key.Equals(other.key);
public override bool Equals(object obj) =>
obj is OrderTemplateHeader other && Equals(other);
public override int GetHashCode() => key.GetHashCode();
public override string ToString() => $"{display_name} ({key})";
}
class InlineOrderProperties {
public readonly InlineUserProperty<AccountTuple> account;
public readonly InlineUserProperty<TimeInForce> time_in_force;
@@ -39,7 +94,7 @@ namespace mbts.spark {
class OrderPropertiesEditorVM : DisposableViewModelBase {
readonly OrdersModel _model;
readonly Property<SecurityImmutable> _security;
readonly SelectorProperty<AccountTuple> _accounts;
readonly SelectorProperty<AccountTuple, AccountTuple?> _accounts;
readonly SelectorProperty<TimeInForce> _time_in_force;
readonly SelectorProperty<IOrderTypeVM> _order_type;
IDisposable _order_type_param_errors_sub;
@@ -58,14 +113,21 @@ namespace mbts.spark {
nameof(selected_account),
model.factory.subscribe_account_tuples(),
order_properties.Select(ops => ops.account),
() => accounts?.FirstOrDefault() ?? default,
val => (!string.IsNullOrWhiteSpace(val.name) && accounts.Contains(val), "Please select an account"));
a => new AccountTuple?(a),
() => {
foreach (var a in accounts) {
return a;
}
return new AccountTuple?();
},
EqualityComparer<AccountTuple?>.Default,
new[] { new PropertyValidation<AccountTuple?>(a => (a.HasValue && !string.IsNullOrWhiteSpace(a.Value.name) && accounts.Contains(a.Value), "Please select an account")) }.ToReadOnlyList());
_order_type = backing(
nameof(selected_order_type),
security_source.Select(s => model.factory.default_order_type_defs(s).Select(create_order_type_vm)),
security_source.Select(s => model.factory.default_order_type_templates(s).Select(kv => create_order_type_vm(kv.Value, kv.Key.key.name, kv.Key.display_name))),
order_properties.Select(get_order_type_vm_prop),
get_default_order_type_vm,
(a, b) => a.order_type == b.order_type);
EqualityComparer.null_check_wrapper(OrderTypeVMExtensions.key_comparer));
_order_type.Changed += on_selected_order_type_changed;
_time_in_force = backing(
nameof(selected_time_in_force),
@@ -77,19 +139,22 @@ namespace mbts.spark {
tif => time_in_forces.Contains(tif));
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
// local methods
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) {
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd, string id, string display_name) {
IOrderTypeVM vm = null;
if(cd.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic)
vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source, cd.Dynamic.DynamicOrderType);
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source, cd.Dynamic.DynamicOrderType);
else
vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source);
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source);
vm.load_from(cd);
return vm;
}
InlineUserProperty<IOrderTypeVM> get_order_type_vm_prop(InlineOrderProperties p) {
if (!p.order_type_def.has_value)
return new InlineUserProperty<IOrderTypeVM>();
return new InlineUserProperty<IOrderTypeVM>(create_order_type_vm(p.order_type_def.value), p.order_type_def.force_override);
return new InlineUserProperty<IOrderTypeVM>(
create_order_type_vm(p.order_type_def.value, p.order_type_def.value.CustomCase.ToString(),
OrderUtils.order_type_display_name(p.order_type_def.value.CustomCase)),
p.order_type_def.force_override);
}
IOrderTypeVM get_default_order_type_vm() {
var ot = model.factory.default_order_type(security);
@@ -114,13 +179,13 @@ namespace mbts.spark {
public SecurityImmutable security => _security.Value;
public ReadOnlyObservableCollection<AccountTuple> accounts => _accounts.items;
public AccountTuple selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; }
public AccountTuple? selected_account { get => _accounts.selected_item; set => _accounts.selected_item = value; }
public ReadOnlyObservableCollection<TimeInForce> time_in_forces => _time_in_force.items;
public TimeInForce selected_time_in_force { get => _time_in_force.selected_item; set => _time_in_force.selected_item = value; }
public ReadOnlyObservableCollection<IOrderTypeVM> order_types => _order_type.items;
public IOrderTypeVM selected_order_type { get => _order_type.selected_item; set => _order_type.selected_item = value; }
public IObservable<AccountTuple> observe_account(bool start_immediately = false) =>
public IObservable<AccountTuple?> observe_account(bool start_immediately = false) =>
_accounts.observe_current(start_immediately);
public IObservable<TimeInForce> observe_time_in_force(bool start_immediately = false) =>
@@ -17,22 +17,22 @@ namespace mbts.spark
{
class InlineCoverOrderProperties
{
public InlineCoverOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
public InlineCoverOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
var hedgetype = CustomOneofCase.Limit;
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
hedge_ticks = new InlineUserProperty<int>(1, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> hedge_order_types;
public readonly InlineUserProperty<int> hedge_ticks;
@@ -42,25 +42,25 @@ namespace mbts.spark
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineCoverOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
IOrderTypeVM _hedge_order;
IDisposable _hedge_order_errors_sub;
public int hedge_ticks { get; set; } = 1;
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineCoverOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
new InlineCoverOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name)
{ }
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineCoverOrderProperties> inline_properties) : base(model)
IObservable<InlineCoverOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
@@ -75,11 +75,11 @@ namespace mbts.spark
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
{
_select_hedge_order(p.selected_hedge_order_type.value);
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
}
if (p.hedge_ticks.force_override)
{
@@ -116,7 +116,7 @@ namespace mbts.spark
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _child_order_choices;
}
}
@@ -126,7 +126,7 @@ namespace mbts.spark
get
{
if (_hedge_order_choices == null)
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _hedge_order_choices;
}
}
@@ -200,7 +200,7 @@ namespace mbts.spark
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -216,7 +216,7 @@ namespace mbts.spark
void _select_hedge_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
_inline_props.selected_hedge_order_type.value :
_inline_props.selected_hedge_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_hedge_order(ot);
}
@@ -20,23 +20,23 @@ namespace mbts.spark
public static void add_dynamic_order_defs(List<CustomOrderDefinitionImmutable> defs,
Func<CustomOrderDefinition.CustomOneofCase, CustomOrderDefinition> factory)
{
var dyntypes = _list.Value;
foreach (var d in dyntypes.dynamic_orders)
{
var dorder = factory(CustomOrderDefinition.CustomOneofCase.Dynamic);
dorder.Dynamic.DynamicOrderType = d.type_name;
foreach (var p in d.parameter_keys)
dorder.Dynamic.Parameters.Add(p, string.Empty);
defs.Add(new CustomOrderDefinitionImmutable(dorder));
}
// var dyntypes = _list.Value;
// foreach (var d in dyntypes.dynamic_orders)
// {
// var dorder = factory(CustomOrderDefinition.CustomOneofCase.Dynamic);
// dorder.Dynamic.DynamicOrderType = d.type_name;
// foreach (var p in d.parameter_keys)
// dorder.Dynamic.Parameters.Add(p, string.Empty);
// defs.Add(new CustomOrderDefinitionImmutable(dorder));
// }
}
internal static List<NameValueVM> get_parameters(string dyntype)
{
List<NameValueVM> par = new List<NameValueVM>();
var order = _list.Value.dynamic_orders.FirstOrDefault(x => x.type_name == dyntype);
if (order != null)
par.AddRange(order.parameter_keys.Select(x => new NameValueVM() { name = x, value = string.Empty }));
// var order = _list.Value.dynamic_orders.FirstOrDefault(x => x.type_name == dyntype);
// if (order != null)
// par.AddRange(order.parameter_keys.Select(x => new NameValueVM() { name = x, value = string.Empty }));
return par;
}
}
@@ -53,14 +53,14 @@ namespace mbts.spark
{
class InlineDynamicOrderProperties
{
public InlineDynamicOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
public InlineDynamicOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
}
class DynamicOrderVM : OrderTypeVMBase<DynamicOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
@@ -68,8 +68,8 @@ namespace mbts.spark
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineDynamicOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
@@ -77,13 +77,13 @@ namespace mbts.spark
string _dyn_type;
List<NameValueVM> _parameters;
public DynamicOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string dyn_type)
public DynamicOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string dyn_type, string id, string display_name)
: this(model, security_md_source, dyn_type, security_md_source.Select(smd =>
new InlineDynamicOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
new InlineDynamicOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name)
{ }
public DynamicOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string dyn_type,
IObservable<InlineDynamicOrderProperties> inline_properties) : base(model)
IObservable<InlineDynamicOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dyn_type = dyn_type;
@@ -100,7 +100,7 @@ namespace mbts.spark
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
_inline_props = p;
_validate();
@@ -115,17 +115,17 @@ namespace mbts.spark
public override void assign_to(CustomOrderDefinition value)
{
if (_child_order == null)
_select_child_order(model.factory.default_order_type(_security.Value, true));
_validate();
if (!HasErrors)
{
var i = factory.dynamic_order();
i.DynamicOrderType = _dyn_type;
set_parameter_dictionary(i.Parameters);
child_order.assign_to(i.Child);
value.Dynamic = i;
}
// if (_child_order == null)
// _select_child_order(model.factory.default_order_type(_security.Value, true));
// _validate();
// if (!HasErrors)
// {
// var i = factory.dynamic_order();
// i.DynamicOrderType = _dyn_type;
// set_parameter_dictionary(i.Parameters);
// child_order.assign_to(i.Child);
// value.Dynamic = i;
// }
}
void set_parameter_dictionary(Google.Protobuf.Collections.MapField<string, string> par)
@@ -139,7 +139,7 @@ namespace mbts.spark
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _child_order_choices;
}
}
@@ -185,7 +185,7 @@ namespace mbts.spark
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -208,4 +208,4 @@ namespace mbts.spark
}
}
}
}
}
@@ -1,11 +1,13 @@
using bts.api.orderman;
using bts.spark;
using bts.utils;
using System.Collections.Generic;
using System.Windows.Input;
using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark {
interface IOrderTypeVM : IPropertyWrapperVM<CustomOrderDefinition, CustomOrderDefinitionImmutable> {
string id { get; }
CustomOneofCase order_type { get; }
string type_name { get; }
bool is_bts_type { get; }
@@ -21,4 +23,12 @@ namespace mbts.spark {
ICollection<IOrderTypeVM> child_order_types { get; }
IOrderTypeVM child_order { get; }
}
static class OrderTypeVMExtensions {
public static IEqualityComparer<IOrderTypeVM> key_comparer =>
EqualityComparer.Create<IOrderTypeVM, OrderTemplateKey>(a => a.key());
public static OrderTemplateKey key(this IOrderTypeVM vm) =>
new OrderTemplateKey(vm.order_type, vm.id);
}
}
@@ -14,34 +14,34 @@ using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark {
static partial class OrderTypeVMFactory {
class InlineIcebergOrderProperties {
public InlineIcebergOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
public InlineIcebergOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types) {
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
show_quantity = new InlineUserProperty<double>(1, true);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<double> show_quantity;
}
class IcebergOrderVM : OrderTypeVMBase<IcebergOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineIcebergOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineIcebergOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
new InlineIcebergOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
public IcebergOrderVM(OrdersModel model,
IObservable<SecurityMD> security_md_source,
IObservable<InlineIcebergOrderProperties> inline_properties)
: base(model) {
IObservable<InlineIcebergOrderProperties> inline_properties, string id, string display_name)
: base(model, id, display_name) {
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_md_source.OnDispatcher()
@@ -52,7 +52,7 @@ namespace mbts.spark {
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) {
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (p.show_quantity.force_override) {
show_quantity = p.show_quantity.value;
@@ -68,7 +68,7 @@ namespace mbts.spark {
public ICollection<IOrderTypeVM> child_order_types {
get {
if (_child_order_choices == null) {
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
}
return _child_order_choices;
}
@@ -122,7 +122,7 @@ namespace mbts.spark {
void _select_child_order() {
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -17,19 +17,19 @@ namespace mbts.spark
{
class InlineJoinOrderProperties
{
public InlineJoinOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
public InlineJoinOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
var hedgetype = CustomOneofCase.Lean;
if(!child_order_types.Contains(hedgetype))
if(!child_order_types.Any(t => t.key.type == hedgetype))
hedgetype = CustomOneofCase.Limit;
//ExceptionAssert.InvalidOperation.That(child_order_types.Contains(hedgetype), $"Hedge order type {hedgetype} is not in hedge_order_types");
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
join_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
@@ -37,10 +37,10 @@ namespace mbts.spark
enable_hedge = new InlineUserProperty<bool>(false, false);
hedge_ticks = new InlineUserProperty<int>(1, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> hedge_order_types;
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
@@ -55,13 +55,13 @@ namespace mbts.spark
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineJoinOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
IOrderTypeVM _hedge_order;
IDisposable _hedge_order_errors_sub;
@@ -79,12 +79,12 @@ namespace mbts.spark
}
public int hedge_ticks { get; set; } = 1;
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineJoinOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
new InlineJoinOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineJoinOrderProperties> inline_properties) : base(model)
IObservable<InlineJoinOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
@@ -98,11 +98,11 @@ namespace mbts.spark
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
{
_select_hedge_order(p.selected_hedge_order_type.value);
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
}
if (p.tick_offset_on_deploy.force_override)
{
@@ -157,7 +157,7 @@ namespace mbts.spark
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _child_order_choices;
}
}
@@ -167,7 +167,7 @@ namespace mbts.spark
get
{
if (_hedge_order_choices == null)
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _hedge_order_choices;
}
}
@@ -241,7 +241,7 @@ namespace mbts.spark
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -257,7 +257,7 @@ namespace mbts.spark
void _select_hedge_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
_inline_props.selected_hedge_order_type.value :
_inline_props.selected_hedge_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_hedge_order(ot);
}
@@ -17,44 +17,41 @@ namespace mbts.spark
{
class InlineLeanOrderProperties
{
public InlineLeanOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
public InlineLeanOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
max_chase_ticks = new InlineUserProperty<uint>(2, false);
trade_toward_behavior = new InlineUserProperty<LeanOrderDefinition.Types.OnToward>(LeanOrderDefinition.Types.OnToward.Cancel, false);
max_chase_ticks = new InlineUserProperty<uint>(10, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
public readonly InlineUserProperty<uint> max_chase_ticks;
public readonly InlineUserProperty<LeanOrderDefinition.Types.OnToward> trade_toward_behavior;
}
class LeanOrderVM : OrderTypeVMBase<LeanOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineLeanOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
public uint max_chase_ticks { get; set; } = 2;
public LeanOrderDefinition.Types.OnToward trade_toward_behavior { get; set; } = LeanOrderDefinition.Types.OnToward.Cancel;
public uint max_chase_ticks { get; set; } = 10;
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineLeanOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
new InlineLeanOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineLeanOrderProperties> inline_properties) : base(model)
IObservable<InlineLeanOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
@@ -68,7 +65,7 @@ namespace mbts.spark
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (p.cross_threshold.force_override)
{
@@ -80,11 +77,6 @@ namespace mbts.spark
max_chase_ticks = p.max_chase_ticks.value;
RaisePropertyChanged(nameof(max_chase_ticks));
}
if (p.trade_toward_behavior.force_override)
{
trade_toward_behavior = p.trade_toward_behavior.value;
RaisePropertyChanged(nameof(trade_toward_behavior));
}
_inline_props = p;
_validate();
})
@@ -102,20 +94,17 @@ namespace mbts.spark
var i = factory.lean_order();
i.CrossThreshold = cross_threshold;
i.MaxChaseTicks = max_chase_ticks;
i.TradeTowardBehavior = trade_toward_behavior;
child_order.assign_to(i.Child);
value.Lean = i;
}
}
public IEnumerable<LeanOrderDefinition.Types.OnToward> trade_toward_types => EnumHelper.get_values_except<LeanOrderDefinition.Types.OnToward>();
}
public ICollection<IOrderTypeVM> child_order_types
{
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _child_order_choices;
}
}
@@ -161,7 +150,7 @@ namespace mbts.spark
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -3,7 +3,7 @@
namespace mbts.spark {
static partial class OrderTypeVMFactory {
private class LimitOrderVM : OrderTypeVMBase<LimitOrderDefinition> {
internal LimitOrderVM(OrdersModel model) : base(model) { }
internal LimitOrderVM(OrdersModel model) : base(model, "Limit", OrderUtils.order_type_display_name(CustomOrderDefinition.CustomOneofCase.Limit)) { }
public override void assign_to(CustomOrderDefinition value) =>
value.Limit = factory.limit_order();
@@ -3,7 +3,7 @@
namespace mbts.spark {
static partial class OrderTypeVMFactory {
private class MarketOrderVM : OrderTypeVMBase<MarketOrderDefinition> {
internal MarketOrderVM(OrdersModel model) : base(model) { }
internal MarketOrderVM(OrdersModel model) : base(model, "Market", OrderUtils.order_type_display_name(CustomOrderDefinition.CustomOneofCase.Market)) { }
public override void assign_to(CustomOrderDefinition parent) =>
parent.Market = factory.market_order();
@@ -12,36 +12,36 @@ namespace mbts.spark {
static partial class OrderTypeVMFactory {
public static IOrderTypeVM market(OrdersModel model) => new MarketOrderVM(model);
public static IOrderTypeVM limit(OrdersModel model) => new LimitOrderVM(model);
public static IOrderTypeVM stop(OrdersModel model) => new StopOrderVM(model);
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source) => new StopLimitOrderVM(model, security_md_source);
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TimeBoxOrderVM(model, security_md_source);
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TickOrderVM(model, security_md_source);
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source) => new LeanOrderVM(model, security_md_source);
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source) => new JoinOrderVM(model, security_md_source);
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TopOrderVM(model, security_md_source);
public static IOrderTypeVM cover(OrdersModel model, IObservable<SecurityMD> security_md_source) => new CoverOrderVM(model, security_md_source);
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TriggerOrderVM(model, security_md_source);
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source) => new IcebergOrderVM(model, security_md_source);
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source) => new SpreaderOrderVM(model, security_source);
public static IOrderTypeVM dynamic(OrdersModel model, IObservable<SecurityMD> security_md_source, string type) => new DynamicOrderVM(model, security_md_source, type);
public static IOrderTypeVM stop(OrdersModel model, string id, string display_name) => new StopOrderVM(model, id, display_name);
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new StopLimitOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TimeBoxOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TickOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new LeanOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new JoinOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TopOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM cover(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new CoverOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new TriggerOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name) => new IcebergOrderVM(model, security_md_source, id, display_name);
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source, string id, string display_name) => new SpreaderOrderVM(model, security_source, id, display_name);
public static IOrderTypeVM dynamic(OrdersModel model, IObservable<SecurityMD> security_md_source, string dynamic_type, string id, string display_name) => new DynamicOrderVM(model, security_md_source, dynamic_type, id, display_name);
public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model,
public static IOrderTypeVM create(CustomOneofCase order_type, string id, string display_name, OrdersModel model,
IObservable<SecurityMD> security_md_source, string dynamic_type = null ) {
switch (order_type) {
case CustomOneofCase.Market: return market(model);
case CustomOneofCase.Limit: return limit(model);
case CustomOneofCase.Stop: return stop(model);
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source);
case CustomOneofCase.TimeBox: return time_box(model, security_md_source);
case CustomOneofCase.Tick: return tick(model, security_md_source);
case CustomOneofCase.Lean: return lean(model, security_md_source);
case CustomOneofCase.Join: return join(model, security_md_source);
case CustomOneofCase.Top: return top(model, security_md_source);
case CustomOneofCase.Cover: return cover(model, security_md_source);
case CustomOneofCase.Trigger: return trigger(model, security_md_source);
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source);
case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security));
case CustomOneofCase.Dynamic: return dynamic(model, security_md_source, dynamic_type);
case CustomOneofCase.Stop: return stop(model, id, display_name);
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source, id, display_name);
case CustomOneofCase.TimeBox: return time_box(model, security_md_source, id, display_name);
case CustomOneofCase.Tick: return tick(model, security_md_source, id, display_name);
case CustomOneofCase.Lean: return lean(model, security_md_source, id, display_name);
case CustomOneofCase.Join: return join(model, security_md_source, id, display_name);
case CustomOneofCase.Top: return top(model, security_md_source, id, display_name);
case CustomOneofCase.Cover: return cover(model, security_md_source, id, display_name);
case CustomOneofCase.Trigger: return trigger(model, security_md_source, id, display_name);
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source, id, display_name);
case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security), id, display_name);
case CustomOneofCase.Dynamic: return dynamic(model, security_md_source, dynamic_type, id, display_name);
default: throw new NotImplementedException();
}
}
@@ -51,12 +51,17 @@ namespace mbts.spark {
protected readonly OrdersModel model;
protected OrderFactory factory => model.factory;
protected OrderTypeVMBase(OrdersModel model) => this.model = model;
protected OrderTypeVMBase(OrdersModel model, string id, string display_name) {
this.model = model;
this.id = id;
this.display_name = display_name;
}
public abstract CustomOneofCase order_type { get; }
public abstract string type_name { get; }
public bool is_bts_type => display_name.StartsWith("BTS");
public string display_name => OrderUtils.order_type_display_name(order_type);
public string id { get; }
public string display_name { get; }
public OrderMetadata meta => factory.get_metadata(order_type);
public virtual bool has_editable_properties => !meta.NoProps;
public virtual bool editable_properties_open { get; set; }
@@ -76,7 +81,7 @@ namespace mbts.spark {
}
private abstract class OrderTypeVMBase<T> : OrderTypeVMBase where T : Google.Protobuf.IMessage, new() {
protected OrderTypeVMBase(OrdersModel model) : base(model) { }
protected OrderTypeVMBase(OrdersModel model, string id, string display_name) : base(model, id, display_name) { }
public override CustomOneofCase order_type =>
OrderDataHelper.get_order_type(new T());
@@ -13,20 +13,20 @@ namespace mbts.spark {
readonly StrategyEditorWrapper _strategy_editor_wrapper;
IDisposable _dispose;
public SpreaderOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
public SpreaderOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source, string id, string display_name)
: this(model, security_source, security_source.SelectMany(s => model.spreader.security_strategy(s.Id)
.Select(strategy => {
var so = model.factory.spreader_order();
so.Security = s.CloneToMutable();
so.ExecutionStrategy = strategy.CloneToMutable();
return new InlineUserProperty<SpreaderOrderDefinitionImmutable>(so, true);
}))) { }
})), id, display_name) { }
public SpreaderOrderVM(
OrdersModel model,
IObservable<SecurityImmutable> security_source,
IObservable<InlineUserProperty<SpreaderOrderDefinitionImmutable>> order_template_source)
: base(model) {
IObservable<InlineUserProperty<SpreaderOrderDefinitionImmutable>> order_template_source, string id, string display_name)
: base(model, id, display_name) {
_template = backing(nameof(template), new SpreaderOrderDefinitionImmutable?());
_strategy_editor_wrapper = new StrategyEditorWrapper(this);
_dispose = Disposable.Create(
@@ -29,10 +29,11 @@ namespace mbts.spark {
InlineStopLimitOrderProperties _inline_props;
IDisposable _dispose;
internal StopLimitOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
: this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source)) { }
internal StopLimitOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source), id, display_name) { }
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties) : base(model) {
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties, string id, string display_name)
: base(model, id, display_name) {
_stop_price = new UserProperty<double>(backing(nameof(stop_price), double.NaN));
_dispose = Disposable.Create(
spec_source.OnDispatcher().Subscribe(s => {
@@ -6,8 +6,7 @@ namespace mbts.spark {
private class StopOrderVM : OrderTypeVMBase<StopOrderDefinition> {
double _stop_price = 0;
internal StopOrderVM(OrdersModel model) : base(model) { }
internal StopOrderVM(OrdersModel model, string id, string display_name) : base(model, id, display_name) { }
public double stop_price {
get => _stop_price;
@@ -17,11 +17,11 @@ namespace mbts.spark
{
class InlineTickOrderProperties
{
public InlineTickOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
public InlineTickOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
@@ -29,8 +29,8 @@ namespace mbts.spark
max_chase_ticks = new InlineUserProperty<uint>(10, false);
detach_on_toward = new InlineUserProperty<bool>(true, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
@@ -43,8 +43,8 @@ namespace mbts.spark
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineTickOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
@@ -55,12 +55,12 @@ namespace mbts.spark
public uint max_chase_ticks { get; set; } = 10;
public bool detach_on_toward { get; set; } = true;
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTickOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
new InlineTickOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineTickOrderProperties> inline_properties) : base(model)
IObservable<InlineTickOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
@@ -74,7 +74,7 @@ namespace mbts.spark
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (p.tick_offset_on_deploy.force_override)
{
@@ -131,7 +131,7 @@ namespace mbts.spark
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _child_order_choices;
}
}
@@ -177,7 +177,7 @@ namespace mbts.spark
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -14,15 +14,15 @@ using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark {
static partial class OrderTypeVMFactory {
class InlineTimeBoxOrderProperties {
public InlineTimeBoxOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
public InlineTimeBoxOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types) {
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
start = new InlineUserProperty<DateTime>(DateTime.Now, true);
stop = new InlineUserProperty<DateTime>(DateTime.Now.AddMinutes(5), true);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<DateTime> start, stop;
}
@@ -30,8 +30,8 @@ namespace mbts.spark {
const double _start_immediately_delay_ms = 500;
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineTimeBoxOrderProperties _inline_props;
TimeTextTuple _absolute_start = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(5)), _absolute_stop = TimeTextTuple.FromDateTime(DateTime.Now.AddMinutes(10));
TimeSpan? _relative_start = TimeSpan.FromMinutes(5), _relative_stop = TimeSpan.FromMinutes(5);
@@ -40,14 +40,14 @@ namespace mbts.spark {
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTimeBoxOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
new InlineTimeBoxOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
public TimeBoxOrderVM(OrdersModel model,
IObservable<SecurityMD> security_md_source,
IObservable<InlineTimeBoxOrderProperties> inline_properties)
: base(model) {
IObservable<InlineTimeBoxOrderProperties> inline_properties, string id, string display_name)
: base(model, id, display_name) {
_security_md_source = security_md_source;
_dispose = Disposable.Create(
security_md_source.OnDispatcher()
@@ -58,7 +58,7 @@ namespace mbts.spark {
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) {
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (p.start.force_override) {
absolute_start = TimeTextTuple.FromDateTime(p.start.value);
@@ -94,7 +94,7 @@ namespace mbts.spark {
public ICollection<IOrderTypeVM> child_order_types {
get {
if (_child_order_choices == null) {
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
}
return _child_order_choices;
}
@@ -173,7 +173,7 @@ namespace mbts.spark {
void _select_child_order() {
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -17,17 +17,17 @@ namespace mbts.spark
{
class InlineTopOrderProperties
{
public InlineTopOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
public InlineTopOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
{
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
var hedgetype = CustomOneofCase.Lean;
if(!child_order_types.Contains(hedgetype))
if(!child_order_types.Any(ct => ct.key.type == hedgetype))
hedgetype = CustomOneofCase.Limit;
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
watch_level_tick_offset = new InlineUserProperty<uint>(0, false);
top_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
@@ -35,10 +35,10 @@ namespace mbts.spark
enable_hedge = new InlineUserProperty<bool>(false, false);
hedge_ticks = new InlineUserProperty<int>(1, false);
}
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_hedge_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> hedge_order_types;
public readonly InlineUserProperty<uint> watch_level_tick_offset;
@@ -53,13 +53,13 @@ namespace mbts.spark
{
readonly IObservable<SecurityMD> _security_md_source;
SecurityImmutable? _security;
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineTopOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
IOrderTypeVM _hedge_order;
IDisposable _hedge_order_errors_sub;
@@ -77,13 +77,13 @@ namespace mbts.spark
}
public int hedge_ticks { get; set; } = 1;
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTopOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
new InlineTopOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name)
{ }
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
IObservable<InlineTopOrderProperties> inline_properties) : base(model)
IObservable<InlineTopOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
{
_security_md_source = security_md_source;
_dispose = Disposable.Create(
@@ -98,11 +98,11 @@ namespace mbts.spark
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
{
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
{
_select_hedge_order(p.selected_hedge_order_type.value);
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
}
if (p.watch_level_tick_offset.force_override)
{
@@ -157,7 +157,7 @@ namespace mbts.spark
get
{
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _child_order_choices;
}
}
@@ -167,7 +167,7 @@ namespace mbts.spark
get
{
if (_hedge_order_choices == null)
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _hedge_order_choices;
}
}
@@ -241,7 +241,7 @@ namespace mbts.spark
void _select_child_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
@@ -257,7 +257,7 @@ namespace mbts.spark
void _select_hedge_order()
{
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
_inline_props.selected_hedge_order_type.value :
_inline_props.selected_hedge_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_hedge_order(ot);
}
@@ -14,8 +14,8 @@ using static bts.api.orderman.CustomOrderDefinition;
namespace mbts.spark {
static partial class OrderTypeVMFactory {
class InlineTriggerOrderProperties {
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
public readonly InlineUserProperty<TriggerPriceType> trigger_price_type;
public readonly InlineUserProperty<TriggerPayupPrice.Types.TriggerPayupPriceType> payup_price_type;
public readonly InlineUserProperty<double> fixed_price;
@@ -23,10 +23,10 @@ namespace mbts.spark {
public readonly InlineUserProperty<bool> use_fixed_price, is_priceout_checked, is_timeout_checked;
public readonly InlineUserProperty<int> timeout_secs;
public InlineTriggerOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
public InlineTriggerOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types) {
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
trigger_price_type = new InlineUserProperty<TriggerPriceType>(TriggerPriceType.Bid, false);
payup_price_type = new InlineUserProperty<TriggerPayupPrice.Types.TriggerPayupPriceType>();
use_fixed_price = new InlineUserProperty<bool>(false);
@@ -47,19 +47,20 @@ namespace mbts.spark {
readonly UserProperty<bool> _use_fixed_price, _is_timeout_checked, _is_priceout_checked;
SecurityMD? _security_md;
SecurityImmutable? _security => _security_md.security();
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
InlineTriggerOrderProperties _inline_props;
IOrderTypeVM _child_order;
IDisposable _dispose, _child_order_errors_sub;
ulong _bbo_ref = 0, _last_trade_ref = 0;
double _best_bid = double.NaN, _best_ask = double.NaN, _last_trade_price = double.NaN;
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
: this(model, security_md_source, security_md_source.Select(smd =>
new InlineTriggerOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
new InlineTriggerOrderProperties(new OrderTemplateHeader(model.factory.default_order_type(smd.security)), model.factory.default_order_types(smd.security).Select(t => new OrderTemplateHeader(t)).ToList())), id, display_name) { }
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineTriggerOrderProperties> inline_properties) : base(model) {
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineTriggerOrderProperties> inline_properties, string id, string display_name)
: base(model, id, display_name) {
_trigger_price_type = create_user_prop(nameof(trigger_price_type), TriggerPriceType.Bid);
_use_fixed_price = create_user_prop(nameof(use_fixed_price), true);
_fixed_price = create_user_prop(nameof(fixed_price), double.NaN);
@@ -88,7 +89,7 @@ namespace mbts.spark {
_order_types.SetItems(p.child_order_types.value);
}
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override)) {
_select_child_order(p.selected_child_order_type.value);
_select_child_order(p.selected_child_order_type.value.key.type);
}
// trigger price
_trigger_price_type.on_inline_prop(p.trigger_price_type);
@@ -135,7 +136,7 @@ namespace mbts.spark {
public ICollection<IOrderTypeVM> child_order_types {
get {
if (_child_order_choices == null)
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
return _child_order_choices;
}
}
@@ -210,13 +211,13 @@ namespace mbts.spark {
_security_md.Value.md.get_bbo(ref _bbo_ref, out co2.NativePriceSize bid, out co2.NativePriceSize ask);
_best_bid = bid.price;
_best_ask = ask.price;
_security_md.Value.md.get_last_trade(ref _last_trade_ref, out co2.NativePriceSize ps, out DateTime ts, out ulong v);
_security_md.Value.md.get_last_trade(ref _last_trade_ref, out co2.NativePriceSize ps, out ulong v);
}
}
void _select_child_order() {
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
_inline_props.selected_child_order_type.value :
_inline_props.selected_child_order_type.value.key.type :
model.factory.default_order_type(_security.Value, true);
_select_child_order(ot);
}
+4 -4
View File
@@ -32,7 +32,7 @@ namespace mbts.spark {
IDisposable _dispose;
[ImportingConstructor]
private OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, SpreaderModel spreader, ErrorReporter error, AdminModel admin_model) {
private OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, SpreaderModel spreader, ErrorReporter error) {
_bus = bus;
_factory = factory;
_service = service;
@@ -41,15 +41,15 @@ namespace mbts.spark {
_error = error;
var scheduler = ThreadPoolScheduler.Instance;
var interval = TimeSpan.FromMilliseconds(100);
on_orders = _service.subscribei_orders().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_orders = _service.subscribei_orders().Naggle(interval, scheduler).Table().Cache();
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).Table().Cache();
working_orders =
on_orders.Scan(
ImmutableDictionary<uint,OrderRecordImmutable>.Empty,
(workers, table) => _update_working_orders(workers, table))
.Replay(1).RefCount();
on_order_tuples = OrderRecordTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders);
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model).Table().Cache();
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).Table().Cache();
_activity = new OrderActivitySummarizer(bus, on_orders);
on_fill_tuples = FillTupleSource.create_observable(bus.subscribe(SparkTags.SecuritiesTag), on_orders, on_fills);
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
@@ -1,5 +1,6 @@
using bts.collections;
using bts.test;
using mbts.collections.extensions;
using NUnit.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -8,7 +9,7 @@ using System.Text;
using System.Threading.Tasks;
namespace bts.tests {
[TestGroup]
[TestFixture]
class SortedTreeTests {
class Order {
@@ -91,30 +92,5 @@ namespace bts.tests {
}
}
[Test]
public void test_enumerate_group() {
var comparer = Comparer<Order>.Create((l, r) => l.name.CompareTo(r.name));
var input = orders.ToList();
input.Shuffle();
var tree = new SortedTree<Order, int>(comparer, id, pid, input);
Assert.SequenceEqual(tree, orders);
var children_of_a = tree.EnumerateGroup(orders.First(o => o.name == 'A'));
var also_children_of_a = tree.EnumerateGroup(orders.First(o => o.name == 'C'));
Assert.AreEqual(3, children_of_a.Count());
Assert.AreEqual(3, also_children_of_a.Count());
Assert.SequenceEqual(children_of_a, also_children_of_a);
var children_of_d = tree.EnumerateGroup(orders.First(o => o.name == 'F'));
var also_children_of_d = tree.EnumerateGroup(orders.First(o => o.name == 'E'));
Assert.AreEqual(children_of_d.First().name, 'D');
Assert.AreEqual(also_children_of_d.First().name, 'D');
Assert.SequenceEqual(children_of_d, also_children_of_d);
Assert.SequenceEqual(children_of_d.Select(o => o.name), new [] { 'D', 'E', 'F' });
var children_of_g = tree.EnumerateGroup(orders.First(o => o.name == 'G'));
Assert.SequenceEqual(children_of_g.Select(o => o.name), new [] { 'G' });
}
}
}
+4 -4
View File
@@ -70,7 +70,7 @@ namespace bts.spark
public static SparkCommand ShowWorkspaces =>
SparkCommand.create("Workspaces", Icons.FavoriteWindow, SparkTags.ShowWorkspaces);
public static SparkCommand ShowSettings =>
SparkCommand.create("Settings", Icons.Admin, SparkTags.ShowSettings);
SparkCommand.create("Settings", Icons.AdminFrozen, SparkTags.ShowSettings);
// Support
public static SparkCommand ShowManual =>
@@ -113,12 +113,12 @@ namespace bts.spark
// TODO: remove this method, eventually want all windows to use window_settings_command()
public static SparkCommand change_securities_command(Func<(PartialCommand<SecurityCriteria>, SecurityCriteria)> get_param) =>
SparkCommand.create("Settings", Icons.Admin, SparkTags.ShowAndEditPickMultiple, get_param);
SparkCommand.create("Settings", Icons.AdminFrozen, SparkTags.ShowAndEditPickMultiple, get_param);
public static PartialCommand<SecurityCriteria> change_securities_callback(string window_name) =>
SparkCommand.create(window_name, Icons.Admin, new SparkTags.SparkTag<SecurityCriteria>($"Change {window_name} securities callback"));
SparkCommand.create(window_name, Icons.AdminFrozen, new SparkTags.SparkTag<SecurityCriteria>($"Change {window_name} securities callback"));
public static SparkCommand window_settings_command(IDialogCommand launch_settings_editor) =>
new SparkCommandWrapper("Settings", Icons.Admin, launch_settings_editor);
new SparkCommandWrapper("Settings", Icons.AdminFrozen, launch_settings_editor);
}
}
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using NUnit.Framework;
using mbts.collections.extensions;
using System.Collections;
using bts.collections;
namespace mbts.ui.utility
{
@@ -666,6 +666,44 @@ namespace mbts.collections.extensions {
}
}
public static class ListExtensions
{
#region RemoveWhere
public static void RemoveWhere<T>(this IList<T> list, Func<T, bool> where) { foreach (var r in list.Where(where).ToList()) list.Remove(r); }
public static void RemoveWhere<T>(this IList<T> list, Func<T, bool> where, Action<T> on_remove) {
int count = list.Count;
for (int i = 0; i < count; i++) {
var t = list[i];
if (where(t)) {
list.RemoveAt(i--);
--count;
on_remove(t);
}
}
}
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list) {
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
#endregion
public static void RemoveAt<T>(this IList<T> list, int index, int count) {
for (int i = 0; i < count; i++)
list.RemoveAt(index);
}
}
public static class DictionaryExtensions
{
#region GetValueOrDefault
@@ -29,7 +29,6 @@ namespace mbts.ui.settings
}
public Task<IShellWindow> restore(WindowState window) {
Log.Ui.info("Loading window '{0}'".fmt(window.title));
bool changed;
int count = 0;
do {
@@ -0,0 +1,99 @@
using bts;
using bts.utils;
using mbts.collections;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace mbts.spark {
interface IPrepareUpdateForDispatcher {
IDisposable prepare_update();
}
class DispatcherUpdateMaster : IDisposable {
readonly string _name;
readonly DispatcherPriority _invocation_priority;
SetMap<Dispatcher, IPrepareUpdateForDispatcher> _vms;
int _state = States.ReadyForUpdate;
IDisposable _dispose;
public DispatcherUpdateMaster(string name)
: this(name, WorkLoop.default_interval, WorkLoop.default_priority) { }
public DispatcherUpdateMaster(string name, TimeSpan period, DispatcherPriority invocation_priority) {
_name = name;
_vms = new SetMap<Dispatcher, IPrepareUpdateForDispatcher>(EqualityComparer.Create<Dispatcher, int>(d => d.Thread.ManagedThreadId));
_dispose = ThreadPoolScheduler.Instance.SchedulePeriodic(period, _update_async);
_invocation_priority = invocation_priority;
}
public IDisposable attach(IPrepareUpdateForDispatcher vm, Dispatcher dispatcher) {
var clone = _vms.clone();
clone.add(dispatcher, vm);
_vms = clone;
return Disposable.Create(() => _detach(vm, dispatcher));
}
public void Dispose() {
Thread.VolatileWrite(ref _state, States.Disposed);
Disposable.dispose(ref _dispose);
}
void _detach(IPrepareUpdateForDispatcher preper, Dispatcher dispatcher) {
var clone = _vms.clone();
clone.remove(dispatcher, preper);
_vms = clone;
}
async void _update_async() {
if (Interlocked.CompareExchange(ref _state, States.UpdateInProgress, States.ReadyForUpdate) != States.ReadyForUpdate) {
return;
}
var temp = _vms;
try {
ConcurrentBag<(Dispatcher dispatcher, ConcurrentBag<IDisposable> update)> update_jobs =
new ConcurrentBag<(Dispatcher dispatcher, ConcurrentBag<IDisposable> update_jobs)>();
await Task.Run(() => {
Parallel.ForEach(temp, dispatcher_group => {
ConcurrentBag<IDisposable> update = new ConcurrentBag<IDisposable>();
Parallel.ForEach(dispatcher_group, vm => {
// need to check for null, that indicates an empty update (rather than an actual empty enumerable)
var ujs = vm.prepare_update();
if (ujs != null)
update.Add(ujs);
});
if (update.Count > 0)
update_jobs.Add((dispatcher_group.Key, update));
});
});
await Task.WhenAll(update_jobs.Select(_update_dispatcher_group_async));
} catch (Exception ex) {
Log.Ui.error($"{_name} error during parallel update: {ex}");
} finally {
// signal that the update is complete, and we are ready for another
Interlocked.CompareExchange(ref _state, States.ReadyForUpdate, States.UpdateInProgress);
}
}
async Task _update_dispatcher_group_async((Dispatcher dispatcher, ConcurrentBag<IDisposable> update_jobs) t) =>
await t.dispatcher.InvokeAsync(() => t.update_jobs.DisposeAll(), _invocation_priority);
static class States {
public const int
ReadyForUpdate = 0,
UpdateInProgress = 1,
Disposed = 2;
}
}
static class DispatcherUpdateMasterExtensions {
public static IDisposable attach(this DispatcherUpdateMaster dum, IPrepareUpdateForDispatcher preparer) =>
dum.attach(preparer, Dispatcher.CurrentDispatcher);
}
}
@@ -1,156 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using bts;
using bts.api;
namespace mbts.ui.utility
{
class ExpirationFormatter
{
private static class DateFormatter
{
private static string[] month_codes = new string[] { "F", "G", "H", "J", "K", "M", "N", "Q", "U", "V", "X", "Z" };
private static string[] month_three_letters = new string[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
public static string format_condensed(DateTime date, bool use_month_code, bool show_year)
{
return String.Format("{0}{1}{2}", date.Day, month_codes[date.Month - 1], show_year ? (date.Year - 2000).ToString() : String.Empty);
}
public static string format(DateTime date, bool use_month_code, bool show_day) {
string month_text = use_month_code ? "{0}{1:00}".fmt(month_codes[date.Month - 1], date.Year - 2000) : "{0} {1:00}".fmt(month_three_letters[date.Month - 1], date.Year - 2000);
return show_day ? "{0} {1}".fmt(date.Day, month_text) : month_text;
}
public static string format_standard(DateTime date) { return "{0} {1} {2:00}".fmt(date.Day, month_three_letters[date.Month - 1], date.Year - 2000); }
public static string format_coded(DateTime date) { return "{0} {1}{2:00}".fmt(date.Day, month_codes[date.Month - 1], date.Year - 2000); }
public static string format_coded_hide_day(DateTime date) { return "{0}{1}".fmt(month_codes[date.Month - 1], date.Year - 2000); }
public static DateTime parse_standard(string date_text) {
string[] split = date_text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
int year = 0;
int month = Array.IndexOf(month_three_letters, split[1]) + 1;
int day = 0;
int.TryParse(split[2], out year);
int.TryParse(split[0], out day);
return new DateTime(year + 2000, month, day);
}
public static DateTime parse_coded(string date_text) {
string[] split = date_text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
string year_text = split[1].Substring(1);
string month_text = split[1].Substring(0, 1);
string day_text = split[0];
int month = Array.IndexOf(month_codes, month_text) + 1;
int day, year;
int.TryParse(year_text, out year);
int.TryParse(day_text, out day);
return new DateTime(year + 2000, month, day);
}
public static DateTime parse_coded_hide_day(string date_text) {
string year_text = date_text.Substring(1);
string month_text = date_text.Substring(0, 1);
int month = Array.IndexOf(month_codes, month_text) + 1;
int year;
int.TryParse(year_text, out year);
return new DateTime(year + 2000, month, 0);
}
}
private static IDictionary<SecurityType, IDictionary<string, bts.config.ExpirationDisplayOffsetEntry>> _offset_map;
static ExpirationFormatter()
{
try
{
_offset_map = new Dictionary<SecurityType, IDictionary<string, bts.config.ExpirationDisplayOffsetEntry>>();
_offset_map[SecurityType.Option] = new Dictionary<string, bts.config.ExpirationDisplayOffsetEntry>();
_offset_map[SecurityType.Future] = new Dictionary<string, bts.config.ExpirationDisplayOffsetEntry>();
bts.config.ExpirationDisplayOffset offset_list = mbts.config.Configuration.get<bts.config.ExpirationDisplayOffset>();
foreach (bts.config.ExpirationDisplayOffsetEntry e in offset_list.entries)
{
string security_name = e.security_name;
string security_type_text = e.security_type;
SecurityType security_type = SecurityType.Unset;
Enum.TryParse<SecurityType>(security_type_text, true, out security_type);
if (security_type == SecurityType.Option || security_type == SecurityType.Future)
_offset_map[security_type][security_name] = e;
}
}
catch (Exception ex)
{
Log.Ui.error(String.Format("Exception message = {0}", ex.Message));
}
}
private static bts.config.ExpirationDisplayOffsetEntry _get_offset_entry(SecurityType security_type, string security_name)
{
bts.config.ExpirationDisplayOffsetEntry rv = null;
IDictionary<string, bts.config.ExpirationDisplayOffsetEntry> this_security_type_map = null;
if (_offset_map.TryGetValue(security_type, out this_security_type_map))
if (this_security_type_map.TryGetValue(security_name, out rv))
return rv;
return rv;
}
public static DateTime convert_real_to_mapped(SecurityType security_type, string security_name, DateTime security_expiration)
{
bts.config.ExpirationDisplayOffsetEntry offset_entry = _get_offset_entry(security_type, security_name);
DateTime rv = security_expiration;
if (offset_entry != null)
{
if (offset_entry.addition_type == bts.config.ExpirationOffsetType.type.Month)
rv = security_expiration.AddMonths(offset_entry.addition_count);
else if (offset_entry.addition_type == bts.config.ExpirationOffsetType.type.MonthMoveToStart)
{
rv = security_expiration.AddMonths(offset_entry.addition_count);
rv = new DateTime(rv.Year, rv.Month, 1);
}
}
return rv;
}
public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration) {
return format_expiration_standard(security_type, security_name, security_expiration, map_expiration, false);
}
public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool use_month_code) {
return format_expiration_standard(security_type, security_name, security_expiration, map_expiration, use_month_code, true);
}
public static string format_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool use_month_code, bool show_day) {
DateTime mapped_expiration = map_expiration ? convert_real_to_mapped(security_type, security_name, security_expiration) : security_expiration;
return DateFormatter.format(mapped_expiration, use_month_code, show_day);
}
public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration) {
return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, false, false, true);
}
public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type) {
return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, show_security_type, false, true);
}
public static string format_symbol_expiration_standard(SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type, bool use_month_code) {
return format_symbol_expiration_standard(security_type, security_name, security_expiration, map_expiration, show_security_type, use_month_code, true);
}
public static string format_symbol_expiration_standard (SecurityType security_type, string security_name, DateTime security_expiration, bool map_expiration, bool show_security_type, bool use_month_code, bool show_day) {
string exp_text = format_expiration_standard(security_type, security_name, security_expiration, map_expiration, use_month_code, show_day);
return !show_security_type ? String.Format("{0} {1}", security_name, exp_text) : String.Format("{0} {1} {2}", security_type.ToString().ToUpper(), security_name, exp_text);
}
public static string format_option_expiration_condensed(string option_name, DateTime option_expiration, bool map_expiration, bool show_symbol_name, bool show_year)
{
DateTime mapped_expiration = map_expiration ? convert_real_to_mapped(SecurityType.Option, option_name, option_expiration) : option_expiration;
return String.Format("{0}{1}", show_symbol_name ? option_name : String.Empty, DateFormatter.format_condensed(mapped_expiration, true, show_year));
}
public static DateTime parse_standard_formatted_expiration(SecurityType security_type, string security_name, string security_expiration) { return DateFormatter.parse_standard(security_expiration); }
}
}
@@ -66,73 +66,15 @@ namespace mbts.utils {
class AnonymousConnectable<TResult> : IConnectableObservable<TResult> {
readonly IObservable<TSource> _source;
readonly Func<TSource, IObservable<TResult>> _select_next;
List<LazySubscription> _lazy_subs = new List<LazySubscription>();
KeyDrivenObservableSeries<TSource, TResult> _driver;
IDisposable _driver_sub;
bool _is_connected = false;
readonly KeyDrivenObservableSeries<TSource, TResult> _driver;
public AnonymousConnectable(IObservable<TSource> source, Func<TSource, IObservable<TResult>> select_next) {
_source = source;
_select_next = select_next;
_driver = new KeyDrivenObservableSeries<TSource, TResult>(select_next);
}
// idempotent
public IDisposable Connect() {
lock (this) {
if (!_is_connected) {
_driver = new KeyDrivenObservableSeries<TSource, TResult>(_select_next);
foreach (var ls in _lazy_subs)
ls.attach();
_lazy_subs.Clear();
_driver_sub = _source.Subscribe(_driver);
_is_connected = true;
}
}
return Disposable.Create(_deconnect);
}
void _deconnect() {
lock (this) {
if (_is_connected) {
_driver_sub.Dispose();
_driver.Dispose();
_is_connected = false;
}
}
}
public IDisposable Subscribe(IObserver<TResult> observer) {
lock (this) {
if (_is_connected)
return _driver.Subscribe(observer);
var ls = new LazySubscription(this, observer);
_lazy_subs.Add(ls);
return ls;
}
throw new InvalidOperationException();
}
class LazySubscription : IDisposable {
readonly IObserver<TResult> _observer;
readonly AnonymousConnectable<TResult> _owner;
IDisposable _sub;
public LazySubscription(AnonymousConnectable<TResult> owner, IObserver<TResult> observer) {
_owner = owner;
_observer = observer;
}
public void attach() =>
_sub = _owner._driver.Subscribe(_observer);
public void Dispose() {
lock (_owner) {
Disposable.dispose(ref _sub);
_owner._lazy_subs.Remove(this);
}
}
}
public IDisposable Connect() => _source.Subscribe(_driver);
public IDisposable Subscribe(IObserver<TResult> observer) => _driver.Subscribe(observer);
}
}
+88 -57
View File
@@ -156,15 +156,13 @@ namespace mbts.ui.shell
public string name => _propname;
public void set(T value) {
if (!Equals(value, _value)) {
Changing?.Invoke(_value, value);
var old = _value;
_value = value;
Changed?.Invoke(old, value);
_owner.RaisePropertyChanged(_propname);
}
if (!Equals(value, _value))
_apply_new_value(value);
}
public IDisposable set_as_job(T value) =>
Equals(value, _value) ? null : Disposable.Create(() => _apply_new_value(value));
public IDisposable Subscribe(IObserver<(T oldval, T newval)> observer) {
Changed += pch;
return Disposable.Create(() => Changed -= pch);
@@ -182,6 +180,14 @@ namespace mbts.ui.shell
get { return _value; }
set { set(value); }
}
void _apply_new_value(T new_value) {
Changing?.Invoke(_value, new_value);
var old = _value;
_value = new_value;
Changed?.Invoke(old, new_value);
_owner.RaisePropertyChanged(_propname);
}
}
protected class DelegateProperty<T> : Property<T> {
@@ -446,6 +452,9 @@ namespace mbts.ui.shell
public IEnumerable<string> AllErrors { get { return _errors.Values; } }
#endregion
protected Property<T> backing<T>(string name, T default_value, bool validate_now = false, params Func<T, bool>[] validation_steps) =>
backing(name, default_value, validation_steps.Select(v => new PropertyValidation<T>(v)).ToLArray(), validate_now);
protected Property<T> backing<T>(string name, T default_value, IEnumerable<PropertyValidation<T>> validation_steps, bool validate_now = false) {
void run_validation(T val) {
foreach (var pv in validation_steps) {
@@ -526,7 +535,7 @@ namespace mbts.ui.shell
/// <para/>If the user has not selected an item, but the current items do not contain the desired item, it will re-evaluate whether or not to select the item when new items arrive.
/// </summary>
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, IReadOnlyList<PropertyValidation<T>> validation_steps) =>
backing(name, items, desired_item, select_default, null, validation_steps);
backing(name, items, desired_item, select_default, EqualityComparer<T>.Default, validation_steps);
/// <summary>
/// Returns a SelectorProperty&lt;T&gt; that observes an items source, and a desired item. When either observable ticks, it determines whether or not to assign the selected_item property.
@@ -538,8 +547,8 @@ namespace mbts.ui.shell
/// <param name="desired_item">The observable sequence of a desired item.</param>
/// <param name="select_default">The fallback function to select an item by default.</param>
/// <param name="items_match">The item-matching function used to preserve a selected item when new items arrive.</param>
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, Func<T, T, bool> items_match) =>
backing(name, items, desired_item, select_default, items_match, new List<PropertyValidation<T>>(0));
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, IEqualityComparer<T> comparer) =>
backing(name, items, desired_item, select_default, comparer, new List<PropertyValidation<T>>(0));
/// <summary>
/// Returns a SelectorProperty&lt;T&gt; that observes an items source, and a desired item. When either observable ticks, it determines whether or not to assign the selected_item property.
@@ -552,14 +561,24 @@ namespace mbts.ui.shell
/// <param name="select_default">The fallback function to select an item by default.</param>
/// <param name="items_match">The item-matching function used to preserve a selected item when new items arrive.</param>
/// <param name="validation_steps">The validation steps to take when selected_item changes.</param>
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, Func<T, T, bool> items_match, IReadOnlyList<PropertyValidation<T>> validation_steps) {
if (_observable_properties == null) _observable_properties = new List<IDisposable>();
var prop = new SelectorProperty<T>(this, name, select_default, items_match, validation_steps);
_observable_properties.Add(items.OnDispatcher().Subscribe(prop));
_observable_properties.Add(desired_item.OnDispatcher().Subscribe(prop));
protected SelectorProperty<T> backing<T>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T> select_default, IEqualityComparer<T> comparer, IReadOnlyList<PropertyValidation<T>> validation_steps) {
var prop = new SelectorProperty<T>(this, name, select_default, comparer, validation_steps);
attach(prop, items, desired_item);
return prop;
}
protected SelectorProperty<T, V> backing<T, V>(string name, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item, Func<T, V> item_to_value, Func<V> select_default, IEqualityComparer<V> comparer, IReadOnlyList<PropertyValidation<V>> validation_steps) {
var prop = new SelectorProperty<T, V>(this, name, item_to_value, select_default, comparer, validation_steps);
attach(prop, items, desired_item);
return prop;
}
protected void attach<T, V>(SelectorProperty<T, V> selector_property, IObservable<IEnumerable<T>> items, IObservable<InlineUserProperty<T>> desired_item) {
if (_observable_properties == null) _observable_properties = new List<IDisposable>();
_observable_properties.Add(items.OnDispatcher().Subscribe(selector_property));
_observable_properties.Add(desired_item.OnDispatcher().Subscribe(selector_property));
}
protected void set_dispose(IDisposable disposable) {
if (_dispose != null) throw new InvalidOperationException("Cannot set disposable twice");
_dispose = disposable;
@@ -576,37 +595,44 @@ namespace mbts.ui.shell
OnDisposed?.Invoke(true);
}
protected class SelectorProperty<T> : SelectorProperty<T, T> {
public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func<T> select_default, IEqualityComparer<T> comparer, IReadOnlyList<PropertyValidation<T>> validation_steps)
: base(owner, prop_name, i => i, select_default, comparer, validation_steps) { }
}
/// <summary>
/// Designed to observe an items source, and a [settings-driven] desired item. When a new desired item is observed, it will evaluate it's current state and determine if it should be assigned to the selected_item.
/// <para/>Notes: If the user has selected an item (ie the selected_item has been assigned through the public setter), the desired item will be ignored unless force_override is true.
/// <para/>If the user has not selected an item, but the current items do not contain the desired item, it will re-evaluate whether or not to select the item when new items arrive.
/// </summary>
protected class SelectorProperty<T> : IObserver<InlineUserProperty<T>>, IObserver<IEnumerable<T>>, IObservable<(T oldval, T newval)> {
protected class SelectorProperty<TItem, TValue> : IObserver<InlineUserProperty<TItem>>, IObserver<IEnumerable<TItem>>, IObservable<(TValue oldval, TValue newval)> {
readonly DisposableViewModelBase _owner;
readonly string _prop_name;
readonly IReadOnlyList<PropertyValidation<T>> _validation_steps;
readonly ObservableCollection<T> _items = new ObservableCollection<T>();
readonly Func<T> _select_default;
readonly Func<T, T, bool> _items_match; // used to maintain selected_item through an items-change
T _selected_item;
InlineUserProperty<T> _desired_item;
readonly IReadOnlyList<PropertyValidation<TValue>> _validation_steps;
readonly ObservableCollection<TItem> _items = new ObservableCollection<TItem>();
readonly Func<TValue> _select_default;
readonly IEqualityComparer<TValue> _value_comparer;
readonly Func<TItem, TValue> _item_to_value;
TValue _selected_item;
InlineUserProperty<TItem> _desired_item = new InlineUserProperty<TItem>();
bool _is_user_selected_item;
public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func<T> select_default, Func<T, T, bool> items_match, IReadOnlyList<PropertyValidation<T>> validation_steps) {
public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func<TItem, TValue> item_to_value, Func<TValue> select_default, IEqualityComparer<TValue> value_comparer, IReadOnlyList<PropertyValidation<TValue>> validation_steps) {
_owner = owner;
_prop_name = prop_name;
_item_to_value = item_to_value;
_select_default = select_default;
_items_match = items_match;
_value_comparer = value_comparer ?? EqualityComparer<TValue>.Default;
_validation_steps = validation_steps;
items = new ReadOnlyObservableCollection<T>(_items);
items = new ReadOnlyObservableCollection<TItem>(_items);
}
public event PropertyChange<T> Changing;
public event PropertyChange<T> Changed;
public event PropertyChange<TValue> Changing;
public event PropertyChange<TValue> Changed;
public ReadOnlyObservableCollection<T> items { get; }
public ReadOnlyObservableCollection<TItem> items { get; }
public T selected_item {
public TValue selected_item {
get => _selected_item;
set {
_is_user_selected_item = true;
@@ -614,31 +640,31 @@ namespace mbts.ui.shell
}
}
public IDisposable Subscribe(IObserver<(T oldval, T newval)> observer) {
public IDisposable Subscribe(IObserver<(TValue oldval, TValue newval)> observer) {
Changed += pch;
return Disposable.Create(() => Changed -= pch);
void pch(T o, T n) => observer.OnNext((o, n));
void pch(TValue o, TValue n) => observer.OnNext((o, n));
}
public IObservable<T> observe_current(bool start_now = false) {
public IObservable<TValue> observe_current(bool start_now = false) {
var source = this.Select(vals => vals.newval);
if (start_now)
source = source.StartWith(_selected_item);
source = source.StartWith(selected_item);
return source;
}
void IObserver<InlineUserProperty<T>>.OnNext(InlineUserProperty<T> iup) {
void IObserver<InlineUserProperty<TItem>>.OnNext(InlineUserProperty<TItem> iup) {
_desired_item = iup;
_try_auto_select();
}
void IObserver<InlineUserProperty<T>>.OnError(Exception error) { }
void IObserver<InlineUserProperty<T>>.OnCompleted() { }
void IObserver<InlineUserProperty<TItem>>.OnError(Exception error) { }
void IObserver<InlineUserProperty<TItem>>.OnCompleted() { }
void IObserver<IEnumerable<T>>.OnNext(IEnumerable<T> value) {
if (_items_match != null && _is_user_selected_item) {
void IObserver<IEnumerable<TItem>>.OnNext(IEnumerable<TItem> value) {
if (_is_user_selected_item) {
var curr_selected_item = selected_item;
_items.sync_with(value);
if (_try_match_item(curr_selected_item, out T match))
if (_try_match_item(curr_selected_item, out TValue match))
selected_item = match;
else {
_is_user_selected_item = false;
@@ -646,18 +672,13 @@ namespace mbts.ui.shell
}
} else {
_items.sync_with(value);
if (_is_user_selected_item) {
_is_user_selected_item = _items.Contains(_selected_item);
}
if (!_is_user_selected_item) {
_try_auto_select();
}
_try_auto_select();
}
}
void IObserver<IEnumerable<T>>.OnError(Exception error) { }
void IObserver<IEnumerable<T>>.OnCompleted() { }
void IObserver<IEnumerable<TItem>>.OnError(Exception error) { }
void IObserver<IEnumerable<TItem>>.OnCompleted() { }
void _run_validation(T val) {
void _run_validation(TValue val) {
var invalid_steps = _validation_steps.Select(v => v.validate(val)).Where(r => !r.valid);
if (invalid_steps.Count() > 0) {
var error_msg = string.Join("\n", invalid_steps.Where(r => !string.IsNullOrEmpty(r.msg)).Select(r => r.msg));
@@ -667,15 +688,15 @@ namespace mbts.ui.shell
void _try_auto_select() {
if (_is_user_selected_item) {
if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out T match)) {
if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out TValue match)) {
_set_selected_item(match);
return;
}
} else if (desired_item_valid() && _try_match_item(_desired_item.value, out T match)) {
} else if (desired_item_valid() && _try_match_item(_desired_item.value, out TValue match)) {
_set_selected_item(match);
return;
}
if (_try_match_item(_select_default(), out T default_match))
if (_try_match_item(_select_default(), out TValue default_match))
_set_selected_item(default_match);
else {
// the _set_selected_item has never been entered, so validation has never ran, force it here
@@ -685,8 +706,8 @@ namespace mbts.ui.shell
bool desired_item_valid() => (_desired_item?.has_value).GetValueOrDefault();
}
void _set_selected_item(T value) {
if (!Equals(value, _selected_item)) {
void _set_selected_item(TValue value) {
if (!_value_comparer.Equals(value, _selected_item)) {
Changing?.Invoke(_selected_item, value);
var old = _selected_item;
_selected_item = value;
@@ -696,10 +717,20 @@ namespace mbts.ui.shell
}
}
bool _try_match_item(T item, out T match) =>
_items_match != null ?
_items.TryGetFirst(i => _items_match(i, item), out match) :
_items.TryGetFirst(i => i.Equals(item), out match);
bool _try_match_item(TValue value, out TValue match) {
foreach (TItem item in _items) {
TValue v = _item_to_value(item);
if (_value_comparer.Equals(v, value)) {
match = v;
return true;
}
}
match = default;
return false;
}
bool _try_match_item(TItem item, out TValue match) =>
_try_match_item(_item_to_value(item), out match);
}
public event Action<bool> OnDisposed;
@@ -15,7 +15,7 @@ namespace mbts.spark {
value = initial_value;
}
event Action<(T oldval, T newval)> _value_set;
public event Action<(T oldval, T newval)> value_set;
public T value { get; private set; }
public string text => _text ?? (_text = _format(value));
@@ -27,14 +27,22 @@ namespace mbts.spark {
}
}
public IDisposable on_next_distinct_as_job(T value, ref bool dirty) {
if (!value.Equals(this.value)) {
dirty = true;
return Disposable.Create(() => OnNext(value));
}
return null;
}
public void OnCompleted() { }
public void OnError(Exception error) { }
public void OnNext(T value) {
if (_value_set != null) {
if (value_set != null) {
T prev = this.value;
this.value = value;
_text = null;
_value_set.Invoke((prev, value));
value_set.Invoke((prev, value));
} else {
this.value = value;
_text = null;
@@ -42,7 +50,7 @@ namespace mbts.spark {
}
public IDisposable Subscribe(IObserver<(T oldval, T newval)> observer) =>
Observable.FromEvent<Action<(T oldval, T newval)>, (T oldval, T newval)>(cb => _value_set += cb, cb => _value_set -= cb)
Observable.FromEvent<Action<(T oldval, T newval)>, (T oldval, T newval)>(cb => value_set += cb, cb => value_set -= cb)
.Subscribe(observer);
}
}
@@ -168,7 +168,7 @@ namespace mbts.ui.controls {
o.Resources[ActiveSelectionHighlightKey] = v;
o.Resources[InactiveSelectionHighlightKey] = new SolidColorBrush(v.Color) { Opacity = 0.2 }.frozen();
o.Resources[SelectionStrokeKey] = new Pen(o.SelectionHighlight.Color.to_brush().frozen(), 1).frozen();
foreach (var col in o.Columns.empty_if_null().OfType<ISelectionHighlightListener>())
foreach (var col in o.Columns.OfType<ISelectionHighlightListener>())
col.on_selection_fill(v);
}, (o, v) => v as SolidColorBrush ?? DefaultSelectionHighlight,
DefaultSelectionHighlight);
@@ -634,7 +634,7 @@ namespace mbts.ui.controls {
(o, v) => {
if (DoubleHelper.IsNaNOrInfinity(v))
v = 0;
if (!o._visible_columns.is_null_or_empty() && o.Columns != null) {
if (!o._visible_columns.is_null_or_empty()) {
double max_font_size = o.Columns.Max(c => c.font_size);
double min_row_height = o.RowPadding.Top + max_font_size + o.RowPadding.Bottom;
if (v < min_row_height)
@@ -31,7 +31,7 @@ namespace mbts.ui {
readonly ObservableCollection<TRow> _rows;
readonly ICollectionView _rows_view;
readonly ObservableCollection<ColumnBase> _columns;
//readonly ObservableAutoWrapper<PredicateAnimator<TRow, TRowData>, TRow> _animators;
readonly ObservableAutoWrapper<PredicateAnimator<TRow, TRowData>, TRow> _animators;
readonly Property<GridSettings> _grid_settings;
readonly DelegateProperty<FontFamily> _font_family;
readonly DelegateProperty<FontWeight> _font_weight;
@@ -59,18 +59,18 @@ namespace mbts.ui {
_font_size = backing(nameof(font_size), () => grid_settings.font_size, nameof(font_size));
_font_color = backing(nameof(font_color), () => grid_settings.font_color.rgb_solid_brush().frozen(), nameof(grid_settings));
_selection_highlight = backing(nameof(selection_highlight), () => grid_settings.selection_highlight.to_brush().frozen(), nameof(grid_settings));
//_animators = new ObservableAutoWrapper<PredicateAnimator<TRow, TRowData>, TRow>(_rows = rows, create_animator);
_animators = new ObservableAutoWrapper<PredicateAnimator<TRow, TRowData>, TRow>(_rows = rows, create_animator);
_resources = new List<IDisposable> {
ObservableAutoWrapper.connect<SortDescriptionCollection, SortDescription, string>(
rows_view.SortDescriptions, live_sorting_props = ((ICollectionViewLiveShaping)rows_view).LiveSortingProperties, sd => sd.PropertyName),
(launch_settings_editor = new DialogCommand(create_settings_view))
};
//
//PredicateAnimator<TRow, TRowData> create_animator(TRow r) {
// var pa = new PredicateAnimator<TRow, TRowData>(r);
// pa.set_predicate_animations(_animation_defs);
// return pa;
//}
PredicateAnimator<TRow, TRowData> create_animator(TRow r) {
var pa = new PredicateAnimator<TRow, TRowData>(r);
pa.set_predicate_animations(_animation_defs);
return pa;
}
Window create_settings_view() {
var vm = new GridSettingsVM<TRowData, TPredicateBuilder>(owner_title, _bus, _orders_model, create_predicate_vm, create_column_predicate_vm);
// need to refresh before launching the settings-view bc users can chanve some grid-column-settings properties directly from the grid (e.g. width, is_collapsed, etc.)
@@ -134,9 +134,9 @@ namespace mbts.ui {
_animation_defs.add(pa.predicate_builder.create_predicate(_arg), PredicateAnimations.flat_definitions<TRow, TRowData>(pa.row_animations, pa.cell_animations, lookup_column));
}
}
//foreach (var a in _animators) {
// a.set_predicate_animations(_animation_defs);
//}
foreach (var a in _animators) {
a.set_predicate_animations(_animation_defs);
}
// ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings
// don't add insult to injury by crashing **
if (grid_settings.columns != null && _columns != null) {
@@ -47,7 +47,7 @@
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<usp:OrderTypeItem OrderType="{Binding order_type}" Metadata="{Binding meta}"/>
<usp:OrderTypeItem OrderType="{Binding order_type}" DisplayName="{Binding display_name}" Metadata="{Binding meta}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
@@ -222,8 +222,6 @@
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="4" />
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@@ -235,11 +233,9 @@
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
<TextBlock Grid.Row="3" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
<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>
<TextBlock Grid.Row="5">Child</TextBlock>
<uag:OrderDetailsPopupControl
Grid.Row="7" Grid.Column="2"
Grid.Row="5" Grid.Column="2"
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
ItemsSource="{Binding child_order_types}"
SelectedItem="{Binding child_order}"
@@ -33,7 +33,7 @@
Width="15"
Height="10"
/>
<TextBlock
<TextBlock x:Name="text"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="20,0,0,0"
@@ -3,23 +3,49 @@ using bts.spark;
using System.Windows;
using System.Windows.Controls;
namespace mbts.ui.spark
{
namespace mbts.ui.spark {
/// <summary>
/// Interaction logic for OrderTypeItem.xaml
/// </summary>
public partial class OrderTypeItem : Control
{
static OrderTypeItem() {
partial class OrderTypeItem : Control {
static OrderTypeItem() =>
DefaultStyleKeyProperty.OverrideMetadata(typeof(OrderTypeItem), new FrameworkPropertyMetadata(typeof(OrderTypeItem)));
}
public static readonly DependencyProperty OrderTypeProperty = DependencyProperty.Register(nameof(OrderType), typeof(CustomOrderDefinition.CustomOneofCase), typeof(OrderTypeItem),
new PropertyMetadata(CustomOrderDefinition.CustomOneofCase.None));
public CustomOrderDefinition.CustomOneofCase OrderType { get => (CustomOrderDefinition.CustomOneofCase)GetValue(OrderTypeProperty); set => SetValue(OrderTypeProperty, value); }
public static readonly DependencyProperty DisplayNameProperty =
WPFHelper.create_dp<string, OrderTypeItem>(nameof(DisplayName), (o, v) => {
o._display_name = v;
o._update_text();
});
public string DisplayName {
get => GetValue(DisplayNameProperty) as string;
set => SetValue(DisplayNameProperty, value);
}
public static readonly DependencyProperty MetadataProperty = DependencyProperty.Register(nameof(Metadata), typeof(OrderMetadata), typeof(OrderTypeItem),
new PropertyMetadata(new OrderMetadata { Color = 0, KeyBinding = string.Empty }));
public OrderMetadata Metadata { get => GetValue(MetadataProperty) as OrderMetadata; set => SetValue(MetadataProperty, value); }
string _display_name;
TextBlock _text;
public override void OnApplyTemplate() {
this.try_get_template_child("text", out _text);
_update_text();
}
void _update_text() {
if (_text != null) {
if (string.IsNullOrEmpty(_display_name)) {
_text.ClearValue(TextBlock.TextProperty);
} else {
_text.SetCurrentValue(TextBlock.TextProperty, _display_name);
}
}
}
}
}
}
@@ -1,7 +1,6 @@
using bts.api;
using bts.spark;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@@ -15,10 +14,9 @@ namespace mbts.ui.controls {
SetCurrentValue(FocusableProperty, false);
}
public SparkMenu(IEnumerable<SparkCommand> commands, IBus bus) : this() =>
// ObservableCollection to avoid WPF Binding Leak
ItemsSource = new ObservableCollection<SparkCommandVM>(commands.Select(c => new SparkCommandVM(c, bus)));
ItemsSource = commands.Select(c => new SparkCommandVM(c, bus)).ToList();
public bool ShowText {
get { return (bool)GetValue(ShowTextProperty); }
@@ -1,18 +0,0 @@
using bts.api;
using mbts.ui.utility;
using System;
using System.Windows.Data;
namespace mbts.ui.converters
{
public class SecurityTypeStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return SecurityDisplay.format_security_type((SecurityType)value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
}
@@ -1,32 +0,0 @@
using bts.api;
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace mbts.ui.converters
{
[ValueConversion(typeof(SecurityType), typeof(Brush))]
public class SecurityTypeForegroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value is SecurityType) return _convert((SecurityType)value);
//else if (value is SecurityTuple) return _convert(((SecurityTuple)value).security_type);
//else if (value is SecurityExpiration) return _convert(((SecurityExpiration)value).security_type);
else return SystemColors.ControlTextBrush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
private Brush _convert(SecurityType type) {
switch (type) {
case SecurityType.Option: return (Brush)BTSColors.header_text;
case SecurityType.Future: return new SolidColorBrush(Colors.Gold);
case SecurityType.Spread: return new SolidColorBrush(Colors.SlateBlue);
default: return new SolidColorBrush(Colors.PaleGreen);
}
}
}
}
@@ -26,8 +26,6 @@
<cv:CellPathConverter x:Key="CellPathConverter" />
<cv:CellKeyConverter x:Key="CellKeyConverter" />
<cv:SecurityDisplayTextConverter x:Key="SecurityTextConverter"/>
<cv:SecurityTypeForegroundConverter x:Key="SecurityTypeForegroundConverter" />
<cv:SecurityTypeStringConverter x:Key="SecurityTypeStringConverter" />
<cv:NumericSignBrushConverter x:Key="NumericSignBrushConverter" />
<cv:TickDisplayTextConverter x:Key="TickTextConverter" />
<cv:DoubleTextConverter x:Key="DoubleTextConverter" />
@@ -154,7 +154,6 @@ namespace mbts.ui.shell
foreach (TabDocument document in _tabs.Items.OfType<TabDocument>().ToList()) {
if (document != null && document.ViewModel.CloseCommand.CanExecute(this)) {
document.ViewModel.CloseCommand.Execute(this);
document.ViewModel.Closing -= TabClosing;
}
}
}
@@ -6,7 +6,6 @@ using System.Windows.Interop;
using System.Runtime.InteropServices;
using mbts.ui.native;
using System.Windows;
using System.Windows.Threading;
namespace mbts.ui.shell
{
@@ -83,14 +82,7 @@ namespace mbts.ui.shell
return _hwnd;
}
protected override void DestroyWindowCore(HandleRef hwnd) {
// Improve GC performance by keeping windows out of the Finalizer queue
if (hwnd.Wrapper is WPFPanel p) {
p.BeginInvoke((Action)(() => {
p.Dispose();
}));
}
}
protected override void DestroyWindowCore(HandleRef hwnd) { }
[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr SetParent(HandleRef hwndChild, HandleRef hwndParent);
@@ -10,7 +10,6 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Windows.Interop;
namespace mbts.ui.shell
{
@@ -20,8 +19,9 @@ namespace mbts.ui.shell
class UIThreadData : IDisposable {
public Dispatcher dispatcher;
public Thread thread;
public MSG last_msg;
public void Dispose() {}
public List<ISlowWindow> windows = new List<ISlowWindow>();
public System.Windows.Interop.MSG last_msg;
public void Dispose() { lock(windows) Disposable.dispose(windows); }
}
int _count = 0;
ConcurrentDictionary<string, UIThreadData> _shared_threads = new ConcurrentDictionary<string, UIThreadData>();
@@ -38,7 +38,7 @@ namespace mbts.ui.shell
uid.thread = new Thread(() => {
uid.dispatcher = Dispatcher.CurrentDispatcher;
uid.dispatcher.UnhandledException += BTSApplicationBootstrapper.CurrentDispatcher_HandleClipboardError;
ComponentDispatcher.ThreadFilterMessage += (ref MSG m,ref bool h) => ComponentDispatcher_ThreadFilterMessage(uid, ref m, ref h);
System.Windows.Interop.ComponentDispatcher.ThreadFilterMessage += (ref System.Windows.Interop.MSG m,ref bool h) => ComponentDispatcher_ThreadFilterMessage(uid, ref m, ref h);
try {
Dispatcher.Run();
}
@@ -62,7 +62,7 @@ namespace mbts.ui.shell
return uid;
}
private void ComponentDispatcher_ThreadFilterMessage(UIThreadData d, ref MSG msg, ref bool handled) {
private void ComponentDispatcher_ThreadFilterMessage(UIThreadData d, ref System.Windows.Interop.MSG msg, ref bool handled) {
d.last_msg = msg;
// We definitely don't need to reload themes on background threads
@@ -75,6 +75,7 @@ namespace mbts.ui.shell
private Dispatcher attach_window(UIThreadData uid, ISlowWindow window) {
uid.dispatcher.BeginInvoke(DispatcherPriority.Send, (Action)(() => {
lock(uid.windows) uid.windows.Add(window);
window.thread_init();
}));
return uid.dispatcher;
@@ -29,8 +29,8 @@ namespace mbts.ui.shell
public ShellWindowVM(IShellWindow model, IWindowManager wm, IShellThreadFactory threads, string host = "") {
Log.assert(model != null);
_model = model;
_model.TitleChanged += title_changed;
_model.IconChanged += icon_changed;
_model.TitleChanged += () => { if (!HasCustomTitle) RaisePropertyChanged("Title"); };
_model.IconChanged += () => { _icon = null; RaisePropertyChanged("Icon"); };
_wm = wm;
host_id = host;
@@ -46,16 +46,6 @@ namespace mbts.ui.shell
_edit_mode = backing(nameof(EditMode), false);
}
void title_changed() {
if (!HasCustomTitle) {
RaisePropertyChanged("Title");
}
}
void icon_changed() {
_icon = null;
RaisePropertyChanged("Icon");
}
public string host_id { get; set; }
public Dispatcher Dispatcher { get { return _dispatcher; } }
@@ -218,9 +208,6 @@ namespace mbts.ui.shell
public void Dispose() {
_model.TitleChanged -= title_changed;
_model.IconChanged -= icon_changed;
if (IsShared) {
// Shared threads continue running
_dispatcher.Invoke(() => Disposable.dispose(ref _model));
@@ -234,7 +221,6 @@ namespace mbts.ui.shell
Disposable.dispose(ref _model);
}
_ui = null;
Disposable.dispose(ref _hwnd_host);
}
+5 -11
View File
@@ -31,13 +31,11 @@ namespace mbts.ui.shell {
readonly Lazy<IBookmarks> _bookmarks;
IDisposable _dispose;
object _lock = new object();
readonly IBus _bus;
[ImportingConstructor]
public WindowManager(
IShellThreadFactory threads, Lazy<ISettingsCollector> settings,
ISavedWindowFactory factory, Lazy<IBookmarks> bookmarks, IBus bus) {
_bus = bus;
_threads = threads;
_window_settings = settings;
_window_factory = factory;
@@ -94,15 +92,11 @@ namespace mbts.ui.shell {
public IReadOnlyList<TabDocument> list_documents() => _documents.Values.ToList();
public async Task add(IShellWindow window) {
if (window != null) {
if (!Application.Current.Dispatcher.CheckAccess())
await Application.Current.Dispatcher.InvokeAsync(() => add(window));
Log.Ui.info($"Opening window {window.diagnostic_name()}.");
lock (_lock) {
_documents[window] = _create_document(window);
}
// Prevent the bus from holding on to a reference to the window
_bus.post(SparkTags.ShowWindow, null);
if (!Application.Current.Dispatcher.CheckAccess())
await Application.Current.Dispatcher.InvokeAsync(() => add(window));
Log.Ui.info($"Opening window {window.diagnostic_name()}.");
lock (_lock) {
_documents[window] = _create_document(window);
}
}
@@ -1,37 +0,0 @@
using bts.api.core;
using bts.api.persistence;
using bts.api.user;
using bts.test;
using mbts.persist;
using mbts.ui.shell;
using System;
using System.Linq;
namespace bts.settings {
public class SettingsManagerTool {
public static void run() {
var sc = new ServiceContext();
var users = sc.locate<UserService>();
var persist = sc.locate<PersistenceService>("settings");
var user_list = users.get_users(new GetUsersRequest());
var user_names = user_list.Users.Select(u => u.Login);
var from = ConsoleEx.prompt("From User", user_names);
var from_persist = new UserUISettings(user_list.Users.First(u => u.Login == from).Id, persist, null);
var workspaces = from_persist.query<WorkspaceDefinition>(WorkspacesModel.WORKSPACE_NAMESPACE, ".*").Result;
var selected_ws = ConsoleEx.prompt("Choose workspace:", workspaces.Select(w => w.key));
var to = ConsoleEx.prompt("To User", user_names);
var name = ConsoleEx.input_prompt("Rename workspace?", selected_ws);
var confirm = ConsoleEx.prompt($"copy workspace '{selected_ws}' from {from} to {to} and name it '{name}'. Confirm?", "Yes", "No");
if (confirm == "Yes") {
var ws_blob = workspaces.First(w => w.key == selected_ws).value;
var to_persist = new UserUISettings(user_list.Users.First(u => u.Login == to).Id, persist, null);
to_persist.set(WorkspacesModel.WORKSPACE_NAMESPACE, name, ws_blob).Wait();
Console.WriteLine("Done");
}
}
}
}
@@ -222,15 +222,16 @@ namespace mbts.ui.shell
}
}
if (def.windows != null) {
async Task restore(WindowState window) {
var w = await _window_restore.restore(window);
if (!_closing) {
await _wm.add(w);
foreach (var window in def.windows) {
Log.Ui.info("Loading window '{0}'".fmt(window.title));
var new_shell_window = await _window_restore.restore(window);
// user can close the launcher from the taskbar while a workspace is loading
if (_closing) {
return;
} else if (new_shell_window != null) {
await _wm.add(new_shell_window);
}
}
var loading = def.windows.Select(restore);
await Task.WhenAll(loading);
if (_closing) return;
}
sw.Stop();
Log.Ui.info($"Time to load workspace '{name}': {sw.Elapsed}");
@@ -73,10 +73,6 @@ namespace mbts.spark {
};
RecenterCommand = new DelegateCommand(_scroll_to_center);
DataContextChanged += (s, e) => _connect_vm();
Unloaded += (s, e) => {
Disposable.dispose(ref _settings_sub);
Disposable.dispose(ref _tick_sub);
};
}
enum ColumnType { Order, Bid, Price, Ask, Last, None }
@@ -390,7 +386,7 @@ namespace mbts.spark {
void _tick() {
if (_settings != null) {
ulong unused = 0;
bool last_trade_changed = _vm.md.get_last_trade(ref unused, out _last_trade, out DateTime _, out ulong __);
bool last_trade_changed = _vm.md.get_last_trade(ref unused, out _last_trade, out ulong __);
bool depth_changed = _vm.md.get_depth(ref _prices_updated, ref _bids, ref _asks);
_mkt_break_lines.refresh();
bool prices_changed =
@@ -78,7 +78,7 @@ namespace mbts.spark {
_dispose = Disposable.Create(
settings_command,
order_properties_vm,
order_properties_vm.observe_account(true).Select(a => a.name).Subscribe(account_positions),
order_properties_vm.observe_account(true).Select(a => a?.name ?? string.Empty).Subscribe(account_positions),
account_positions,
size_calculator = new CalculatorLogic(0, _settings_manager.subscribe_current().Select(s => s.calculator_buttons)),
bus.subscribe(SparkTags.LogActivity).OnDispatcher().Subscribe(status_update),
@@ -275,7 +275,7 @@ namespace mbts.spark {
_open.set(md.open_price);
_settlement.set(md.settlement_price);
ulong unused = 0;
md.get_last_trade(ref unused, out NativePriceSize last, out DateTime time, out ulong volume);
md.get_last_trade(ref unused, out NativePriceSize last, out ulong volume);
if (!double.IsNaN(last.price)) {
_change.set(last.price - md.settlement_price);
} else {
@@ -113,7 +113,7 @@ namespace mbts.spark {
if (!sec_md.md.get_bbo(ref bbo_time, out NativePriceSize bid, out NativePriceSize ask)) return false;
// last is only used in waterfall and last calculations, so if neither of those types are selected
// we should not exit early if last is somehow not avaialble
if (!sec_md.md.get_last_trade(ref last_trade_time, out NativePriceSize last, out DateTime unused1, out ulong unused2) && pnl_request.HasFlag(pnl_calc_type.waterfall | pnl_calc_type.last)) return false;
if (!sec_md.md.get_last_trade(ref last_trade_time, out NativePriceSize last, out ulong unused2) && pnl_request.HasFlag(pnl_calc_type.waterfall | pnl_calc_type.last)) return false;
if (pnl_request.HasFlag(pnl_calc_type.waterfall)) {
// last price is available and withing range of available bid and ask
@@ -16,7 +16,6 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using mbts.ui;
using System.Collections.ObjectModel;
namespace bts.view {
public partial class SparkWindowView : UserControl {
@@ -37,8 +36,7 @@ namespace bts.view {
_menu.ShowText = false;
_grid.Children.Add(_menu);
if (ContextMenu != null) {
// ObservableCollection to avoid WPF Binding Leak
ContextMenu.ItemsSource = new ObservableCollection<SparkCommandVM>(commands.Select(c => new SparkCommandVM(c, bus)));
ContextMenu.ItemsSource = commands.Select(c => new SparkCommandVM(c, bus));
}
} else {
expand_button.Visibility = Visibility.Collapsed;
@@ -12,7 +12,7 @@ namespace mbts.spark.utilities
internal interface ISecurityMDPump
{
IObservable<(double bid, uint bid_size, double ask, uint ask_size)> bbo { get; }
IObservable<(double last, uint last_size, DateTime time_stamp, ulong volume)> last_trade { get; }
IObservable<(double last, uint last_size, ulong volume)> last_trade { get; }
IObservable<double> bid_price { get; }
IObservable<uint> bid_size { get; }
IObservable<double> ask_price { get; }
@@ -61,7 +61,6 @@ namespace mbts.spark.utilities
private double _bid_price, _ask_price, _last_price;
private uint _bid_size, _ask_size, _last_size;
private ulong _volume;
private DateTime _last_time;
private bool _refresh_bbo(MDConsumerHandle md) {
if (md.get_bbo(ref _bbo_ref, out NativePriceSize b, out NativePriceSize a)) {
@@ -75,10 +74,9 @@ namespace mbts.spark.utilities
}
private bool _refresh_last(MDConsumerHandle md) {
if (md.get_last_trade(ref _last_ref, out NativePriceSize ps, out DateTime ts, out ulong v)) {
if (md.get_last_trade(ref _last_ref, out NativePriceSize ps, out ulong v)) {
_last_price = ps.price;
_last_size = ps.size;
_last_time = ts;
_volume = v;
return true;
}
@@ -86,17 +84,16 @@ namespace mbts.spark.utilities
}
private (double, uint, double, uint) _select_bbo() { return (_bid_price, _bid_size, _ask_price, _ask_size); }
private (double, uint, DateTime, ulong) _select_last() { return (_last_price, _last_size, _last_time, _volume); }
private (double, uint, ulong) _select_last() { return (_last_price, _last_size, _volume); }
public IObservable<(double bid, uint bid_size, double ask, uint ask_size)> bbo => _bbo_pump.Select(_ => _select_bbo()).StartWith(_select_bbo());
public IObservable<(double last, uint last_size, DateTime time_stamp, ulong volume)> last_trade => _last_pump.Select(_ => _select_last()).StartWith(_select_last());
public IObservable<(double last, uint last_size, ulong volume)> last_trade => _last_pump.Select(_ => _select_last()).StartWith(_select_last());
public IObservable<double> bid_price => _bbo_pump.Select(_ => _bid_price).DistinctUntilChanged().StartWith(_bid_price);
public IObservable<uint> bid_size => _bbo_pump.Select(_ => _bid_size).DistinctUntilChanged().StartWith(_bid_size);
public IObservable<double> ask_price => _bbo_pump.Select(_ => _ask_price).DistinctUntilChanged().StartWith(_ask_price);
public IObservable<uint> ask_size => _bbo_pump.Select(_ => _ask_size).DistinctUntilChanged().StartWith(_ask_size);
public IObservable<double> last_price => _bbo_pump.Select(_ => _last_price).DistinctUntilChanged().StartWith(_last_price);
public IObservable<uint> last_size => _bbo_pump.Select(_ => _last_size).DistinctUntilChanged().StartWith(_last_size);
public IObservable<DateTime> last_time => _last_pump.Select(_ => _last_time).DistinctUntilChanged().StartWith(_last_time);
public IObservable<ulong> volume => _last_pump.Select(_ => _volume).DistinctUntilChanged().StartWith(_volume);
public IObservable<double> session_low => _root_pump.Select(md => md.session_low).DistinctUntilChanged();
public IObservable<double> session_high => _root_pump.Select(md => md.session_high).DistinctUntilChanged();
@@ -110,6 +110,29 @@ namespace mbts.spark.utilities {
return defs;
}
public Dictionary<OrderTemplateHeader, CustomOrderDefinitionImmutable> default_order_type_templates(SecurityImmutable security, bool is_child = false) {
const int name_ref_seed = 1;
// standard types
var defs = default_order_types(security, is_child)
.Except(new[] { CustomOrderDefinition.CustomOneofCase.Dynamic })
.ToDictionary(ot => new OrderTemplateHeader(new OrderTemplateKey(ot), OrderUtils.order_type_display_name(ot)), ot => new CustomOrderDefinitionImmutable(custom_order(ot)));
// TODO: replace with better naming scheme (right now, the order type ref count 'n' is appended to the order-type name)
var order_type_ref_count = defs.ToDictionary(kv => kv.Key, kv => name_ref_seed);
var dynamic_defs = new List<CustomOrderDefinitionImmutable>();
DynamicOrderHelper.add_dynamic_order_defs(dynamic_defs, ot => custom_order(ot));
foreach (var ddef in dynamic_defs) {
var header = new OrderTemplateHeader(new OrderTemplateKey(ddef.CustomCase, ddef.Dynamic.DynamicOrderType), ddef.Dynamic.DynamicOrderType );
// if (order_type_ref_count.TryGetValue(header, out int count)) {
// order_type_ref_count[header]++;
// header = new OrderTemplateHeader(new OrderTemplateKey(header.key.type, $"{header.key.name}{count}"), $"{header.display_name}{count}");
// } else {
// order_type_ref_count[header] = name_ref_seed;
// }
defs.Add(header, ddef);
}
return defs;
}
public IEnumerable<CustomOrderDefinition.CustomOneofCase> default_order_types(SecurityImmutable? security, bool is_child=false) =>
security.HasValue ? default_order_types(security.Value, is_child) : default_order_types();
@@ -212,7 +235,7 @@ namespace mbts.spark.utilities {
public DynamicOrderDefinition dynamic_order() => new DynamicOrderDefinition
{
Child = custom_order(),
//Child = custom_order(),
};
@@ -7,8 +7,8 @@ using System.Windows.Threading;
namespace mbts.spark {
static class WorkLoop {
public const double default_excessive_cycle_ratio = 1.2;
static readonly DispatcherPriority default_priority;
static readonly TimeSpan default_interval;
public static readonly TimeSpan default_interval;
public static readonly DispatcherPriority default_priority;
static WorkLoop() {
var timer_params = config.Configuration.get<bts.config.LadderKnobs>();
@@ -1,90 +0,0 @@
using bts.api;
using mbts.utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui.utility
{
public enum DateFormat { Standard, DayMonthAbvDiff2000, DayMonthCodeDiff2000 }
static class SecurityDisplay
{
/// <summary>
/// Returns a shorthand string representation of a security type appropriate for UIs.
/// </summary>
public static string format_security_type(SecurityType type) {
switch (type) {
case SecurityType.Option: return "Opt";
case SecurityType.Future: return "Fut";
case SecurityType.Stock: return "Stk";
case SecurityType.Spread: return "Spd";
default: return type.ToString();
}
}
/// <summary>
/// Returns a concise string representation of a security expiration appropriate for UIs (e.g. es March 22, 2019: es 22 Mar 19).
/// </summary>
public static string format_expiration(SecurityType sec_type, string symbol, DateTime exp_date) { return format_expiration(sec_type, symbol, exp_date, false, true, true, DateFormat.DayMonthAbvDiff2000); }
/// <summary>
/// Returns a concise string representation of a security expiration (using symbol-mapped expiration dates, and single-letter month codes) appropriate for UIs (e.g. es March 22, 2019: [Opt] [es] 22 Mar 19).
/// </summary>
public static string format_expiration(SecurityType sec_type, string symbol, DateTime exp_date, bool show_type, bool show_symbol, bool use_mapped_exp) { return format_expiration(sec_type, symbol, exp_date, show_type, show_symbol, use_mapped_exp, DateFormat.Standard); }
/// <summary>
/// Returns a concise string representation of a security expiration (using symbol-mapped expiration dates, and single-letter month codes) appropriate for UIs (e.g. es March 22, 2019: [Opt] [es] 22 Mar 19, or [Opt] [es] 22 H19).
/// </summary>
public static string format_expiration(SecurityType sec_type, string symbol, DateTime exp_date, bool show_type, bool show_symbol, bool use_mapped_exp, DateFormat date_format) {
if (use_mapped_exp) exp_date = ExpirationFormatter.convert_real_to_mapped(sec_type, symbol, exp_date);
string result = format_date(exp_date);
if (show_symbol) result = (symbol + " " + result);
if (show_type) result = (format_security_type(sec_type) + " " + result);
return result;
}
/// <summary>
/// Returns a concise string representation of a future trade appropriate for UIs.
/// </summary>
public static string format_future_trade(string symbol, DateTime exp_date, int size, double marginal_price) { return "{0} {1} lots @ {2:C}".fmt(format_expiration(SecurityType.Future, symbol, exp_date, false, true, true), size, marginal_price); }
/// <summary>
/// Returns a concise string representation of an option trade appropriate for UIs.
/// </summary>
public static string format_option_trade(string symbol, DateTime exp_date, double strike, bool is_call, int size, double marginal_price) {
return "{0} {1} {2} {3} lots @ {4:C}".fmt(format_expiration(SecurityType.Option, symbol, exp_date, false, true, true), strike, is_call ? "C" : "P", size, marginal_price);
}
/// <summary>
/// Returns a concise string representation of a date (using standard month abbreviations) appropriate for UIs (e.g. March 22, 2019: 22 Mar 19).
/// </summary>
public static string format_date(DateTime date) { return format_date(date.Year, date.Month, date.Day, DateFormat.DayMonthAbvDiff2000); }
/// <summary>
/// Returns a concise string representation of a date (using single-letter month codes) appropriate for UIs (e.g. March 22, 2019: 22 Mar 19, or 22 H19).
/// </summary>
public static string format_date(int year, int month, int day, DateFormat format) {
switch(format) {
case DateFormat.DayMonthAbvDiff2000: return "{0} {1} {2}".fmt(day, get_month_abv(month), year - 2000);
case DateFormat.DayMonthCodeDiff2000: return "{0} {1}{2}".fmt(day, get_month_code(month), year - 2000);
case DateFormat.Standard: goto default;
default: return "{0}/{1}/{2}".fmt(day, month, year);
}
}
/// <summary>
/// Returns the option industry standard month code.
/// </summary>
public static string get_month_code(int month) { _check_month(month); return _month_codes[month - 1]; }
/// <summary>
/// Returns the standard month abbreviation (Jan, Feb, Mar, etc.).
/// </summary>
public static string get_month_abv(int month) { _check_month(month); return _month_abvs[month - 1]; }
#region Private Members
private static void _check_month(int month) { if (month < 1 || month > 12) throw new ArgumentException("There are only twelve months in a year", "month"); }
private static string[] _month_codes = new string[] { "F", "G", "H", "J", "K", "M", "N", "Q", "U", "V", "X", "Z" };
private static string[] _month_abvs = new string[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
#endregion
}
}
-68
View File
@@ -129,7 +129,6 @@ namespace bts.api.core {
public IEnumerable<T> removed { get; }
public TKey key(T t) { return _key(t); }
internal Func<T, TKey> key_extractor => _key;
#if DEBUG
public override string ToString() {
@@ -348,72 +347,5 @@ namespace bts.api.core {
return source.Select(t => new LegacyTableUpdate<T, TKey>(t));
}
public static IObservable<Update<T, TKey>> Filter<T, TKey>(this IObservable<Update<T, TKey>> source, IObservable<Func<T, bool>> predicate) where TKey : IEquatable<TKey>
{
return Observable.Create<Update<T, TKey>>(observer =>
{
Update<T, TKey> unfiltered_cache = null, filtered_cache = null;
Func<T, bool> pred_cache = null;
Log.DataFlow.diag($"Create Filter {typeof(T).Name}");
return source.CombineLatest(predicate, (upd, pred) => (upd, pred)).Subscribe( tup =>
{
bool pred_changed = !object.ReferenceEquals(tup.pred, pred_cache) && (pred_cache != null);
pred_cache = tup.pred;
unfiltered_cache = tup.upd;
if (pred_changed && filtered_cache != null)
{
//clear out existing
var clear = new Update<T, TKey>(
(IReadOnlyCollection<T>)Array.Empty<T>(),
Enumerable.Empty<T>(),
Enumerable.Empty<Change<T>>(),
filtered_cache.all, unfiltered_cache.key_extractor);
observer.OnNext(clear);
var refilter = unfiltered_cache.all.Where(tup.pred).ToList();
var readd = new Update<T, TKey>(
refilter, refilter,
Enumerable.Empty<Change<T>>(),
Enumerable.Empty<T>(), unfiltered_cache.key_extractor);
observer.OnNext(readd);
filtered_cache = readd;
}
else
{
IReadOnlyCollection<T> all; IEnumerable<T> added, removed; IEnumerable<Change<T>> changed;
apply_predicate(tup.pred, tup.upd, out all, out added, out changed, out removed);
var next = new Update<T, TKey>(all, added, changed, removed, tup.upd.key_extractor);
observer.OnNext(next);
filtered_cache = next;
}
},
() => observer.OnCompleted());
});
}
private static void apply_predicate<T, TKey>(Func<T, bool> predicate, Update<T, TKey> cache,
out IReadOnlyCollection<T> all, out IEnumerable<T> added,
out IEnumerable<Change<T>> changed, out IEnumerable<T> removed) where TKey : IEquatable<TKey>
{
var adds = new List<T>();
adds.AddRange(cache.added.Where(predicate));
var rems = new List<T>();
rems.AddRange(cache.removed.Where(predicate));
var diffs = new List<Change<T>>();
foreach (var change in cache.changes)
{
if(!predicate(change.Old) && predicate(change.New))
adds.Add(change.New);
else if(predicate(change.Old) && !predicate(change.New))
rems.Add(change.Old);
else if(predicate(change.Old) && predicate(change.New))
diffs.Add(change);
}
all = cache.all.Where(predicate).ToList();
added = adds;
changed = diffs;
removed = rems;
}
}
}
+2 -1
View File
@@ -31,7 +31,8 @@ namespace bts {
Mercury,
Testing,
Latency,
DataFlow
DataFlow,
Plugin
// Please keep this list in sync with logging.h/logging.cpp
}
readonly struct Logger {
@@ -113,7 +113,5 @@ namespace mbts.co2
[DllImport("libspark")]
internal static extern void co2_get_zero_and_nan_book(ref NativePriceLevel nps);
[DllImport("libspark")]
internal static extern void co2_get_sample_datetime(ref utils.LLDateTime now, ref utils.LLDateTime utc_now);
}
}
+10 -5
View File
@@ -72,7 +72,12 @@ namespace mbts.co2
return false;
}
public bool get_last_trade(ref ulong time, out NativePriceSize ps, out DateTime timestamp, out ulong total_volume) {
public bool get_last_trade(ref ulong time, out NativePriceSize ps, out ulong total_volume) {
total_volume = _cold->total_volume;
return (_cold->last_trade.load_if_new(ref time, out ps));
}
public bool get_last_trade(ref ulong time, out NativePriceSize ps, out utils.LLDateTime timestamp, out ulong total_volume) {
timestamp = _cold->last_trade_time;
total_volume = _cold->total_volume;
return (_cold->last_trade.load_if_new(ref time, out ps));
@@ -88,15 +93,15 @@ namespace mbts.co2
public ulong total_volume { get { return _cold->total_volume; } }
public double settlement_price { get { return _cold->settlement_price;} }
public DateTime settlement_date { get { return _cold->settlement_date;} }
public utils.LLDate settlement_date { get { return _cold->settlement_date;} }
public double previous_settlement_price { get { return _cold->previous_settlement_price;} }
public DateTime previous_settlement_date { get { return _cold->previous_settlement_date;} }
public utils.LLDate previous_settlement_date { get { return _cold->previous_settlement_date;} }
public double open_price { get { return _cold->open_price;} }
public DateTime open_date { get { return _cold->open_date; } }
public utils.LLDate open_date { get { return _cold->open_date; } }
public double close_price { get { return _cold->close_price;} }
public DateTime close_date { get { return _cold->close_date; } }
public utils.LLDate close_date { get { return _cold->close_date; } }
public double session_high { get { return _cold->session_high;} }
public double session_low { get { return _cold->session_low; } }
public double open_interest { get { return _cold->open_interest; } }
@@ -1,40 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace bts.collections {
public static class ListExtensions {
public static void RemoveWhere<T>(this IList<T> list, Func<T, bool> where) { foreach (var r in list.Where(where).ToList()) list.Remove(r); }
public static void RemoveWhere<T>(this IList<T> list, Func<T, bool> where, Action<T> on_remove) {
int count = list.Count;
for (int i = 0; i < count; i++) {
var t = list[i];
if (where(t)) {
list.RemoveAt(i--);
--count;
on_remove(t);
}
}
}
private static Random rng = new Random();
public static void Shuffle<T>(this IList<T> list) {
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
public static void RemoveAt<T>(this IList<T> list, int index, int count) {
for (int i = 0; i < count; i++)
list.RemoveAt(index);
}
}
}
@@ -27,7 +27,7 @@ namespace bts.collections {
// Insert (return: true) or overwrite (return false) a new value
// NOTE: you should use AddAll if you are adding multiple values
public bool Insert(T item) {
bool Insert(T item) {
var item_id = id(item);
if (!_index.TryGetValue(item_id, out Node itemnode)) {
// Item is new
@@ -57,7 +57,7 @@ namespace bts.collections {
}
}
}
public void Remove(T item) {
void Remove(T item) {
var item_id = id(item);
if (_index.TryGetValue(item_id, out Node itemnode)) {
if (_index.TryGetValue(parent(item), out Node parentnode)) {
@@ -75,7 +75,7 @@ namespace bts.collections {
}
// Note: This method does not handle replacements correctly.
public void AddAll(IEnumerable<T> items) {
void AddAll(IEnumerable<T> items) {
// When adding multiple items we build the index first to avoid the children before parents problem
foreach (var item in items) {
//_index[id(item)] = new Node(item, _comparer);
@@ -103,27 +103,6 @@ namespace bts.collections {
}
}
}
Node parentmost(T item) {
if (_index.TryGetValue(parent(item), out Node pnode)) {
return parentmost(pnode.value);
}
else if (_index.TryGetValue(id(item), out Node mynode)) {
return mynode;
}
return null;
}
public IEnumerable<T> EnumerateGroup(T item) {
var pnode = parentmost(item);
if (pnode != null) {
yield return pnode.value;
foreach (var child in iterate(pnode.children)) {
yield return child;
}
}
}
public IEnumerator<T> GetEnumerator() {
return iterate(_tree).GetEnumerator();
}
+9 -28
View File
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace bts.test {
public static class ConsoleEx {
@@ -23,34 +21,17 @@ namespace bts.test {
}
public static T prompt<T>(string prompt, params T[] options) {
return ConsoleEx.prompt(prompt, (IEnumerable<T>)options);
}
public static T prompt<T>(string prompt, IEnumerable<T> options) {
WriteLine(prompt, ConsoleColor.Yellow);
for (int x = 0; x < options.Count(); ++x) {
Console.WriteLine(prompt);
for (int x = 1; x <= options.Length; ++x) {
Write($"{x}. ", ConsoleColor.Yellow);
WriteLine(options.ElementAt(x).ToString());
WriteLine(options[x].ToString());
}
int response = 0;
bool success = false;
while(!success) {
var key = Console.ReadKey();
success = Int32.TryParse(key.KeyChar.ToString(), out response);
if (response > options.Count()) success = false;
}
var value = options.ElementAt(response);
Console.WriteLine(" >> " + value.ToString());
Console.WriteLine();
return value;
}
public static string input_prompt(string prompt, string default_result) {
Write(prompt + " (", ConsoleColor.Yellow);
Write(default_result, ConsoleColor.White);
Write(") ", ConsoleColor.Yellow);
var result = Console.ReadLine();
if (string.IsNullOrWhiteSpace(result)) return default_result;
return result;
int response;
do {
Int32.TryParse(Console.ReadKey().ToString(), out response);
if (response > options.Length) response = 0;
} while (response == 0);
return options[response];
}
@@ -43,9 +43,6 @@ namespace bts.test {
if (ue.InnerException is AssertFailture af) {
result.Message = $"Assertion Failure:\n{af.Message}\nLocation:{af._filename}:{af._line}";
}
else if (ue.InnerException != null) {
result.Message = $"Unexpected error: {ue.InnerException}";
}
else {
result.Message = $"Unexpected error: {ue.Message}\n\n{ue.StackTrace}";
}
@@ -27,6 +27,9 @@ namespace bts.utils {
public static IEqualityComparer<T?> CreateNullable<T>(Func<T, T, bool> equals) where T : struct =>
new AnonymousEqualityComparer<T?>(nullable_equals);
public static IEqualityComparer<T> null_check_wrapper<T>(IEqualityComparer<T> comparer) where T : class =>
new AnonymousEqualityComparer<T>((a, b) => NullCheckEquals(a, b, comparer.Equals), comparer.GetHashCode);
public static bool safe_sequence_equals<T>(IEnumerable<T> source, IEnumerable<T> other) =>
NullCheckEquals(source, other, (a, b) => a.SequenceEqual(b));
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace mbts {
@@ -23,14 +24,20 @@ namespace mbts {
return range.Skip(index).First();
}
[DllImport("libspark")]
private static extern void ll_to_parts(UInt64 ll_time, out int year, out int month, out int day, out int hour, out int minute, out int second, out int ms);
public static DateTime lldatetime(this ulong ll) {
// Warning: Do not remove these consts, or try to simplify this method.
// System.DateTime() requires bits 63-64 to be empty in the "DateTime(long ticks)" constructor.
// The only way to set the DateTimeKind is through the "DateTime(long ticks, DateTimeKind kind)" constructor.
// (Yes, it is annoying. Internally, System.DateTime stores the DateTimeKind in bits 63-64 - in the exact format we already have!!!)
const ulong ticks_mask = 0x3FFFFFFFFFFFFFFF;
const int kind_shift = 62;
return new DateTime((long)(ll & ticks_mask), (DateTimeKind)(ll >> kind_shift));
int year, month, day, hour, minute, second, ms;
ll_to_parts(ll, out year, out month, out day, out hour, out minute, out second, out ms);
if (year > 0) {
return new DateTime(year, month, day, hour, minute, second, ms, DateTimeKind.Utc).ToLocalTime();
}
return DateTime.MinValue;
}
public static DateTime lldatetime(this long ll) { return ((ulong)ll).lldatetime(); }
}
}
}
@@ -18,7 +18,6 @@ namespace mbts.utils
}
public static implicit operator DateTime (LLDateTime time) { return time.to_datetime(); }
public static implicit operator LLDateTime (DateTime time) { return new LLDateTime((ulong)time.Ticks); }
public static LLDateTime now() {
return new LLDateTime(BTSNativeMethods.ll_now());
-5
View File
@@ -56,11 +56,6 @@ namespace bts.utils {
if (value > max) return max;
return value;
}
public static long within_range(long min, long max, long value) {
if (value < min) return min;
if (value > max) return max;
return value;
}
public static double within_range(double min, double max, double value) {
if (value < min) return min;
if (value > max) return max;
+13 -27
View File
@@ -112,7 +112,7 @@ namespace mbts.co2.tests
Assert.IsTrue(last.price == 4.2);
Assert.IsTrue(last.size == 101);
Assert.IsTrue(entry.total_volume == 12345);
Assert.IsTrue(compare_date(entry.last_trade_time, DateTime.UtcNow));
Assert.IsTrue(compare_date(entry.last_trade_time, DateTime.Now));
//// Settlement
Assert.AreEqual(entry.settlement_price, 3.3);
Assert.IsTrue(entry.settlement_date == new DateTime(2015, 9, 3));
@@ -210,19 +210,24 @@ namespace mbts.co2.tests
Assert.IsTrue(ask.size == 202);
time = 0;
DateTime trade_date;
ulong total_volume;
NativePriceSize last_trade;
Assert.IsTrue(handle.get_last_trade(ref time, out last_trade, out trade_date, out total_volume));
Assert.IsTrue(handle.get_last_trade(ref time, out NativePriceSize last_trade, out utils.LLDateTime ll_trade_date, out ulong total_volume));
// trade date
DateTime trade_date = ll_trade_date;
Assert.IsTrue(last_trade.price == 5.5 && last_trade.size == 505);
Assert.IsTrue(trade_date.Year == 2015 && trade_date.Month == 9 && trade_date.Day == 22);
Assert.IsTrue(total_volume == 4200);
Assert.IsTrue(handle.settlement_date.Year == 2000 && handle.settlement_date.Day == 9 && handle.settlement_date.Month == 8);
// settlement date
DateTime settlement_dt = handle.settlement_date;
Assert.IsTrue(settlement_dt.Year == 2000 && settlement_dt.Day == 9 && settlement_dt.Month == 8);
Assert.IsTrue(handle.settlement_price == 6.6);
Assert.IsTrue(handle.open_price == 7.7);
Assert.IsTrue(handle.open_date.Year == 2001 && handle.open_date.Month == 1 && handle.open_date.Day == 2);
// open date
DateTime open_dt = handle.open_date;
Assert.IsTrue(open_dt.Year == 2001 && open_dt.Month == 1 && open_dt.Day == 2);
Assert.IsTrue(handle.close_price == 8.8);
Assert.IsTrue(handle.close_date.Year == 2002 && handle.close_date.Month == 3 && handle.close_date.Day == 4);
// close date
DateTime close_dt = handle.close_date;
Assert.IsTrue(close_dt.Year == 2002 && close_dt.Month == 3 && close_dt.Day == 4);
Assert.IsTrue(handle.session_high == 9.9);
Assert.IsTrue(handle.session_low == 10.1);
@@ -251,25 +256,6 @@ namespace mbts.co2.tests
Assert.IsTrue(double.IsNaN(npl.bid.price));
}
[Test]
public void lldatetime_test() {
utils.LLDateTime now = DateTime.MinValue, utc_now = DateTime.MinValue;
Co2NativeTestMethods.co2_get_sample_datetime(ref now, ref utc_now);
DateTime dt_now = now;
DateTime dt_utc_now = utc_now;
Assert.AreEqual(dt_now.ToShortDateString(), DateTime.Now.ToShortDateString());
Assert.AreEqual(dt_utc_now.ToShortDateString(), DateTime.UtcNow.ToShortDateString());
void assert_about_equal(int a, int b) {
Assert.That(Math.Abs(a - b) <= 1);
}
assert_about_equal(dt_now.Hour, DateTime.Now.Hour);
assert_about_equal(dt_now.Minute, DateTime.Now.Minute);
assert_about_equal(dt_now.Second, DateTime.Now.Second);
assert_about_equal(dt_utc_now.Hour, DateTime.UtcNow.Hour);
assert_about_equal(dt_utc_now.Minute, DateTime.UtcNow.Minute);
assert_about_equal(dt_utc_now.Second, DateTime.UtcNow.Second);
}
}
}
-116
View File
@@ -6,7 +6,6 @@ using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Subjects;
namespace mbts.api.Test {
[TestGroup]
@@ -105,121 +104,6 @@ namespace mbts.api.Test {
}
void validate_filter(IObservable<RawUpdate<Item>> raw, IObservable<Update<Item, int>> filtered, Func<Item, bool> pred)
{
var total = raw.ToEnumerable().Count();
// Validate that update is right at each point
for (var count = 1; count < total; ++count)
{
var orig = raw.Take(count).reconstitute(id).ToEnumerable().Last();
var next = filtered.Take(count).ToEnumerable().Last();
Assert.That(next.all.All(pred));
Assert.That(next.added.All(pred));
Assert.That(next.removed.All(pred));
Assert.That(next.changed.All(pred));
}
// Do it again with TableUpdate
for (var count = 1; count < total; ++count)
{
var orig = raw.Take(count).reconstitute(id).Table().ToEnumerable().Last();
var next = filtered.Take(count).ToEnumerable().Last();
Assert.That(next.added.All(pred));
Assert.That(next.removed.All(pred));
Assert.That(next.changed.All(pred));
}
}
void validate_filter_sub(IObservable<RawUpdate<Item>> raw, IObservable<Update<Item, int>> filtered, BehaviorSubject<Func<Item, bool>> pred)
{
raw.reconstitute(x => x.id).CombineLatest(filtered, (r, f) => Tuple.Create(r, f)).Subscribe(tup =>
{
Assert.That(tup.Item2.all.All(pred.Value));
Assert.That(tup.Item2.added.All(pred.Value));
Assert.That(!tup.Item2.removed.Any(pred.Value));
Assert.That(tup.Item2.changed.All(pred.Value));
Dictionary<int, Item> rawd = tup.Item1.added.ToDictionary(x => x.id);
Dictionary<int, Item> fd = tup.Item2.added.ToDictionary(x => x.id);
foreach (var kv in rawd)
{
if(pred.Value(kv.Value))
Assert.That(fd.ContainsKey(kv.Key));
else
Assert.That(!fd.ContainsKey(kv.Key));
}
});
}
[Test]
public void test_filter()
{
var raw = Observable.Create<RawUpdate<Item>>(observer =>
{
RawUpdate<Item> rup = new RawUpdate<Item>();
for (int x = 0; x < 50; ++x)
{
rup.Clear();
var item = _item_pool[x];
rup.added_items.Add(item);
rup.added_keys.Add(item.id);
observer.OnNext(rup);
}
var rand = new Random();
for (int r = 0; r < rand.Next(4, 30); r += rand.Next(1, 2))
{
rup.Clear();
rup.removed_keys.Add(_item_pool[r].id);
observer.OnNext(rup);
}
rup.Clear();
for (int c = 31; c < rand.Next(32, 50); c += rand.Next(1, 2))
{
var ch = new Item(_item_pool[c].id, _item_pool[c].name + "_change");
rup.changed_items.Add(ch);
rup.changed_keys.Add(ch.id);
}
observer.OnNext(rup);
observer.OnCompleted();
return Disposable.empty;
});
Func<Item, bool> evens = (x => x.id % 2 == 0);
Func<Item, bool> odds = (x => x.id % 2 == 1);
var predeven = Observable.Create<Func<Item, bool>>(observer =>
{
observer.OnNext(evens);
observer.OnCompleted();
return Disposable.empty;
});
var filtered = raw.reconstitute(x=> x.id).Filter(predeven);
validate_filter(raw, filtered, evens);
var predodd = Observable.Create<Func<Item, bool>>(observer =>
{
observer.OnNext(odds);
observer.OnCompleted();
return Disposable.empty;
});
filtered = raw.reconstitute(x => x.id).Filter(predodd);
validate_filter(raw, filtered, odds);
var sub= new BehaviorSubject<Func<Item, bool>>(evens);
filtered = raw.reconstitute(x => x.id).Filter(sub);
validate_filter_sub(raw, filtered, sub);
sub.OnNext(odds);
sub.OnCompleted();
}
[Test]
public async Task test_add() {
// Add 1 item 10x
@@ -1,20 +0,0 @@
using ManyConsole;
using System.Reflection;
namespace bts {
public class SettinsManagerCommand : ConsoleCommand {
public SettinsManagerCommand() {
IsCommand("SettingsManager", "Copy workspaces around");
}
public override int Run(string[] remainingArguments) {
var asm = Assembly.Load("spark");
var type = asm.GetType("bts.settings.SettingsManagerTool");
var run = type.GetMethod("run", BindingFlags.Static | BindingFlags.Public);
run.Invoke(null, new object[0]);
return 0;
}
}
}
-3
View File
@@ -13,8 +13,5 @@ namespace sparkcmd {
ConsoleCommandDispatcher.DispatchCommand(commands, args, Console.Out);
return;
}
}
}
+2 -1
View File
@@ -8,7 +8,8 @@
<Authors>Blue Trading Systems</Authors>
<Company>Blue Trading Systems</Company>
<Product>Spark</Product>
<Platforms>x64</Platforms>
<Platform>x64</Platform>
<PlatformTarget>x64</PlatformTarget>
<Version>1.0.0</Version>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
+1 -1
View File
@@ -1 +1 @@
set(sources files/Onixs.BTS.lic;files/cert_secure_logon.txt;files/perf_trace/compiled.css;files/perf_trace/index.html;files/perf_trace/jquery-3.3.1.min.js;files/perf_trace/main.js;files/perf_trace/plotly-latest.min.js)
set(sources /home/ppinto/Dropbox/bts/spark/src/data_files/files/Onixs.BTS.lic;/home/ppinto/Dropbox/bts/spark/src/data_files/files/Onixs.BTS.lic.orig;/home/ppinto/Dropbox/bts/spark/src/data_files/files/cert_secure_logon.txt)
+1 -1
View File
@@ -4,7 +4,7 @@ project(data_files)
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
# DEPENDENCIES
file (GLOB_RECURSE To_Copy RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} files/*)
file (GLOB_RECURSE To_Copy ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR} files/*)
update_deps_file("${To_Copy}")
# END DEPENDENCIES
Binary file not shown.
@@ -1,63 +0,0 @@
<html>
<head>
<title>BTS Spark Socket To Socket Requote 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;
}
.summary-block > ul {
margin: 0em 1em;
display:inline-block;
vertical-align: top;
}
.histogram-div {
width: 550px;
height:350px;
display:inline-block
}
.clear {
clear:both;
}
</style>
</head>
<body>
<div>
<div class="columns is-fullheight">
<div class="column is-narrow is-launcher-sidebar-menu">
<aside class="menu">
<ul class="menu-list">
<li>&nbsp;&nbsp;</li>
</ul>
</aside>
</div>
<div class="column is-main-content">
<div class="columns is-fullheight">
<!--div class="column is-narrow is-sidebar-menu">
</div-->
<div class="column is-main-content">
<!--div class="tabs is-boxed">
<ul>
<li class="is-active"><a>Stats</a></li>
</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>
<div id="output"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/perf_trace/jquery-3.3.1.min.js"></script>
<script src="/perf_trace/plotly-latest.min.js"></script>
<script src="/perf_trace/main.js"></script>
</body>
</html>
-2
View File
@@ -1,2 +0,0 @@
/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:s,sort:n.sort,splice:n.splice},w.extend=w.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||g(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],a!==(r=e[t])&&(l&&r&&(w.isPlainObject(r)||(i=Array.isArray(r)))?(i?(i=!1,o=n&&Array.isArray(n)?n:[]):o=n&&w.isPlainObject(n)?n:{},a[t]=w.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},w.extend({expando:"jQuery"+("3.3.1"+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==c.call(e))&&(!(t=i(e))||"function"==typeof(n=f.call(t,"constructor")&&t.constructor)&&p.call(n)===d)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e){m(e)},each:function(e,t){var n,r=0;if(C(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(C(Object(e))?w.merge(n,"string"==typeof e?[e]:e):s.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:u.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;o<a;o++)(r=!t(e[o],o))!==s&&i.push(e[o]);return i},map:function(e,t,n){var r,i,o=0,s=[];if(C(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&s.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&s.push(i);return a.apply([],s)},guid:1,support:h}),"function"==typeof Symbol&&(w.fn[Symbol.iterator]=n[Symbol.iterator]),w.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function C(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!g(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},P="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",I="\\["+M+"*("+R+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+R+"))|)"+M+"*\\]",W=":("+R+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+I+")*)|.*)\\)|)",$=new RegExp(M+"+","g"),B=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),F=new RegExp("^"+M+"*,"+M+"*"),_=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)Line truncated
-164
View File
@@ -1,164 +0,0 @@
$( document ).ready(function (){
let urlParams = new URLSearchParams(window.location.search);
num_rows = urlParams.get('r');
let show_rows = urlParams.get('showrows');
let trace_data_url = "/perf_trace_data";
if(num_rows != null) {
trace_data_url += "/" + num_rows;
}
$.ajax({
url: trace_data_url,
dataType: 'json',
context: document.body,
error: ajax_err,
crossDomain: true
}).done(function(result) {
if(show_rows != null) {
list_rows(show_rows, result);
}
else {
if(result.length > 0){
let summary_info = "<h1 class='is-size-3'><b>Host:</b> " + result[0].host + "</h1>";
$("#output").html(summary_info);
$(result).each(display_session);
}
}
});
});
function list_rows(show_rows, 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>";
$.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 += "</tr>";
});
summary_info += "</tbody></table>";
$("#output").html(summary_info);
return false;
}
});
}
function ajax_err (jqXHR, textStatus,errorThrown) {
$("#output").html("<h3 class='is-size-3 has-text-danger'>Error loading perf_trace_data</h3>" +
"<p>" + textStatus + "</p>" +
"<pre>" + errorThrown + "</pre>"
);
console.log(textStatus);
console.log(errorThrown);
}
function first_upper (str)
{
let ret_str = str[0].toUpperCase() + str.substring(1, str.length);
return ret_str
}
function generate_histogram(id, data)
{
var trace = {
x: data,
type: 'histogram',
marker: {
color: 'rgb(65,105,170)'
},
};
var plot_data = [trace];
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>";
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>";
}
$(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>";
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 += "</ul><hr class='clear'>";
$("#output").append(session_info);
let all_data = {};
$(value.rows).each(function(i, row) {
$.each(row, function(name, v) {
if(all_data[name] === undefined){
all_data[name] = [v];
}
else {
all_data[name].push(v);
}
});
});
//console.log(all_data);
$(value.summaries).each(function(i,v){
generate_histogram(value.session_name + "-" + v.value, all_data[v.value]);
});
}
Loaded 100 of 323 files, more files were not shown because too many files have changed in this diff. Show more