Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5330000e12 | ||
|
|
1d51a063ea | ||
|
|
da3dc9f477 | ||
|
|
f437d50041 | ||
|
|
c7bbcc1738 | ||
|
|
3bdde95b42 | ||
|
|
52e6be5d60 | ||
|
|
bda76d0234 | ||
|
|
7cb08ba90a | ||
|
|
ee6d05c737 | ||
|
|
62a5ee94e8 | ||
|
|
f6e12d1664 | ||
|
|
d022080b80 | ||
|
|
44acb1caac | ||
|
|
28347f1074 | ||
|
|
7826a1d0b4 | ||
|
|
975edb48df | ||
|
|
c2a5c278e9 | ||
|
|
f895e69aa9 | ||
|
|
7c050a9e10 | ||
|
|
38087d21d8 | ||
|
|
4b2e49c4aa | ||
|
|
e31c0a8112 | ||
|
|
876d6e0981 | ||
|
|
cb2dac6126 | ||
|
|
a3cc1c652f | ||
|
|
7ab6fd273e | ||
|
|
7d47361446 | ||
|
|
9a95b5ed34 | ||
|
|
0070c1f1cc | ||
|
|
cd4664a012 | ||
|
|
b1b8d79706 | ||
|
|
002832a87d | ||
|
|
958212ea0c | ||
|
|
0013d529da | ||
|
|
23f8d0bbbb | ||
|
|
3c72760a3f | ||
|
|
8844f60cd0 | ||
|
|
62e01e43e2 | ||
|
|
7953cc1209 | ||
|
|
67227320c9 | ||
|
|
5dd2c14fba | ||
|
|
dcf116fe82 | ||
|
|
9c571cc16d | ||
|
|
aa8e32ee43 | ||
|
|
4d0a793ecc | ||
|
|
659bf6b208 | ||
|
|
33312a4bf8 | ||
|
|
929a93b1f7 | ||
|
|
f49e41c5c0 | ||
|
|
87f99d42bf | ||
|
|
e4f5531855 | ||
|
|
ae2712c9ac | ||
|
|
d62bde8158 | ||
|
|
53ea5ddf81 | ||
|
|
9e0b95a1ad | ||
|
|
5325c372ba | ||
|
|
c3f4183531 | ||
|
|
6de5416394 | ||
|
|
1a947097a5 | ||
|
|
50abbd6aea | ||
|
|
f324504df1 | ||
|
|
931d5dad01 | ||
|
|
ef094b891a | ||
|
|
d25d91038d | ||
|
|
700f1d97fd | ||
|
|
5b1cf890e8 | ||
|
|
c254248886 | ||
|
|
5aaa5a3360 | ||
|
|
54d31c345a | ||
|
|
ae3e721f62 | ||
|
|
0833dce43d | ||
|
|
47b2e917c1 | ||
|
|
c2864d1715 | ||
|
|
2769de6f7e | ||
|
|
ef3b39361a | ||
|
|
fa2a2e2107 | ||
|
|
3005dfdc0f | ||
|
|
4fd8d9dc5b | ||
|
|
c948b981cf | ||
|
|
8e51b05941 | ||
|
|
75568e6c75 | ||
|
|
6e3c880ea2 | ||
|
|
ded957a0ef | ||
|
|
05741ceab0 | ||
|
|
9e3b3879d0 | ||
|
|
a1b189fcb1 | ||
|
|
7a48e5dd5c | ||
|
|
2842aea933 | ||
|
|
f97d156020 | ||
|
|
d8922be0ad | ||
|
|
5634ada367 | ||
|
|
94d249edbd | ||
|
|
1c29ac0ec5 | ||
|
|
e7e280bad8 | ||
|
|
7ca11c3eee | ||
|
|
caf6ac029c | ||
|
|
4c0fa81963 | ||
|
|
720c1595e0 | ||
|
|
bedd0051a9 | ||
|
|
09bed1a713 | ||
|
|
ebb53c5035 | ||
|
|
243b82a6d7 | ||
|
|
bd4f840d27 | ||
|
|
de893d2175 | ||
|
|
33658f13f6 | ||
|
|
469ee033b2 | ||
|
|
451fb67195 |
No files matched your search
@@ -25,3 +25,4 @@ 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/
|
||||
Vendored
+12
-1
@@ -42,7 +42,18 @@
|
||||
"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",
|
||||
|
||||
+1
-3
@@ -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} -DNDEBUG -O3")
|
||||
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -DNDEBUG -O3")
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/lib)
|
||||
add_subdirectory(src/web)
|
||||
@@ -54,8 +54,6 @@ 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)
|
||||
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
<?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>‎</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>
|
||||
@@ -0,0 +1,55 @@
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
Submodule docker/linux updated: aedfd79e02...8ee9692597.
+1
-1
Submodule externals updated: 83046b27ef...5f21fd13e7.
@@ -1,3 +1,6 @@
|
||||
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
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
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.
|
||||
|
||||
+55
-59
@@ -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", "{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{8530E0E8-3768-37EC-B1E1-8248C5A92FE3}"
|
||||
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", "{33E7E6C5-04E3-36A8-A751-639BE5E72B9B}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZERO_CHECK", "build\ZERO_CHECK.vcxproj", "{9AD1A867-93A9-301D-BF77-0113E18E9E39}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C} = {69D73CF1-9D7A-38CA-9817-0D0C3E3BCB3C}
|
||||
{8530E0E8-3768-37EC-B1E1-8248C5A92FE3} = {8530E0E8-3768-37EC-B1E1-8248C5A92FE3}
|
||||
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
|
||||
{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
|
||||
{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
|
||||
{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,30 +93,26 @@ 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|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
|
||||
{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
|
||||
{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
|
||||
@@ -137,22 +133,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
|
||||
{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
|
||||
{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
|
||||
{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
|
||||
|
||||
@@ -11,18 +11,22 @@ 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>();
|
||||
IDisposable _dispose;
|
||||
readonly BehaviorSubject<AdminModelWrapper> _wrapper_subject = new BehaviorSubject<AdminModelWrapper>(null);
|
||||
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
|
||||
@@ -52,9 +56,10 @@ 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) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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.AdminFrozen)}.");
|
||||
icon = Icons.AdminFrozen;
|
||||
Log.Api.warn($"{nameof(SecurityPickerDaemon)} received a message '{message.title}' with {icon_error_desc}, will use {nameof(Icons.Admin)}.");
|
||||
icon = Icons.Admin;
|
||||
}
|
||||
//
|
||||
var cpv = new ContractPickerView(vm);
|
||||
|
||||
@@ -16,11 +16,6 @@ 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/";
|
||||
@@ -32,40 +27,43 @@ 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));
|
||||
return new BitmapImage(uri(filename)).frozen();
|
||||
}
|
||||
|
||||
private static ImageSource load_resource_and_freeze(string resource_name) {
|
||||
return ((DrawingImage)_resources[resource_name]).frozen();
|
||||
}
|
||||
|
||||
#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 (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 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 SheetsRed { get { return load_image("SheetsRed.png"); } }
|
||||
public static ImageSource SheetsGreen { get { return load_image("SheetsGreen.png"); } }
|
||||
public static ImageSource Spreads { get { return (DrawingImage)_resources["Spreads"]; } }
|
||||
public static ImageSource Spreads { get { return load_resource_and_freeze("Spreads"); } }
|
||||
public static ImageSource BlueSpreads { get { return load_image("BlueSpread.ico"); } }
|
||||
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 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 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 => (DrawingImage)_resources["Browser"];
|
||||
public static ImageSource Browser => load_resource_and_freeze("Browser");
|
||||
|
||||
public static ImageSource Date { get { return load_image("Date.png"); } }
|
||||
|
||||
public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; } }
|
||||
public static ImageSource Cube3d { get { return load_resource_and_freeze("Cube"); } }
|
||||
public static ImageSource Algo { get { return Cube3d; } }
|
||||
public static ImageSource Quote { get { return Math; } }
|
||||
public static ImageSource Math { get { return (DrawingImage)_resources["Surface"]; } }
|
||||
public static ImageSource Math { get { return load_resource_and_freeze("Surface"); } }
|
||||
public static ImageSource Abacus { get { return load_image("Abacus.png"); } }
|
||||
|
||||
public static ImageSource Delete { get { return load_image("Delete.png"); } }
|
||||
@@ -77,58 +75,58 @@ namespace mbts.ui {
|
||||
#pragma warning restore CS0618
|
||||
|
||||
// Vector icons
|
||||
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 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 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 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 Orders => create_icon(OrdersGeometryKey);
|
||||
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 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 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 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 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 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 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 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 Draggable => create_icon(DraggableGeometryKey);
|
||||
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 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 Gear => create_icon(GearGeometryKey);
|
||||
public static ImageSource Import => (DrawingImage)_resources["Import"];
|
||||
public static ImageSource Knight { get { return (DrawingImage)_resources["Knight"]; } }
|
||||
public static ImageSource Import => load_resource_and_freeze("Import");
|
||||
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
|
||||
public static ImageSource Payment => create_icon(PaymentGeometryKey);
|
||||
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 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 Tools => create_icon(ToolsGeometryKey);
|
||||
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 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 create_icon(string geometric_path_key) =>
|
||||
create_icon(geometric_path_key, Brushes.Black);
|
||||
|
||||
@@ -5,9 +5,11 @@ 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;
|
||||
@@ -16,6 +18,7 @@ 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 {
|
||||
@@ -75,29 +78,16 @@ 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),
|
||||
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;
|
||||
})
|
||||
_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)
|
||||
);
|
||||
|
||||
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;
|
||||
@@ -139,52 +129,27 @@ namespace mbts.spark {
|
||||
|
||||
public void raise_invalidated() => invalidated?.Invoke();
|
||||
|
||||
/// <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>();
|
||||
public void refresh() {
|
||||
bool dirty = false;
|
||||
if (md.md.get_bbo(ref _bbo_ref, out NativePriceSize b, out NativePriceSize a)) {
|
||||
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);
|
||||
_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);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
_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,13 +3,10 @@ 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;
|
||||
@@ -17,7 +14,7 @@ using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.MarketViewView;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class MarketViewVM : SecurityMDGrid<MarketViewVM.Row, SecurityMD>, IPrepareUpdateForDispatcher {
|
||||
partial class MarketViewVM : SecurityMDGrid<MarketViewVM.Row, SecurityMD> {
|
||||
readonly IBus _bus;
|
||||
readonly OrdersModel _orders_model;
|
||||
|
||||
@@ -115,7 +112,8 @@ 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()
|
||||
securities_connectable.Connect(),
|
||||
WorkLoop.Start("MarketView", _refresh)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -161,25 +159,6 @@ 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); }
|
||||
@@ -188,4 +167,4 @@ namespace mbts.spark {
|
||||
foreach (var r in _row_vms) r.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,18 +14,16 @@ 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 _resources;
|
||||
IDisposable _subscriptions;
|
||||
|
||||
public MarketViewWindow(MarketViewWindowFactory factory, DispatcherUpdateMaster dispatcher_update_master, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state)
|
||||
public MarketViewWindow(MarketViewWindowFactory factory, 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();
|
||||
@@ -59,14 +57,12 @@ 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))
|
||||
};
|
||||
_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));
|
||||
_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())));
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
Disposable.dispose(ref _resources);
|
||||
Disposable.dispose(ref _subscriptions);
|
||||
_vm.Dispose();
|
||||
Commands.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ 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;
|
||||
@@ -26,7 +25,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, _dispatcher_update_master, _bus, _orders_model, state);
|
||||
IShellWindow _create(StartupData state) => new MarketViewWindow(this, _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 = trade.Time.lldatetime();
|
||||
time = ((ulong)trade.Time).lldatetime().ToLocalTime();
|
||||
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.AdminFrozen;
|
||||
public ImageSource icon => Icons.Admin;
|
||||
|
||||
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> {
|
||||
class OrderBookCellStore<T> : IOrderBookCellStore<T>, INotifyPropertyChanged {
|
||||
readonly OrderBookRowVM _row;
|
||||
readonly ICVOrderBookColumn _column;
|
||||
readonly string _prop_name;
|
||||
@@ -58,6 +58,10 @@ 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,31 +30,66 @@ namespace mbts.spark {
|
||||
string _symbol;
|
||||
OrderBookRowVM _parent;
|
||||
OrderBookVM.BranchFilterSource _branch_filter_source;
|
||||
IDisposable _branch_filter_sub;
|
||||
// Core
|
||||
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;
|
||||
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;
|
||||
// Def
|
||||
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;
|
||||
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;
|
||||
// State
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public OrderBookRowVM(IBus bus, OrderBookVM source_vm, OrdersModel model, OrderRecordImmutable record, SecurityMD? s) {
|
||||
_book_vm = source_vm;
|
||||
@@ -62,43 +97,7 @@ namespace mbts.spark {
|
||||
_order = record;
|
||||
_security_md = s;
|
||||
_symbol = _security_md.HasValue ? _security_md.Value.security.Symbol : null;
|
||||
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;
|
||||
}
|
||||
}
|
||||
//
|
||||
//animator = new PredicateAnimator<OrderBookRowVM,OrderRecordTuple>(this);
|
||||
_visible = backing(nameof(is_visible), true);
|
||||
_expanded = backing(nameof(is_expanded), false);
|
||||
_depth_level = backing(nameof(depth_level), () => {
|
||||
@@ -114,15 +113,11 @@ 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),
|
||||
Observable.FromEvent<Action<IObservable<bool>>, IObservable<bool>>(
|
||||
cb => _branch_filter_source_changed += cb, cb => _branch_filter_source_changed -= cb)
|
||||
.SelectMany(o => o));
|
||||
_passes_filter = backing(nameof(passes_filter), row_passes_filter);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -217,7 +212,31 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public ObservableCollection<RowAnimation> row_animations { get; } = new ObservableCollection<RowAnimation>();
|
||||
public IReadOnlyList<DIYGrid.IGridCellStore> cell_stores { get; }
|
||||
|
||||
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 static Stack<OrderBookRowVM> lineage(OrderBookRowVM node) {
|
||||
var rv = new Stack<OrderBookRowVM>();
|
||||
@@ -228,11 +247,15 @@ 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>
|
||||
@@ -311,7 +334,13 @@ 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) => Disposable.dispose(ref _branch_filter_sub);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerable<OrderBookRowVM> _descendents(OrderBookRowVM root) {
|
||||
foreach (var child in root.child_orders) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.api;
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
@@ -27,10 +28,7 @@ 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>();
|
||||
@@ -39,7 +37,8 @@ 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) {
|
||||
columns = new OrderBookColumnCollection(bus, model, _order_securities_subject);
|
||||
var order_securities_subject = new Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)>();
|
||||
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 += () => {
|
||||
@@ -71,11 +70,12 @@ namespace mbts.spark {
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(on_securities),
|
||||
model.on_orders.OnDispatcher().AddRemoveOnly().Subscribe(on_orders),
|
||||
model.on_orders.OnDispatcher().Subscribe(on_orders),
|
||||
grid_settings_manager,
|
||||
columns);
|
||||
columns,
|
||||
order_securities_subject);
|
||||
|
||||
void on_orders(LegacyTableUpdate<OrderRecordImmutable, uint> table) {
|
||||
void on_orders(TableUpdate<OrderRecordImmutable, uint> table) {
|
||||
// clear everything (most likely the server restarted)
|
||||
if (table.removed.Count() == _order_rows.Count && !table.added.Any()) {
|
||||
_selected_rows.Clear();
|
||||
@@ -86,18 +86,14 @@ 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 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)) {
|
||||
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)) {
|
||||
@@ -124,10 +120,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;
|
||||
@@ -170,7 +166,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))
|
||||
@@ -206,7 +202,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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +239,9 @@ 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) {
|
||||
@@ -297,24 +296,21 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
internal class BranchFilterSource : IObservable<bool> {
|
||||
internal class BranchFilterSource {
|
||||
readonly HashSet<uint> _orders_pass_filter = new HashSet<uint>();
|
||||
event Action<bool> _passes_filter_changed;
|
||||
|
||||
public event Action<bool> passes_filter_changed;
|
||||
|
||||
public bool passes_filter => _orders_pass_filter.Count > 0;
|
||||
|
||||
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,11 +15,9 @@ namespace mbts.spark {
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
public const double RowHeight = 17;
|
||||
OrderBookVM _vm;
|
||||
|
||||
public OrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
_vm = vm;
|
||||
DataContext = vm;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
@@ -12,60 +11,6 @@ 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;
|
||||
@@ -94,7 +39,7 @@ namespace mbts.spark {
|
||||
class OrderPropertiesEditorVM : DisposableViewModelBase {
|
||||
readonly OrdersModel _model;
|
||||
readonly Property<SecurityImmutable> _security;
|
||||
readonly SelectorProperty<AccountTuple, AccountTuple?> _accounts;
|
||||
readonly SelectorProperty<AccountTuple> _accounts;
|
||||
readonly SelectorProperty<TimeInForce> _time_in_force;
|
||||
readonly SelectorProperty<IOrderTypeVM> _order_type;
|
||||
IDisposable _order_type_param_errors_sub;
|
||||
@@ -113,21 +58,14 @@ namespace mbts.spark {
|
||||
nameof(selected_account),
|
||||
model.factory.subscribe_account_tuples(),
|
||||
order_properties.Select(ops => ops.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());
|
||||
() => accounts?.FirstOrDefault() ?? default,
|
||||
val => (!string.IsNullOrWhiteSpace(val.name) && accounts.Contains(val), "Please select an account"));
|
||||
_order_type = backing(
|
||||
nameof(selected_order_type),
|
||||
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))),
|
||||
security_source.Select(s => model.factory.default_order_type_defs(s).Select(create_order_type_vm)),
|
||||
order_properties.Select(get_order_type_vm_prop),
|
||||
get_default_order_type_vm,
|
||||
EqualityComparer.null_check_wrapper(OrderTypeVMExtensions.key_comparer));
|
||||
(a, b) => a.order_type == b.order_type);
|
||||
_order_type.Changed += on_selected_order_type_changed;
|
||||
_time_in_force = backing(
|
||||
nameof(selected_time_in_force),
|
||||
@@ -139,22 +77,19 @@ 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, string id, string display_name) {
|
||||
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) {
|
||||
IOrderTypeVM vm = null;
|
||||
if(cd.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic)
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source, cd.Dynamic.DynamicOrderType);
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source, cd.Dynamic.DynamicOrderType);
|
||||
else
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, id, display_name, model, security_md_source);
|
||||
vm = OrderTypeVMFactory.create(cd.CustomCase, 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.value.CustomCase.ToString(),
|
||||
OrderUtils.order_type_display_name(p.order_type_def.value.CustomCase)),
|
||||
p.order_type_def.force_override);
|
||||
return new InlineUserProperty<IOrderTypeVM>(create_order_type_vm(p.order_type_def.value), p.order_type_def.force_override);
|
||||
}
|
||||
IOrderTypeVM get_default_order_type_vm() {
|
||||
var ot = model.factory.default_order_type(security);
|
||||
@@ -179,13 +114,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(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
public InlineCoverOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
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);
|
||||
|
||||
var hedgetype = CustomOneofCase.Limit;
|
||||
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
|
||||
hedge_ticks = new InlineUserProperty<int>(1, false);
|
||||
}
|
||||
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<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<int> hedge_ticks;
|
||||
|
||||
@@ -42,25 +42,25 @@ namespace mbts.spark
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineCoverOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _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, string id, string display_name)
|
||||
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
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)
|
||||
new InlineCoverOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
|
||||
{ }
|
||||
|
||||
public CoverOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineCoverOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
IObservable<InlineCoverOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
|
||||
{
|
||||
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
|
||||
_select_hedge_order(p.selected_hedge_order_type.value);
|
||||
}
|
||||
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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, 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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, 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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
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.key.type :
|
||||
_inline_props.selected_hedge_order_type.value :
|
||||
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(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
public InlineDynamicOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
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);
|
||||
}
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> 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<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _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, string id, string display_name)
|
||||
public DynamicOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string dyn_type)
|
||||
: this(model, security_md_source, dyn_type, security_md_source.Select(smd =>
|
||||
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)
|
||||
new InlineDynamicOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
|
||||
{ }
|
||||
|
||||
public DynamicOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string dyn_type,
|
||||
IObservable<InlineDynamicOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
IObservable<InlineDynamicOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
_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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, 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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
@@ -208,4 +208,4 @@ namespace mbts.spark
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
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; }
|
||||
@@ -23,12 +21,4 @@ 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(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types) {
|
||||
public InlineIcebergOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
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);
|
||||
show_quantity = new InlineUserProperty<double>(1, true);
|
||||
}
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<double> show_quantity;
|
||||
}
|
||||
|
||||
class IcebergOrderVM : OrderTypeVMBase<IcebergOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineIcebergOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
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) { }
|
||||
new InlineIcebergOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public IcebergOrderVM(OrdersModel model,
|
||||
IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineIcebergOrderProperties> inline_properties, string id, string display_name)
|
||||
: base(model, id, display_name) {
|
||||
IObservable<InlineIcebergOrderProperties> inline_properties)
|
||||
: base(model) {
|
||||
_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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, 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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -17,19 +17,19 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineJoinOrderProperties
|
||||
{
|
||||
public InlineJoinOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
public InlineJoinOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
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);
|
||||
|
||||
var hedgetype = CustomOneofCase.Lean;
|
||||
if(!child_order_types.Any(t => t.key.type == hedgetype))
|
||||
if(!child_order_types.Contains(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<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(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<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<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<uint> tick_offset_on_deploy;
|
||||
@@ -55,13 +55,13 @@ namespace mbts.spark
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineJoinOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _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, string id, string display_name)
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
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) { }
|
||||
new InlineJoinOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineJoinOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
IObservable<InlineJoinOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
|
||||
{
|
||||
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
|
||||
_select_hedge_order(p.selected_hedge_order_type.value);
|
||||
}
|
||||
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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, 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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
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.key.type :
|
||||
_inline_props.selected_hedge_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_hedge_order(ot);
|
||||
}
|
||||
|
||||
@@ -17,41 +17,44 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineLeanOrderProperties
|
||||
{
|
||||
public InlineLeanOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
public InlineLeanOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
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);
|
||||
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(10, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(2, false);
|
||||
trade_toward_behavior = new InlineUserProperty<LeanOrderDefinition.Types.OnToward>(LeanOrderDefinition.Types.OnToward.Cancel, false);
|
||||
}
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> 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<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _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; } = 10;
|
||||
public uint max_chase_ticks { get; set; } = 2;
|
||||
public LeanOrderDefinition.Types.OnToward trade_toward_behavior { get; set; } = LeanOrderDefinition.Types.OnToward.Cancel;
|
||||
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, string id, string display_name)
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
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) { }
|
||||
new InlineLeanOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineLeanOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
IObservable<InlineLeanOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
@@ -65,7 +68,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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (p.cross_threshold.force_override)
|
||||
{
|
||||
@@ -77,6 +80,11 @@ 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();
|
||||
})
|
||||
@@ -94,17 +102,20 @@ 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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -150,7 +161,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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
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, "Limit", OrderUtils.order_type_display_name(CustomOrderDefinition.CustomOneofCase.Limit)) { }
|
||||
internal LimitOrderVM(OrdersModel model) : base(model) { }
|
||||
|
||||
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, "Market", OrderUtils.order_type_display_name(CustomOrderDefinition.CustomOneofCase.Market)) { }
|
||||
internal MarketOrderVM(OrdersModel model) : base(model) { }
|
||||
|
||||
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, 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 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 create(CustomOneofCase order_type, string id, string display_name, OrdersModel model,
|
||||
public static IOrderTypeVM create(CustomOneofCase order_type, 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, 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);
|
||||
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);
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -51,17 +51,12 @@ namespace mbts.spark {
|
||||
protected readonly OrdersModel model;
|
||||
protected OrderFactory factory => model.factory;
|
||||
|
||||
protected OrderTypeVMBase(OrdersModel model, string id, string display_name) {
|
||||
this.model = model;
|
||||
this.id = id;
|
||||
this.display_name = display_name;
|
||||
}
|
||||
protected OrderTypeVMBase(OrdersModel model) => this.model = model;
|
||||
|
||||
public abstract CustomOneofCase order_type { get; }
|
||||
public abstract string type_name { get; }
|
||||
public bool is_bts_type => display_name.StartsWith("BTS");
|
||||
public string id { get; }
|
||||
public string display_name { get; }
|
||||
public string display_name => OrderUtils.order_type_display_name(order_type);
|
||||
public OrderMetadata meta => factory.get_metadata(order_type);
|
||||
public virtual bool has_editable_properties => !meta.NoProps;
|
||||
public virtual bool editable_properties_open { get; set; }
|
||||
@@ -81,7 +76,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
private abstract class OrderTypeVMBase<T> : OrderTypeVMBase where T : Google.Protobuf.IMessage, new() {
|
||||
protected OrderTypeVMBase(OrdersModel model, string id, string display_name) : base(model, id, display_name) { }
|
||||
protected OrderTypeVMBase(OrdersModel model) : base(model) { }
|
||||
|
||||
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, string id, string display_name)
|
||||
public SpreaderOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: 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, string id, string display_name)
|
||||
: base(model, id, display_name) {
|
||||
IObservable<InlineUserProperty<SpreaderOrderDefinitionImmutable>> order_template_source)
|
||||
: base(model) {
|
||||
_template = backing(nameof(template), new SpreaderOrderDefinitionImmutable?());
|
||||
_strategy_editor_wrapper = new StrategyEditorWrapper(this);
|
||||
_dispose = Disposable.Create(
|
||||
|
||||
@@ -29,11 +29,10 @@ namespace mbts.spark {
|
||||
InlineStopLimitOrderProperties _inline_props;
|
||||
IDisposable _dispose;
|
||||
|
||||
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<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source)) { }
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties, string id, string display_name)
|
||||
: base(model, id, display_name) {
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties) : base(model) {
|
||||
_stop_price = new UserProperty<double>(backing(nameof(stop_price), double.NaN));
|
||||
_dispose = Disposable.Create(
|
||||
spec_source.OnDispatcher().Subscribe(s => {
|
||||
|
||||
@@ -6,7 +6,8 @@ namespace mbts.spark {
|
||||
private class StopOrderVM : OrderTypeVMBase<StopOrderDefinition> {
|
||||
double _stop_price = 0;
|
||||
|
||||
internal StopOrderVM(OrdersModel model, string id, string display_name) : base(model, id, display_name) { }
|
||||
internal StopOrderVM(OrdersModel model) : base(model) { }
|
||||
|
||||
|
||||
public double stop_price {
|
||||
get => _stop_price;
|
||||
|
||||
@@ -17,11 +17,11 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineTickOrderProperties
|
||||
{
|
||||
public InlineTickOrderProperties (OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
public InlineTickOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
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);
|
||||
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<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> 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<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _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, string id, string display_name)
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
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) { }
|
||||
new InlineTickOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTickOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
IObservable<InlineTickOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, 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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
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(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types) {
|
||||
public InlineTimeBoxOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
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);
|
||||
start = new InlineUserProperty<DateTime>(DateTime.Now, true);
|
||||
stop = new InlineUserProperty<DateTime>(DateTime.Now.AddMinutes(5), true);
|
||||
}
|
||||
public readonly InlineUserProperty<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> 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<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _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, string id, string display_name)
|
||||
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
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) { }
|
||||
new InlineTimeBoxOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TimeBoxOrderVM(OrdersModel model,
|
||||
IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTimeBoxOrderProperties> inline_properties, string id, string display_name)
|
||||
: base(model, id, display_name) {
|
||||
IObservable<InlineTimeBoxOrderProperties> inline_properties)
|
||||
: base(model) {
|
||||
_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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, 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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@ namespace mbts.spark
|
||||
{
|
||||
class InlineTopOrderProperties
|
||||
{
|
||||
public InlineTopOrderProperties(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types)
|
||||
public InlineTopOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
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);
|
||||
|
||||
var hedgetype = CustomOneofCase.Lean;
|
||||
if(!child_order_types.Any(ct => ct.key.type == hedgetype))
|
||||
if(!child_order_types.Contains(hedgetype))
|
||||
hedgetype = CustomOneofCase.Limit;
|
||||
this.selected_hedge_order_type = new InlineUserProperty<OrderTemplateHeader>(new OrderTemplateHeader(hedgetype), true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(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<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<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<uint> watch_level_tick_offset;
|
||||
@@ -53,13 +53,13 @@ namespace mbts.spark
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineTopOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _hedge_order_choices;
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _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, string id, string display_name)
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
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)
|
||||
new InlineTopOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
|
||||
{ }
|
||||
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTopOrderProperties> inline_properties, string id, string display_name) : base(model, id, display_name)
|
||||
IObservable<InlineTopOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
|
||||
{
|
||||
_select_hedge_order(p.selected_hedge_order_type.value.key.type);
|
||||
_select_hedge_order(p.selected_hedge_order_type.value);
|
||||
}
|
||||
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, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, 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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
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.key.type :
|
||||
_inline_props.selected_hedge_order_type.value :
|
||||
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<OrderTemplateHeader> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<TriggerPriceType> trigger_price_type;
|
||||
public readonly InlineUserProperty<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(OrderTemplateHeader selected_child_order_type, IReadOnlyCollection<OrderTemplateHeader> child_order_types) {
|
||||
public InlineTriggerOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types) {
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<OrderTemplateHeader>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<OrderTemplateHeader>>(child_order_types, true);
|
||||
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
trigger_price_type = new InlineUserProperty<TriggerPriceType>(TriggerPriceType.Bid, false);
|
||||
payup_price_type = new InlineUserProperty<TriggerPayupPrice.Types.TriggerPayupPriceType>();
|
||||
use_fixed_price = new InlineUserProperty<bool>(false);
|
||||
@@ -47,20 +47,19 @@ namespace mbts.spark {
|
||||
readonly UserProperty<bool> _use_fixed_price, _is_timeout_checked, _is_priceout_checked;
|
||||
SecurityMD? _security_md;
|
||||
SecurityImmutable? _security => _security_md.security();
|
||||
ObservableCollection<OrderTemplateHeader> _order_types = new ObservableCollection<OrderTemplateHeader>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader> _child_order_choices;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineTriggerOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
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, string id, string display_name)
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
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) { }
|
||||
new InlineTriggerOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineTriggerOrderProperties> inline_properties, string id, string display_name)
|
||||
: base(model, id, display_name) {
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineTriggerOrderProperties> inline_properties) : base(model) {
|
||||
_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);
|
||||
@@ -89,7 +88,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.key.type);
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
// trigger price
|
||||
_trigger_price_type.on_inline_prop(p.trigger_price_type);
|
||||
@@ -136,7 +135,7 @@ namespace mbts.spark {
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, OrderTemplateHeader>(_order_types, ot => create(ot.key.type, ot.key.name, ot.display_name, model, _security_md_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -211,13 +210,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 ulong v);
|
||||
_security_md.Value.md.get_last_trade(ref _last_trade_ref, out co2.NativePriceSize ps, out DateTime ts, 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.key.type :
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
private OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, SpreaderModel spreader, ErrorReporter error, AdminModel admin_model) {
|
||||
_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).Table().Cache();
|
||||
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).Table().Cache();
|
||||
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();
|
||||
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).Table().Cache();
|
||||
on_fills = _service.subscribei_fills().Naggle(interval, scheduler).PermissionFilter(admin_model).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());
|
||||
|
||||
@@ -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.AdminFrozen, SparkTags.ShowSettings);
|
||||
SparkCommand.create("Settings", Icons.Admin, 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.AdminFrozen, SparkTags.ShowAndEditPickMultiple, get_param);
|
||||
SparkCommand.create("Settings", Icons.Admin, SparkTags.ShowAndEditPickMultiple, get_param);
|
||||
|
||||
public static PartialCommand<SecurityCriteria> change_securities_callback(string window_name) =>
|
||||
SparkCommand.create(window_name, Icons.AdminFrozen, new SparkTags.SparkTag<SecurityCriteria>($"Change {window_name} securities callback"));
|
||||
SparkCommand.create(window_name, Icons.Admin, new SparkTags.SparkTag<SecurityCriteria>($"Change {window_name} securities callback"));
|
||||
|
||||
public static SparkCommand window_settings_command(IDialogCommand launch_settings_editor) =>
|
||||
new SparkCommandWrapper("Settings", Icons.AdminFrozen, launch_settings_editor);
|
||||
new SparkCommandWrapper("Settings", Icons.Admin, launch_settings_editor);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using mbts.collections.extensions;
|
||||
using System.Collections;
|
||||
using bts.collections;
|
||||
|
||||
namespace mbts.ui.utility
|
||||
{
|
||||
|
||||
@@ -666,44 +666,6 @@ 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,6 +29,7 @@ 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 {
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
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,15 +66,73 @@ namespace mbts.utils {
|
||||
|
||||
class AnonymousConnectable<TResult> : IConnectableObservable<TResult> {
|
||||
readonly IObservable<TSource> _source;
|
||||
readonly KeyDrivenObservableSeries<TSource, TResult> _driver;
|
||||
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;
|
||||
|
||||
public AnonymousConnectable(IObservable<TSource> source, Func<TSource, IObservable<TResult>> select_next) {
|
||||
_source = source;
|
||||
_driver = new KeyDrivenObservableSeries<TSource, TResult>(select_next);
|
||||
_select_next = select_next;
|
||||
}
|
||||
|
||||
public IDisposable Connect() => _source.Subscribe(_driver);
|
||||
public IDisposable Subscribe(IObserver<TResult> observer) => _driver.Subscribe(observer);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,13 +156,15 @@ namespace mbts.ui.shell
|
||||
public string name => _propname;
|
||||
|
||||
public void set(T value) {
|
||||
if (!Equals(value, _value))
|
||||
_apply_new_value(value);
|
||||
if (!Equals(value, _value)) {
|
||||
Changing?.Invoke(_value, value);
|
||||
var old = _value;
|
||||
_value = value;
|
||||
Changed?.Invoke(old, value);
|
||||
_owner.RaisePropertyChanged(_propname);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -180,14 +182,6 @@ 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> {
|
||||
@@ -452,9 +446,6 @@ 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) {
|
||||
@@ -535,7 +526,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, EqualityComparer<T>.Default, validation_steps);
|
||||
backing(name, items, desired_item, select_default, null, validation_steps);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a SelectorProperty<T> that observes an items source, and a desired item. When either observable ticks, it determines whether or not to assign the selected_item property.
|
||||
@@ -547,8 +538,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, IEqualityComparer<T> comparer) =>
|
||||
backing(name, items, desired_item, select_default, comparer, 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, Func<T, T, bool> items_match) =>
|
||||
backing(name, items, desired_item, select_default, items_match, new List<PropertyValidation<T>>(0));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a SelectorProperty<T> that observes an items source, and a desired item. When either observable ticks, it determines whether or not to assign the selected_item property.
|
||||
@@ -561,22 +552,12 @@ 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, 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) {
|
||||
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>();
|
||||
_observable_properties.Add(items.OnDispatcher().Subscribe(selector_property));
|
||||
_observable_properties.Add(desired_item.OnDispatcher().Subscribe(selector_property));
|
||||
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));
|
||||
return prop;
|
||||
}
|
||||
|
||||
protected void set_dispose(IDisposable disposable) {
|
||||
@@ -595,44 +576,37 @@ 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<TItem, TValue> : IObserver<InlineUserProperty<TItem>>, IObserver<IEnumerable<TItem>>, IObservable<(TValue oldval, TValue newval)> {
|
||||
protected class SelectorProperty<T> : IObserver<InlineUserProperty<T>>, IObserver<IEnumerable<T>>, IObservable<(T oldval, T newval)> {
|
||||
readonly DisposableViewModelBase _owner;
|
||||
readonly string _prop_name;
|
||||
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>();
|
||||
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;
|
||||
bool _is_user_selected_item;
|
||||
|
||||
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) {
|
||||
public SelectorProperty(DisposableViewModelBase owner, string prop_name, Func<T> select_default, Func<T, T, bool> items_match, IReadOnlyList<PropertyValidation<T>> validation_steps) {
|
||||
_owner = owner;
|
||||
_prop_name = prop_name;
|
||||
_item_to_value = item_to_value;
|
||||
_select_default = select_default;
|
||||
_value_comparer = value_comparer ?? EqualityComparer<TValue>.Default;
|
||||
_items_match = items_match;
|
||||
_validation_steps = validation_steps;
|
||||
items = new ReadOnlyObservableCollection<TItem>(_items);
|
||||
items = new ReadOnlyObservableCollection<T>(_items);
|
||||
}
|
||||
|
||||
public event PropertyChange<TValue> Changing;
|
||||
public event PropertyChange<TValue> Changed;
|
||||
public event PropertyChange<T> Changing;
|
||||
public event PropertyChange<T> Changed;
|
||||
|
||||
public ReadOnlyObservableCollection<TItem> items { get; }
|
||||
public ReadOnlyObservableCollection<T> items { get; }
|
||||
|
||||
public TValue selected_item {
|
||||
public T selected_item {
|
||||
get => _selected_item;
|
||||
set {
|
||||
_is_user_selected_item = true;
|
||||
@@ -640,31 +614,31 @@ namespace mbts.ui.shell
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<(TValue oldval, TValue newval)> observer) {
|
||||
public IDisposable Subscribe(IObserver<(T oldval, T newval)> observer) {
|
||||
Changed += pch;
|
||||
return Disposable.Create(() => Changed -= pch);
|
||||
void pch(TValue o, TValue n) => observer.OnNext((o, n));
|
||||
void pch(T o, T n) => observer.OnNext((o, n));
|
||||
}
|
||||
|
||||
public IObservable<TValue> observe_current(bool start_now = false) {
|
||||
public IObservable<T> 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<TItem>>.OnNext(InlineUserProperty<TItem> iup) {
|
||||
void IObserver<InlineUserProperty<T>>.OnNext(InlineUserProperty<T> iup) {
|
||||
_desired_item = iup;
|
||||
_try_auto_select();
|
||||
}
|
||||
void IObserver<InlineUserProperty<TItem>>.OnError(Exception error) { }
|
||||
void IObserver<InlineUserProperty<TItem>>.OnCompleted() { }
|
||||
void IObserver<InlineUserProperty<T>>.OnError(Exception error) { }
|
||||
void IObserver<InlineUserProperty<T>>.OnCompleted() { }
|
||||
|
||||
void IObserver<IEnumerable<TItem>>.OnNext(IEnumerable<TItem> value) {
|
||||
if (_is_user_selected_item) {
|
||||
void IObserver<IEnumerable<T>>.OnNext(IEnumerable<T> value) {
|
||||
if (_items_match != null && _is_user_selected_item) {
|
||||
var curr_selected_item = selected_item;
|
||||
_items.sync_with(value);
|
||||
if (_try_match_item(curr_selected_item, out TValue match))
|
||||
if (_try_match_item(curr_selected_item, out T match))
|
||||
selected_item = match;
|
||||
else {
|
||||
_is_user_selected_item = false;
|
||||
@@ -672,13 +646,18 @@ namespace mbts.ui.shell
|
||||
}
|
||||
} else {
|
||||
_items.sync_with(value);
|
||||
_try_auto_select();
|
||||
if (_is_user_selected_item) {
|
||||
_is_user_selected_item = _items.Contains(_selected_item);
|
||||
}
|
||||
if (!_is_user_selected_item) {
|
||||
_try_auto_select();
|
||||
}
|
||||
}
|
||||
}
|
||||
void IObserver<IEnumerable<TItem>>.OnError(Exception error) { }
|
||||
void IObserver<IEnumerable<TItem>>.OnCompleted() { }
|
||||
void IObserver<IEnumerable<T>>.OnError(Exception error) { }
|
||||
void IObserver<IEnumerable<T>>.OnCompleted() { }
|
||||
|
||||
void _run_validation(TValue val) {
|
||||
void _run_validation(T 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));
|
||||
@@ -688,15 +667,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 TValue match)) {
|
||||
if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out T match)) {
|
||||
_set_selected_item(match);
|
||||
return;
|
||||
}
|
||||
} else if (desired_item_valid() && _try_match_item(_desired_item.value, out TValue match)) {
|
||||
} else if (desired_item_valid() && _try_match_item(_desired_item.value, out T match)) {
|
||||
_set_selected_item(match);
|
||||
return;
|
||||
}
|
||||
if (_try_match_item(_select_default(), out TValue default_match))
|
||||
if (_try_match_item(_select_default(), out T default_match))
|
||||
_set_selected_item(default_match);
|
||||
else {
|
||||
// the _set_selected_item has never been entered, so validation has never ran, force it here
|
||||
@@ -706,8 +685,8 @@ namespace mbts.ui.shell
|
||||
bool desired_item_valid() => (_desired_item?.has_value).GetValueOrDefault();
|
||||
}
|
||||
|
||||
void _set_selected_item(TValue value) {
|
||||
if (!_value_comparer.Equals(value, _selected_item)) {
|
||||
void _set_selected_item(T value) {
|
||||
if (!Equals(value, _selected_item)) {
|
||||
Changing?.Invoke(_selected_item, value);
|
||||
var old = _selected_item;
|
||||
_selected_item = value;
|
||||
@@ -717,20 +696,10 @@ namespace mbts.ui.shell
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
public event Action<bool> OnDisposed;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace mbts.spark {
|
||||
value = initial_value;
|
||||
}
|
||||
|
||||
public event Action<(T oldval, T newval)> value_set;
|
||||
event Action<(T oldval, T newval)> _value_set;
|
||||
|
||||
public T value { get; private set; }
|
||||
public string text => _text ?? (_text = _format(value));
|
||||
@@ -27,22 +27,14 @@ 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;
|
||||
@@ -50,7 +42,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.OfType<ISelectionHighlightListener>())
|
||||
foreach (var col in o.Columns.empty_if_null().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()) {
|
||||
if (!o._visible_columns.is_null_or_empty() && o.Columns != null) {
|
||||
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}" DisplayName="{Binding display_name}" Metadata="{Binding meta}"/>
|
||||
<usp:OrderTypeItem OrderType="{Binding order_type}" Metadata="{Binding meta}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
@@ -222,6 +222,8 @@
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -233,9 +235,11 @@
|
||||
<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">Child</TextBlock>
|
||||
<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>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="5" Grid.Column="2"
|
||||
Grid.Row="7" 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 x:Name="text"
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Margin="20,0,0,0"
|
||||
|
||||
@@ -3,49 +3,23 @@ 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>
|
||||
partial class OrderTypeItem : Control {
|
||||
static OrderTypeItem() =>
|
||||
public 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,6 +1,7 @@
|
||||
using bts.api;
|
||||
using bts.spark;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -14,9 +15,10 @@ namespace mbts.ui.controls {
|
||||
SetCurrentValue(FocusableProperty, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SparkMenu(IEnumerable<SparkCommand> commands, IBus bus) : this() =>
|
||||
ItemsSource = commands.Select(c => new SparkCommandVM(c, bus)).ToList();
|
||||
// ObservableCollection to avoid WPF Binding Leak
|
||||
ItemsSource = new ObservableCollection<SparkCommandVM>(commands.Select(c => new SparkCommandVM(c, bus)));
|
||||
|
||||
public bool ShowText {
|
||||
get { return (bool)GetValue(ShowTextProperty); }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
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,6 +26,8 @@
|
||||
<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,6 +154,7 @@ 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,6 +6,7 @@ using System.Windows.Interop;
|
||||
using System.Runtime.InteropServices;
|
||||
using mbts.ui.native;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace mbts.ui.shell
|
||||
{
|
||||
@@ -82,7 +83,14 @@ namespace mbts.ui.shell
|
||||
return _hwnd;
|
||||
}
|
||||
|
||||
protected override void DestroyWindowCore(HandleRef 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();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError=true)]
|
||||
static extern IntPtr SetParent(HandleRef hwndChild, HandleRef hwndParent);
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using System.Windows.Interop;
|
||||
|
||||
namespace mbts.ui.shell
|
||||
{
|
||||
@@ -19,9 +20,8 @@ namespace mbts.ui.shell
|
||||
class UIThreadData : IDisposable {
|
||||
public Dispatcher dispatcher;
|
||||
public Thread thread;
|
||||
public List<ISlowWindow> windows = new List<ISlowWindow>();
|
||||
public System.Windows.Interop.MSG last_msg;
|
||||
public void Dispose() { lock(windows) Disposable.dispose(windows); }
|
||||
public MSG last_msg;
|
||||
public void Dispose() {}
|
||||
}
|
||||
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;
|
||||
System.Windows.Interop.ComponentDispatcher.ThreadFilterMessage += (ref System.Windows.Interop.MSG m,ref bool h) => ComponentDispatcher_ThreadFilterMessage(uid, ref m, ref h);
|
||||
ComponentDispatcher.ThreadFilterMessage += (ref 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 System.Windows.Interop.MSG msg, ref bool handled) {
|
||||
private void ComponentDispatcher_ThreadFilterMessage(UIThreadData d, ref MSG msg, ref bool handled) {
|
||||
d.last_msg = msg;
|
||||
|
||||
// We definitely don't need to reload themes on background threads
|
||||
@@ -75,7 +75,6 @@ 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 += () => { if (!HasCustomTitle) RaisePropertyChanged("Title"); };
|
||||
_model.IconChanged += () => { _icon = null; RaisePropertyChanged("Icon"); };
|
||||
_model.TitleChanged += title_changed;
|
||||
_model.IconChanged += icon_changed;
|
||||
_wm = wm;
|
||||
host_id = host;
|
||||
|
||||
@@ -46,6 +46,16 @@ 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; } }
|
||||
|
||||
@@ -208,6 +218,9 @@ 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));
|
||||
@@ -221,6 +234,7 @@ namespace mbts.ui.shell
|
||||
Disposable.dispose(ref _model);
|
||||
}
|
||||
|
||||
_ui = null;
|
||||
Disposable.dispose(ref _hwnd_host);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,13 @@ 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;
|
||||
@@ -92,11 +94,15 @@ namespace mbts.ui.shell {
|
||||
public IReadOnlyList<TabDocument> list_documents() => _documents.Values.ToList();
|
||||
|
||||
public async Task add(IShellWindow window) {
|
||||
if (!Application.Current.Dispatcher.CheckAccess())
|
||||
await Application.Current.Dispatcher.InvokeAsync(() => add(window));
|
||||
Log.Ui.info($"Opening window {window.diagnostic_name()}.");
|
||||
lock (_lock) {
|
||||
_documents[window] = _create_document(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
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,16 +222,15 @@ namespace mbts.ui.shell
|
||||
}
|
||||
}
|
||||
if (def.windows != null) {
|
||||
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);
|
||||
async Task restore(WindowState window) {
|
||||
var w = await _window_restore.restore(window);
|
||||
if (!_closing) {
|
||||
await _wm.add(w);
|
||||
}
|
||||
}
|
||||
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,6 +73,10 @@ 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 }
|
||||
@@ -386,7 +390,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 ulong __);
|
||||
bool last_trade_changed = _vm.md.get_last_trade(ref unused, out _last_trade, out DateTime _, 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 ?? string.Empty).Subscribe(account_positions),
|
||||
order_properties_vm.observe_account(true).Select(a => a.name).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 ulong volume);
|
||||
md.get_last_trade(ref unused, out NativePriceSize last, out DateTime time, 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 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 DateTime unused1, 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,6 +16,7 @@ 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 {
|
||||
@@ -36,7 +37,8 @@ namespace bts.view {
|
||||
_menu.ShowText = false;
|
||||
_grid.Children.Add(_menu);
|
||||
if (ContextMenu != null) {
|
||||
ContextMenu.ItemsSource = commands.Select(c => new SparkCommandVM(c, bus));
|
||||
// ObservableCollection to avoid WPF Binding Leak
|
||||
ContextMenu.ItemsSource = new ObservableCollection<SparkCommandVM>(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, ulong volume)> last_trade { get; }
|
||||
IObservable<(double last, uint last_size, DateTime time_stamp, ulong volume)> last_trade { get; }
|
||||
IObservable<double> bid_price { get; }
|
||||
IObservable<uint> bid_size { get; }
|
||||
IObservable<double> ask_price { get; }
|
||||
@@ -61,6 +61,7 @@ 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)) {
|
||||
@@ -74,9 +75,10 @@ namespace mbts.spark.utilities
|
||||
}
|
||||
|
||||
private bool _refresh_last(MDConsumerHandle md) {
|
||||
if (md.get_last_trade(ref _last_ref, out NativePriceSize ps, out ulong v)) {
|
||||
if (md.get_last_trade(ref _last_ref, out NativePriceSize ps, out DateTime ts, out ulong v)) {
|
||||
_last_price = ps.price;
|
||||
_last_size = ps.size;
|
||||
_last_time = ts;
|
||||
_volume = v;
|
||||
return true;
|
||||
}
|
||||
@@ -84,16 +86,17 @@ namespace mbts.spark.utilities
|
||||
}
|
||||
|
||||
private (double, uint, double, uint) _select_bbo() { return (_bid_price, _bid_size, _ask_price, _ask_size); }
|
||||
private (double, uint, ulong) _select_last() { return (_last_price, _last_size, _volume); }
|
||||
private (double, uint, DateTime, ulong) _select_last() { return (_last_price, _last_size, _last_time, _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, ulong volume)> last_trade => _last_pump.Select(_ => _select_last()).StartWith(_select_last());
|
||||
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> 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,29 +110,6 @@ 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();
|
||||
|
||||
@@ -235,7 +212,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;
|
||||
public static readonly TimeSpan default_interval;
|
||||
public static readonly DispatcherPriority default_priority;
|
||||
static readonly DispatcherPriority default_priority;
|
||||
static readonly TimeSpan default_interval;
|
||||
|
||||
static WorkLoop() {
|
||||
var timer_params = config.Configuration.get<bts.config.LadderKnobs>();
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -129,6 +129,7 @@ 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() {
|
||||
@@ -347,5 +348,72 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,7 @@ namespace bts {
|
||||
Mercury,
|
||||
Testing,
|
||||
Latency,
|
||||
DataFlow,
|
||||
Plugin
|
||||
DataFlow
|
||||
// Please keep this list in sync with logging.h/logging.cpp
|
||||
}
|
||||
readonly struct Logger {
|
||||
|
||||
@@ -113,5 +113,7 @@ 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);
|
||||
}
|
||||
}
|
||||
@@ -72,12 +72,7 @@ namespace mbts.co2
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
public bool get_last_trade(ref ulong time, out NativePriceSize ps, out DateTime 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));
|
||||
@@ -93,15 +88,15 @@ namespace mbts.co2
|
||||
public ulong total_volume { get { return _cold->total_volume; } }
|
||||
|
||||
public double settlement_price { get { return _cold->settlement_price;} }
|
||||
public utils.LLDate settlement_date { get { return _cold->settlement_date;} }
|
||||
public DateTime settlement_date { get { return _cold->settlement_date;} }
|
||||
public double previous_settlement_price { get { return _cold->previous_settlement_price;} }
|
||||
public utils.LLDate previous_settlement_date { get { return _cold->previous_settlement_date;} }
|
||||
public DateTime previous_settlement_date { get { return _cold->previous_settlement_date;} }
|
||||
|
||||
public double open_price { get { return _cold->open_price;} }
|
||||
public utils.LLDate open_date { get { return _cold->open_date; } }
|
||||
public DateTime open_date { get { return _cold->open_date; } }
|
||||
|
||||
public double close_price { get { return _cold->close_price;} }
|
||||
public utils.LLDate close_date { get { return _cold->close_date; } }
|
||||
public DateTime 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; } }
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
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
|
||||
bool Insert(T item) {
|
||||
public 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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
void Remove(T item) {
|
||||
public 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.
|
||||
void AddAll(IEnumerable<T> items) {
|
||||
public 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,6 +103,27 @@ 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();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.test {
|
||||
public static class ConsoleEx {
|
||||
@@ -21,17 +23,34 @@ namespace bts.test {
|
||||
}
|
||||
|
||||
public static T prompt<T>(string prompt, params T[] options) {
|
||||
Console.WriteLine(prompt);
|
||||
for (int x = 1; x <= options.Length; ++x) {
|
||||
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) {
|
||||
Write($"{x}. ", ConsoleColor.Yellow);
|
||||
WriteLine(options[x].ToString());
|
||||
WriteLine(options.ElementAt(x).ToString());
|
||||
}
|
||||
int response;
|
||||
do {
|
||||
Int32.TryParse(Console.ReadKey().ToString(), out response);
|
||||
if (response > options.Length) response = 0;
|
||||
} while (response == 0);
|
||||
return options[response];
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ 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,9 +27,6 @@ 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,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace mbts {
|
||||
@@ -24,20 +23,14 @@ 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) {
|
||||
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;
|
||||
// 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));
|
||||
}
|
||||
|
||||
public static DateTime lldatetime(this long ll) { return ((ulong)ll).lldatetime(); }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ 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());
|
||||
|
||||
@@ -56,6 +56,11 @@ 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;
|
||||
|
||||
@@ -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.Now));
|
||||
Assert.IsTrue(compare_date(entry.last_trade_time, DateTime.UtcNow));
|
||||
//// Settlement
|
||||
Assert.AreEqual(entry.settlement_price, 3.3);
|
||||
Assert.IsTrue(entry.settlement_date == new DateTime(2015, 9, 3));
|
||||
@@ -210,24 +210,19 @@ namespace mbts.co2.tests
|
||||
Assert.IsTrue(ask.size == 202);
|
||||
|
||||
time = 0;
|
||||
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;
|
||||
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(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);
|
||||
// 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_date.Year == 2000 && handle.settlement_date.Day == 9 && handle.settlement_date.Month == 8);
|
||||
Assert.IsTrue(handle.settlement_price == 6.6);
|
||||
Assert.IsTrue(handle.open_price == 7.7);
|
||||
// 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.open_date.Year == 2001 && handle.open_date.Month == 1 && handle.open_date.Day == 2);
|
||||
Assert.IsTrue(handle.close_price == 8.8);
|
||||
// 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.close_date.Year == 2002 && handle.close_date.Month == 3 && handle.close_date.Day == 4);
|
||||
|
||||
Assert.IsTrue(handle.session_high == 9.9);
|
||||
Assert.IsTrue(handle.session_low == 10.1);
|
||||
@@ -256,6 +251,25 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-3
@@ -1,6 +1,5 @@
|
||||
using bts.collections;
|
||||
using mbts.collections.extensions;
|
||||
using NUnit.Framework;
|
||||
using bts.test;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@@ -9,7 +8,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.tests {
|
||||
[TestFixture]
|
||||
[TestGroup]
|
||||
class SortedTreeTests {
|
||||
|
||||
class Order {
|
||||
@@ -92,5 +91,30 @@ 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' });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace mbts.api.Test {
|
||||
[TestGroup]
|
||||
@@ -104,6 +105,121 @@ 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
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,5 +13,8 @@ namespace sparkcmd {
|
||||
ConsoleCommandDispatcher.DispatchCommand(commands, args, Console.Out);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,7 @@
|
||||
<Authors>Blue Trading Systems</Authors>
|
||||
<Company>Blue Trading Systems</Company>
|
||||
<Product>Spark</Product>
|
||||
<Platform>x64</Platform>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>x64</Platforms>
|
||||
<Version>1.0.0</Version>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1 +1 @@
|
||||
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)
|
||||
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)
|
||||
@@ -4,7 +4,7 @@ project(data_files)
|
||||
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
|
||||
|
||||
# DEPENDENCIES
|
||||
file (GLOB_RECURSE To_Copy ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR} files/*)
|
||||
file (GLOB_RECURSE To_Copy RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} files/*)
|
||||
update_deps_file("${To_Copy}")
|
||||
# END DEPENDENCIES
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,63 @@
|
||||
<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> </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>
|
||||
@@ -0,0 +1,2 @@
|
||||
/*! 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
|
||||
@@ -0,0 +1,164 @@
|
||||
$( 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
Reference in new issue
Block a user