Compare commits
118
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd5c680155 | ||
|
|
5a1cc4cf2b | ||
|
|
9be3a09749 | ||
|
|
61745d0ed7 | ||
|
|
db1610591e | ||
|
|
bb1571540a | ||
|
|
342d89724e | ||
|
|
07d3903d11 | ||
|
|
d08b134d7f | ||
|
|
92f67d1d2d | ||
|
|
43a18941a4 | ||
|
|
b102b271b0 | ||
|
|
b129bea129 | ||
|
|
38d79eb58b | ||
|
|
dfd0cfc217 | ||
|
|
a123ac2ed8 | ||
|
|
ad4fd13189 | ||
|
|
8a1180835c | ||
|
|
f65f34c2fe | ||
|
|
34401960f9 | ||
|
|
34b7c99c81 | ||
|
|
044ef0b812 | ||
|
|
9cc4b5aa87 | ||
|
|
821274f544 | ||
|
|
ca6c0f984c | ||
|
|
80e337c599 | ||
|
|
5b71918a1f | ||
|
|
d2a9d21700 | ||
|
|
16fee6e478 | ||
|
|
3cf701a211 | ||
|
|
5392d05ec8 | ||
|
|
7838bdf04f | ||
|
|
546553406b | ||
|
|
02d0f6191c | ||
|
|
36722e27ad | ||
|
|
dcfbb6e62f | ||
|
|
ff2d150dc4 | ||
|
|
6692797c3f | ||
|
|
b059aedbce | ||
|
|
73c9be3bec | ||
|
|
bc35e773ce | ||
|
|
4c8d0a4217 | ||
|
|
05abbc4a50 | ||
|
|
95500187f0 | ||
|
|
8716099ac6 | ||
|
|
d7850d6b70 | ||
|
|
905c1cab2c | ||
|
|
6181548902 | ||
|
|
c32eada3ac | ||
|
|
42feedfd36 | ||
|
|
d76c2a30ce | ||
|
|
c312a880a7 | ||
|
|
b2ed88fb4a | ||
|
|
567edafe18 | ||
|
|
9a39d84739 | ||
|
|
d9b4e21c61 | ||
|
|
fa287b14ef | ||
|
|
096bf3338c | ||
|
|
a646f28831 | ||
|
|
391397692c | ||
|
|
8130d5a0f8 | ||
|
|
d89f93c323 | ||
|
|
a376fe9e22 | ||
|
|
7381088d6d | ||
|
|
5f9a421490 | ||
|
|
f7fd81f3af | ||
|
|
f6c0f393bd | ||
|
|
adec1bdfd0 | ||
|
|
a3de83dd04 | ||
|
|
5ba7cd561c | ||
|
|
5ff9f13e7e | ||
|
|
f4b65b21bb | ||
|
|
c1789bb4a1 | ||
|
|
0cf4b5c2c8 | ||
|
|
a101a267ed | ||
|
|
d76b098c51 | ||
|
|
06aa540110 | ||
|
|
9fd7db55ca | ||
|
|
5b118d8fa0 | ||
|
|
b570762054 | ||
|
|
b664c42cae | ||
|
|
a25c17306f | ||
|
|
b0c52f542a | ||
|
|
dabeb63eac | ||
|
|
f0d3cabd14 | ||
|
|
418a1078c2 | ||
|
|
3d37dc481f | ||
|
|
d24b7a3707 | ||
|
|
12362d9719 | ||
|
|
01128639ce | ||
|
|
71d2e01187 | ||
|
|
c923be6ced | ||
|
|
4cf9d544aa | ||
|
|
79f37f7130 | ||
|
|
19c0b94803 | ||
|
|
57fc50ce61 | ||
|
|
3cff5343a3 | ||
|
|
a6ec93e87e | ||
|
|
25acf771ab | ||
|
|
22cc946f52 | ||
|
|
6d0eb62712 | ||
|
|
369b89b3a7 | ||
|
|
8793c6c527 | ||
|
|
df14522433 | ||
|
|
086e90acbe | ||
|
|
467dba98f8 | ||
|
|
f513f7d9f2 | ||
|
|
69f9f2791e | ||
|
|
ad6fc6d23b | ||
|
|
a4575e3fa3 | ||
|
|
dc1a80afd2 | ||
|
|
e1cfa7bf0b | ||
|
|
2020221e65 | ||
|
|
ce438e93a6 | ||
|
|
bad5bc3097 | ||
|
|
e9da338402 | ||
|
|
be6c916691 | ||
|
|
4d60376cb1 |
No files matched your search
Vendored
+1
-1
@@ -215,7 +215,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "/home/bts/spark/build/ninja_debug/server/sparkd",
|
||||
"args": ["test", "[md]"],
|
||||
"args": ["test", "[mbo]"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "/home/bts/spark/build/ninja_debug/server/",
|
||||
"environment": [],
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
+++
|
||||
title = "BTS Basic Algos"
|
||||
weight = 10
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
|
||||
|
||||
## Lean
|
||||
|
||||
The goal of the lean order is to watch the opposite-inside market size and cross when that size goes below the threshold (this also implies a cross if that size goes to zero and the market trades away from you) If the market trades toward you, you can opt into several different behaviors
|
||||
|
||||
##### Parameters
|
||||
* Cross Thresh - size thresh in absolute or ratio terms
|
||||
* Max Chase Ticks - max levels to pay up, in ticks
|
||||
|
||||
|
||||
## Join
|
||||
The goal of the join order is to watch the opposite-inside market and join when the market flips (that is, when the best offer becomes the best bid) and has at least threshold size.
|
||||
|
||||
##### Parameters
|
||||
* Join Thresh - size thresh in absolute or ratio terms
|
||||
|
||||
|
||||
## Top
|
||||
|
||||
The goal of the top order is to not trade by yourself, but the intent at the time of order entry is to get filled.If the order is on the market (say current bid price) AND the quantity on that bid price is below top thresh then cancel the order else remain working.
|
||||
|
||||
##### Parameters
|
||||
* Top Thresh - size thresh in absolute or ratio terms
|
||||
|
||||
|
||||
## Cover
|
||||
A Cover order’s purpose is to fire a hedge order (of any type) in response to fills from it’s child order on the opposite side of the market N ticks away
|
||||
|
||||
##### Parameters
|
||||
* Profit Ticks - ticks above or below fill price to place profit / cover order
|
||||
|
||||
|
||||
## OCO
|
||||
An OCO order creates a relationship between a child order and a 'stop' or 'other' order such that when one of these is cancelled, both cancel, or when one is filled, the other order's size is debited in the amount of the fill.
|
||||
|
||||
Composing this order with Cover simulates scalping functionality
|
||||
|
||||
##### Parameters
|
||||
* Stop Ticks - ticks below or above main order to place stop / other order
|
||||
@@ -1,6 +0,0 @@
|
||||
+++
|
||||
title = "Client"
|
||||
weight = 1
|
||||
sort_by = "weight"
|
||||
redirect_to = "bts_spark_sdk/Client/overview/"
|
||||
+++
|
||||
@@ -1,6 +0,0 @@
|
||||
+++
|
||||
title = "Server"
|
||||
weight = 2
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
@@ -1,6 +0,0 @@
|
||||
+++
|
||||
title = "BTS Spark SDK"
|
||||
weight = 2
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
@@ -4,6 +4,23 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 1.6.0
|
||||
### User Visible Changes
|
||||
#### New Features
|
||||
- You can now view your order's position in queue in the ladder
|
||||
#### Bug Fixes
|
||||
- Fix a bug in the order popup in the ladder which could lead to a crash.
|
||||
|
||||
## 1.5.9
|
||||
### User Visible Changes
|
||||
- Fix for stunnel connections on Windows 7
|
||||
|
||||
## 1.5.8
|
||||
### User Visible Changes
|
||||
- Fixed a bug where it was possible to crash the UI by entering an order through the order ticket if Enter was used to dismiss the dialog.
|
||||
- Fixed a spreader reporting bug which could cause the server to crash if an algo was used to control certain types of synthetics
|
||||
- Improved user message when the UI fails to login
|
||||
- Integrated stunnel into UI to make VPN setup unnecessary in unsecured environments.
|
||||
## 1.5.7
|
||||
### User Visible Changes
|
||||
- New videos have been added to the manual describing the [Algo Studio](./bts_spark/Trading/index.md#algo-studio) and [Spreader](./bts_spark/Spreader/index.md)
|
||||
|
||||
+1
-1
Submodule externals updated: d0e8645b5e...67a79263ba.
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -72,13 +74,71 @@ namespace mbts.ui.shell
|
||||
base.OnExit(e);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
void start_stunnel(string path, uint job_error)
|
||||
{
|
||||
string config_error;
|
||||
if (!mbts.config.Configuration.try_initialize(out config_error))
|
||||
{
|
||||
_stunnel_process = Process.Start(path + "bin\\stunnel.exe");
|
||||
if (_stunnel_process != null)
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (mbts.config.Configuration.try_initialize(out config_error))
|
||||
break;
|
||||
System.Threading.Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
else
|
||||
stunnel_job_error = "Stunnel Job Error code: " + job_error +
|
||||
" could not start stunnel " + (config_error ?? string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
void kill_stunnel()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process[] ps = Process.GetProcessesByName("stunnel");
|
||||
ps.ForEach(p =>
|
||||
{
|
||||
try { p.Kill(); }
|
||||
catch {}
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
Process _stunnel_process;
|
||||
static internal string stunnel_job_error { get; private set;}
|
||||
void init_stunnel(string[] argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\BTS\\stunnel\\";
|
||||
if (File.Exists(path + "config\\stunnel.conf"))
|
||||
{
|
||||
uint job_error = BTSUINativeMethods.setup_process_job_object();
|
||||
//if we can't put this process and stunnel into a job, try to stop stunnel then restart
|
||||
if(job_error != 0)
|
||||
kill_stunnel();
|
||||
start_stunnel(path, job_error);
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
stunnel_job_error = "Error starting stunnel: " + ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
public new int Run() {
|
||||
var argv = Environment.GetCommandLineArgs();
|
||||
try {
|
||||
if (argv.Contains("-multiple") ||
|
||||
_mutex == null ||
|
||||
_mutex.WaitOne(TimeSpan.Zero, true)) {
|
||||
try {
|
||||
if (argv.Contains("-multiple") || _mutex == null || _mutex.WaitOne(TimeSpan.Zero, true))
|
||||
{
|
||||
if(_mutex == null || _mutex.WaitOne(TimeSpan.Zero, true))
|
||||
init_stunnel(argv);
|
||||
return base.Run();
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -6,6 +6,7 @@ using bts.api.persistence;
|
||||
using bts.api.position;
|
||||
using bts.api.synthetic;
|
||||
using bts.co2;
|
||||
using bts.md;
|
||||
using bts.metrics;
|
||||
using bts.support;
|
||||
using System.ComponentModel.Composition;
|
||||
@@ -50,6 +51,9 @@ namespace Spark {
|
||||
AlgoServiceWrapper _algo_service_wrapper;
|
||||
[Export]
|
||||
public AlgoServiceWrapper algo_service_wrapper => _algo_service_wrapper ?? (_algo_service_wrapper = new AlgoServiceWrapper(context));
|
||||
|
||||
[Export]
|
||||
public MBOService mbo_service => context.locate<MBOService>();
|
||||
}
|
||||
|
||||
class AlgoServiceWrapper {
|
||||
|
||||
@@ -22,9 +22,7 @@ namespace mbts.ui {
|
||||
AddBoxesGeometryKey = nameof(AddBoxesGeometryKey),
|
||||
AddFileGeometryKey = nameof(AddFileGeometryKey),
|
||||
AddFileThinGeometryKey = nameof(AddFileThinGeometryKey),
|
||||
AttachAlgosGeometryKey = nameof(AttachAlgosGeometryKey),
|
||||
CheckMarkGeometryKey = nameof(CheckMarkGeometryKey),
|
||||
DetachAlgosGeometryKey = nameof(DetachAlgosGeometryKey),
|
||||
DiskGeometryKey = nameof(DiskGeometryKey),
|
||||
DiskFileGeometryKey = nameof(DiskFileGeometryKey),
|
||||
DocumentGeometryKey = nameof(DocumentGeometryKey),
|
||||
@@ -41,7 +39,6 @@ namespace mbts.ui {
|
||||
PaymentGeometryKey = nameof(PaymentGeometryKey),
|
||||
PencilGeometryKey = nameof(PencilGeometryKey),
|
||||
PlayGeometryKey = nameof(PlayGeometryKey),
|
||||
ProhibitedGeometryKey = nameof(ProhibitedGeometryKey),
|
||||
StarGeometryKey = nameof(StarGeometryKey),
|
||||
ToolsGeometryKey = nameof(ToolsGeometryKey),
|
||||
TrashGeometryKey = nameof(TrashGeometryKey),
|
||||
@@ -118,7 +115,6 @@ namespace mbts.ui {
|
||||
public static ImageSource AddBoxes => create_icon(AddBoxesGeometryKey);
|
||||
public static ImageSource AddEncircled { get { return load_resource_and_freeze("AddEncircled"); } }
|
||||
public static ImageSource AddFile => create_icon(Smashicons.AddFileGeometryKey);
|
||||
public static ImageSource AttachAlgos => create_icon(AttachAlgosGeometryKey);
|
||||
public static ImageSource Beaker { get { return load_resource_and_freeze("Beaker"); } }
|
||||
public static ImageSource Book { get { return load_resource_and_freeze("Book"); } }
|
||||
|
||||
@@ -143,7 +139,6 @@ namespace mbts.ui {
|
||||
public static ImageSource CheckMark => create_icon(CheckMarkGeometryKey);
|
||||
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 DetachAlgos => create_icon(DetachAlgosGeometryKey);
|
||||
public static ImageSource Disk => create_icon(DiskGeometryKey);
|
||||
public static ImageSource DiskFile => create_icon(DiskFileGeometryKey);
|
||||
public static ImageSource Display { get { return load_resource_and_freeze("Display"); } }
|
||||
@@ -163,7 +158,6 @@ namespace mbts.ui {
|
||||
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
|
||||
public static ImageSource Payment => create_icon(PaymentGeometryKey);
|
||||
public static ImageSource Pencil => create_icon(PencilGeometryKey);
|
||||
public static ImageSource Prohibited => create_icon(ProhibitedGeometryKey);
|
||||
public static ImageSource RedoCircular => create_icon(UndoCircularGeometryKey, Brushes.Black, null, new ScaleTransform(-1, 1));
|
||||
public static ImageSource Spawn { get { return load_resource_and_freeze("Spawn"); } }
|
||||
public static ImageSource Star => create_icon(StarGeometryKey);
|
||||
|
||||
@@ -40,69 +40,6 @@
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<!--File (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/freepik
|
||||
Ttile: Unlink free icon
|
||||
Credit: Icon made by Chanut from www.flaticon.com-->
|
||||
<x:Array x:Key="{x:Static mbts:Icons.AttachAlgosGeometryKey}" Type="sys:String">
|
||||
<sys:String>
|
||||
M51.173,3.801c-5.068-5.068-13.315-5.066-18.384,0l-9.192,9.192c-0.781,0.781-0.781,2.047,0,2.828
|
||||
c0.781,0.781,2.047,0.781,2.828,0l9.192-9.192c1.691-1.69,3.951-2.622,6.363-2.622c2.413,0,4.673,0.932,6.364,2.623
|
||||
s2.623,3.951,2.623,6.364c0,2.412-0.932,4.672-2.623,6.363L36.325,31.379c-3.51,3.508-9.219,3.508-12.729,0
|
||||
c-0.781-0.781-2.047-0.781-2.828,0s-0.781,2.048,0,2.828c2.534,2.534,5.863,3.801,9.192,3.801s6.658-1.267,9.192-3.801
|
||||
l12.021-12.021c2.447-2.446,3.795-5.711,3.795-9.192C54.968,9.512,53.62,6.248,51.173,3.801z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
M27.132,40.57l-7.778,7.778c-1.691,1.691-3.951,2.623-6.364,2.623c-2.412,0-4.673-0.932-6.364-2.623
|
||||
c-3.509-3.509-3.509-9.219,0-12.728L17.94,24.306c1.691-1.69,3.951-2.622,6.364-2.622c2.412,0,4.672,0.932,6.363,2.622
|
||||
c0.781,0.781,2.047,0.781,2.828,0s0.781-2.047,0-2.828c-5.067-5.067-13.314-5.068-18.384,0L3.797,32.793
|
||||
c-2.446,2.446-3.794,5.711-3.794,9.192c0,3.48,1.348,6.745,3.795,9.191c2.446,2.447,5.711,3.795,9.191,3.795
|
||||
c3.481,0,6.746-1.348,9.192-3.795l7.778-7.778c0.781-0.781,0.781-2.047,0-2.828S27.913,39.789,27.132,40.57z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<!--File (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/freepik
|
||||
Ttile: Disconnected chains free icon
|
||||
Credit: Icon made by Chanut from www.flaticon.com-->
|
||||
<x:Array x:Key="{x:Static mbts:Icons.DetachAlgosGeometryKey}" Type="sys:String">
|
||||
<sys:String>
|
||||
M462.557,77.627c-14.323-25.556-41.488-41.423-70.889-41.423c-13.835,0-27.54,3.585-39.602,10.364
|
||||
l-152.557,85.635l17.55,31.279l152.565-85.635c6.747-3.78,14.371-5.779,22.069-5.779c16.428,0,31.596,8.852,39.594,23.11
|
||||
c5.909,10.535,7.356,22.752,4.072,34.408c-3.268,11.656-10.876,21.346-21.411,27.255l-138.291,77.604
|
||||
c-2.634-16.851-10.494-32.392-22.793-44.699c-21.687-21.663-56.063-29.328-84.935-18.964l-12.485,4.495l12.128,33.734
|
||||
l12.534-4.471c16.127-5.869,35.319-1.496,47.406,10.567c6.974,6.982,11.388,15.826,12.786,25.41
|
||||
c-0.585-0.138-1.171-0.285-1.731-0.447c-11.681-3.284-21.354-10.9-27.271-21.476l-6.519-11.518l-31.303,17.59l6.56,11.518
|
||||
c10.6,18.915,27.93,32.571,48.836,38.44c0.723,0.187,1.455,0.39,2.187,0.561c-1.105,1.398-2.284,2.731-3.536,3.983
|
||||
l-114.32,114.328c-8.551,8.543-19.931,13.241-32.035,13.241c-12.112,0-23.492-4.698-32.027-13.241
|
||||
c-8.535-8.543-13.233-19.907-13.233-32.027c0-12.112,4.698-23.483,13.233-32.018l99.291-99.282l-25.369-25.369l-99.282,99.282
|
||||
C8.446,319.427,0,339.806,0,361.468c0,21.679,8.446,42.049,23.776,57.396c15.339,15.339,35.709,23.768,57.388,23.768
|
||||
s42.049-8.429,57.388-23.768l114.345-114.32c8.372-8.405,14.81-18.566,18.842-29.71c6.625-1.764,12.924-4.3,18.777-7.6
|
||||
l140.99-79.115c18.875-10.608,32.514-27.946,38.375-48.828C475.749,118.4,473.148,96.493,462.557,77.627z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
M173.171,141.769c4.503-0.821,7.511-5.145,6.69-9.657l-17.103-94.942
|
||||
c-0.821-4.519-5.162-7.527-9.665-6.722c-4.511,0.821-7.527,5.145-6.706,9.673l17.111,94.925
|
||||
C164.311,139.575,168.644,142.574,173.171,141.769z
|
||||
</sys:String>
|
||||
<sys:String>
|
||||
M43.561,191.776l94.934-17.119c4.519-0.821,7.527-5.145,6.706-9.657
|
||||
c-0.821-4.528-5.145-7.543-9.649-6.722L40.61,175.405c-4.503,0.797-7.519,5.145-6.698,9.657
|
||||
C34.717,189.582,39.058,192.589,43.561,191.776z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<!--File (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/chanut
|
||||
Ttile: Cancel free icon
|
||||
Credit: Icon made by Chanut from www.flaticon.com-->
|
||||
<sys:String x:Key="{x:Static mbts:Icons.ProhibitedGeometryKey}">
|
||||
M32.759,0C14.696,0,0,14.695,0,32.759s14.695,32.759,32.759,32.759s32.759-14.695,32.759-32.759S50.822,0,32.759,0z
|
||||
M6,32.759C6,18.004,18.004,6,32.759,6c6.648,0,12.734,2.443,17.419,6.472L12.472,50.178C8.443,45.493,6,39.407,6,32.759z
|
||||
M32.759,59.518c-5.948,0-11.447-1.953-15.895-5.248l37.405-37.405c3.295,4.448,5.248,9.947,5.248,15.895
|
||||
C59.518,47.514,47.514,59.518,32.759,59.518z
|
||||
</sys:String>
|
||||
|
||||
<!--File (the icon is free to use if we include this information in our credit section)
|
||||
Source: https://www.flaticon.com/authors/smashicons
|
||||
Ttile: File free icon
|
||||
|
||||
@@ -16,7 +16,7 @@ using System.Windows;
|
||||
|
||||
namespace mbts.spark {
|
||||
class MarketViewWindow : SparkWindow<MarketViewWindowFactory.StartupData>, ISharedWindow {
|
||||
const string _window_type = "MarketView", _window_type_display_name = "Market View";
|
||||
const string _window_type = "MarketView", _thread_id = "MarketView", _window_type_display_name = "Market View";
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly AllowOnce _init_guard = new AllowOnce("MarketView");
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
@@ -32,7 +32,6 @@ namespace mbts.spark {
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
Icon.Freeze();
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().market_view ? "MarketView" : null;
|
||||
}
|
||||
|
||||
event Action<MarketViewWindowFactory.StartupData> _state_changed;
|
||||
@@ -48,7 +47,7 @@ namespace mbts.spark {
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public string thread_id { get; }
|
||||
public string thread_id => _thread_id;
|
||||
|
||||
public void thread_init() {
|
||||
_init_guard.check_access();
|
||||
|
||||
@@ -19,7 +19,7 @@ using static bts.shell.TimeAndSalesWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
|
||||
namespace mbts.spark {
|
||||
class TimeAndSalesWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
class TimeAndSalesWindow : SparkWindow<StartupData>, ISlowWindow {
|
||||
const string _window_type = "TimeAndSales", _window_type_display_name = "Time And Sales";
|
||||
readonly MDConsumer _consumer;
|
||||
readonly TimeAndSalesWindowFactory _factory;
|
||||
@@ -48,7 +48,7 @@ namespace mbts.spark {
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
public string thread_id { get; }
|
||||
|
||||
public override StartupData SaveState => _state = _vm.value.create();
|
||||
|
||||
public override void Dispose() =>
|
||||
|
||||
@@ -16,7 +16,7 @@ using System.Windows;
|
||||
using static bts.shell.SyntheticManagementWindowFactory;
|
||||
|
||||
namespace mbts.spark {
|
||||
class SyntheticManagementWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
class SyntheticManagementWindow : SparkWindow<StartupData>, ISlowWindow {
|
||||
const string _window_type = "SyntheticManagement";
|
||||
SingleValuePump<SyntheticManagementDialogVM> _vm = new SingleValuePump<SyntheticManagementDialogVM>();
|
||||
StartupData _state;
|
||||
@@ -33,20 +33,8 @@ namespace mbts.spark {
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
Icon.Freeze();
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().synthetic_management ? "SyntheticManagement" : null;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override StartupData SaveState => _state = _vm.value.create();
|
||||
public override double MinWidth =>
|
||||
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
public string thread_id { get; }
|
||||
|
||||
public void thread_init() {
|
||||
Log.notnull(_state);
|
||||
|
||||
@@ -113,6 +101,16 @@ namespace mbts.spark {
|
||||
else _view.DataContext = _vm.value;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override StartupData SaveState => _state = _vm.value.create();
|
||||
public override double MinWidth =>
|
||||
StartupData.DefaultGridSettings.columns.Sum(gcs => gcs.width)
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public override void Dispose() {
|
||||
Disposable.dispose(ref _vm);
|
||||
Commands.OfType<IDisposable>().DisposeAll();
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoTemplateTreeView.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoPickerNodeView.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoContentTemplateSelector.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!--Primitive Constants-->
|
||||
<sys:Double x:Key="HorizontalPush1">4</sys:Double>
|
||||
@@ -557,14 +559,71 @@
|
||||
</Grid>
|
||||
</RadioButton>
|
||||
</Grid>
|
||||
<usp:AlgoEditorView
|
||||
<GroupBox
|
||||
x:Name="content_host"
|
||||
Grid.Row="2"
|
||||
DataContext="{Binding content_source}"
|
||||
Background="Transparent"
|
||||
BorderBrush="{Binding algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
|
||||
BorderBrush="{Binding content_source.algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
|
||||
Margin="0,6,0,0"
|
||||
Padding="12,8"
|
||||
/>
|
||||
>
|
||||
<GroupBox.Style>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="BorderBrush" Value="Transparent">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Trigger>
|
||||
<Trigger Property="usp:AlgoStudioView.IsMouseOverNodeContent" Value="True">
|
||||
<Setter Property="BorderThickness" Value="2"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</GroupBox.Style>
|
||||
<GroupBox.Header>
|
||||
<ComboBox
|
||||
Grid.Column="3"
|
||||
ToolTipService.ShowOnDisabled="True"
|
||||
SelectedItem="{Binding selected_definition.algo_root}"
|
||||
ItemsSource="{Binding selected_definition.algos_view}"
|
||||
Margin="{StaticResource Margin.LP1}"
|
||||
Width="140"
|
||||
>
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding content_source}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding selected_definition.templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:True}">
|
||||
<Setter Property="IsEnabled" Value="True"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding selected_definition.templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:False}">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
<Setter Property="ToolTip" Value="Cannot change structure while templates are present."/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.Style>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<usp:OrderTypeItem DisplayName="{Binding display_name}" BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</GroupBox.Header>
|
||||
<ContentControl
|
||||
Grid.IsSharedSizeScope="True"
|
||||
Content="{Binding selected_definition.algo_root}"
|
||||
ContentTemplateSelector="{StaticResource AlgoTemplateSelector}"
|
||||
Margin="{StaticResource Margin.TP1}"
|
||||
>
|
||||
<ContentControl.Resources>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}"/>
|
||||
</ContentControl.Resources>
|
||||
</ContentControl>
|
||||
</GroupBox>
|
||||
<c:DialogSkirt Grid.Row="3" Margin="{StaticResource Margin.TP2}" Background="Transparent">
|
||||
<c:DialogSkirt.RightItems>
|
||||
<x:ArrayExtension Type="{x:Type Button}">
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using mbts.spark;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
@@ -16,11 +18,26 @@ namespace mbts.ui.spark {
|
||||
public AlgoStudioView() {
|
||||
InitializeComponent();
|
||||
DataContextChanged += (s, e) => set_controller();
|
||||
content_host.PreviewMouseMove += content_host_PreviewMouseMove;
|
||||
content_host.MouseEnter += content_host_MouseEnter;
|
||||
content_host.MouseLeave += content_host_MouseLeave;
|
||||
set_controller();
|
||||
// local methods
|
||||
void set_controller() => Disposable.replace(ref _controller, AlgoStudioViewControllerFactory.create(DataContext, _tree_view, name_text_box, custom_badge_color_picker_button, custom_badge_color_picker_popup));
|
||||
void content_host_PreviewMouseMove(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void content_host_MouseEnter(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void content_host_MouseLeave(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void refresh_is_mouse_over_content_host(MouseEventArgs e) => content_host.set_current_if_changed(IsMouseOverNodeContentProperty, this.ChildAtPoint<GroupBox>(e.GetPosition(this)) == content_host);
|
||||
}
|
||||
|
||||
#region IsMouseOverNodeContent
|
||||
public static readonly DependencyProperty IsMouseOverNodeContentProperty =
|
||||
DependencyProperty.RegisterAttached("IsMouseOverNodeContent", typeof(bool), typeof(AlgoStudioView), new PropertyMetadata(false));
|
||||
|
||||
public static bool GetIsMouseOverNodeContent(DependencyObject d) => (bool)d.GetValue(IsMouseOverNodeContentProperty);
|
||||
public static void SetIsMouseOverNodeContent(DependencyObject d, bool value) => d.SetValue(IsMouseOverNodeContentProperty, value);
|
||||
#endregion
|
||||
|
||||
public new AlgoStudioVM DataContext {
|
||||
get => base.DataContext as AlgoStudioVM;
|
||||
set => base.DataContext = value;
|
||||
|
||||
@@ -13,7 +13,7 @@ using System.Windows;
|
||||
using StartupData = bts.shell.AlgoStudioWindowFactory.StartupData;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal class AlgoStudioWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
internal class AlgoStudioWindow : SparkWindow<StartupData>, IShellWindow {
|
||||
public const string
|
||||
NAME = "Algo Studio",
|
||||
TemplatesEnabledKey = nameof(TemplatesEnabledKey),
|
||||
@@ -37,7 +37,7 @@ namespace mbts.spark {
|
||||
: base(NAME, bus, factory, SparkCommands.ShowAlgoStudio.icon) {
|
||||
_model = model;
|
||||
_state = state;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().algo_studio ? "AlgoStudio" : null;
|
||||
thread_init();
|
||||
}
|
||||
|
||||
public override StartupData SaveState => _state = _vm.create();
|
||||
@@ -45,12 +45,12 @@ namespace mbts.spark {
|
||||
public override string window_type => NAME;
|
||||
public override UIElement Content => _view;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public string thread_id => NAME;
|
||||
public override double MinWidth =>
|
||||
SPARK_MENU_COLLAPSED_WIDTH +
|
||||
LAUNCHER_BUTTON_BORDER_WIDTH +
|
||||
DEFAULT_CONTENT_WIDTH;
|
||||
public override double MinHeight => DEFAULT_CONTENT_HEIGHT;
|
||||
public string thread_id { get; }
|
||||
|
||||
bts.config.AlgoStudioOptions _ui_configuration => _model.algo_model.ui_configuration;
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.utility;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
@@ -11,6 +10,12 @@ namespace mbts.spark {
|
||||
public readonly string name;
|
||||
public readonly AlgoStructureKey structure;
|
||||
|
||||
public AlgoNameStructureKey(CustomOrderDefinitionImmutable definition)
|
||||
: this(definition.CustomCase.type_id(), definition) { }
|
||||
|
||||
public AlgoNameStructureKey(string name, CustomOrderDefinitionImmutable definition)
|
||||
: this(name, new AlgoStructureKey(definition)) { }
|
||||
|
||||
public AlgoNameStructureKey(CustomOrderDefinition.CustomOneofCase base_type)
|
||||
: this(base_type.type_id(), new AlgoStructureKey(base_type)) { }
|
||||
|
||||
@@ -26,22 +31,31 @@ namespace mbts.spark {
|
||||
public static bool operator !=(AlgoNameStructureKey a, AlgoNameStructureKey b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
struct AlgoStructureKey : IEquatable<AlgoStructureKey> {
|
||||
[ProtoMember(1)]
|
||||
readonly int[] _structure;
|
||||
|
||||
public AlgoStructureKey(CustomOrderDefinition.CustomOneofCase base_type)
|
||||
: this(new[] { (int)base_type }) { }
|
||||
|
||||
public AlgoStructureKey(CustomOrderDefinitionImmutable definition, IMaskIndexInt32<string> dynamic_type_index) =>
|
||||
_structure = AlgoStructureSerializer.to_array(definition, dynamic_type_index);
|
||||
public AlgoStructureKey(CustomOrderDefinitionImmutable definition) {
|
||||
int[] s = new[] { (int)definition.CustomCase };
|
||||
_build_structure(definition, ref s, 1);
|
||||
_structure = s;
|
||||
}
|
||||
|
||||
public AlgoStructureKey(int[] structure) =>
|
||||
_structure = structure;
|
||||
|
||||
public IReadOnlyList<int> structure => _structure ?? Array.Empty<int>();
|
||||
public CustomOrderDefinitionImmutable build_def() {
|
||||
int pos = 0;
|
||||
var mdef = new CustomOrderDefinition();
|
||||
_build_def(_structure, ref pos, ref mdef);
|
||||
return new CustomOrderDefinitionImmutable(mdef);
|
||||
}
|
||||
|
||||
public bool Equals(AlgoStructureKey other) =>
|
||||
/// <see cref="Enumerable.SequenceEqual{TSource}(IEnumerable{TSource}, IEnumerable{TSource})"/> is not optimized to check Count/Length, since the args are <see cref="IEnumerable{T}"/>.
|
||||
_structure.Length == other._structure.Length &&
|
||||
_structure.SequenceEqual(other._structure);
|
||||
|
||||
@@ -53,105 +67,35 @@ namespace mbts.spark {
|
||||
|
||||
public static bool operator ==(AlgoStructureKey a, AlgoStructureKey b) => a.Equals(b);
|
||||
public static bool operator !=(AlgoStructureKey a, AlgoStructureKey b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
static class AlgoStructureSerializer {
|
||||
const int
|
||||
_dynamic_mask = 1 << 31,
|
||||
_child_mask_offset = 30;
|
||||
static void _build_structure(CustomOrderDefinitionImmutable definition, ref int[] structure, int pos) =>
|
||||
_build_structure(definition.CloneToMutable(), ref structure, pos);
|
||||
|
||||
public static CustomOrderDefinitionImmutable build_algoi(this AlgoStructureKey structure_key, DynamicOrderHelper dynamic_helper) =>
|
||||
parsei(structure_key.structure, dynamic_helper);
|
||||
|
||||
public static CustomOrderDefinitionImmutable build_algo(this AlgoStructureKey structure_key, DynamicOrderHelper dynamic_helper) =>
|
||||
parsei(structure_key.structure, dynamic_helper);
|
||||
|
||||
public static AlgoStructureKey structure_key(CustomOrderDefinitionImmutable algo, IMaskIndexInt32<string> dynamic_type_index) =>
|
||||
new AlgoStructureKey(to_array(algo, dynamic_type_index));
|
||||
|
||||
public static AlgoStructureKey structure_key(CustomOrderDefinition algo, IMaskIndexInt32<string> dynamic_type_index) =>
|
||||
new AlgoStructureKey(to_array(algo, dynamic_type_index));
|
||||
|
||||
public static int[] to_array(CustomOrderDefinitionImmutable algo, IMaskIndexInt32<string> dynamic_type_index) =>
|
||||
to_array(algo.CloneToMutable(), dynamic_type_index);
|
||||
|
||||
public static int[] to_array(CustomOrderDefinition algo, IMaskIndexInt32<string> dynamic_type_index) {
|
||||
int[] s = new[] { (int)algo.CustomCase };
|
||||
_build_structure(algo, ref s, dynamic_type_index);
|
||||
return s;
|
||||
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, int pos) {
|
||||
var cad = OrderDataHelper.get_child_algo_data(algo);
|
||||
for (int di = 0; di < cad.Count; di++) {
|
||||
if (cad[di].get_child(algo) is CustomOrderDefinition child_algo) {
|
||||
ArrayHelper.ensure_capacity(ref structure, pos + 1);
|
||||
int del_mask = 1 << (31 - di);
|
||||
structure[pos++] = (int)child_algo.CustomCase | del_mask;
|
||||
_build_structure(child_algo, ref structure, pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ArrayHelper.trim(ref structure, pos);
|
||||
}
|
||||
|
||||
public static CustomOrderDefinitionImmutable from_array(int[] structure, DynamicOrderHelper dynamic_helper) =>
|
||||
parsei(structure, dynamic_helper);
|
||||
|
||||
public static CustomOrderDefinitionImmutable parsei(IReadOnlyList<int> structure, DynamicOrderHelper dynamic_helper) =>
|
||||
new CustomOrderDefinitionImmutable(parse(structure, dynamic_helper));
|
||||
|
||||
public static CustomOrderDefinition parse(IReadOnlyList<int> structure, DynamicOrderHelper dynamic_helper) {
|
||||
if (structure == null || structure.Count == 0)
|
||||
return new CustomOrderDefinition();
|
||||
int pos = 1;
|
||||
var mdef = OrderDataHelper.create_algo((CustomOrderDefinition.CustomOneofCase)structure[0]);
|
||||
_build_def(structure, ref pos, ref mdef, dynamic_helper);
|
||||
return mdef;
|
||||
}
|
||||
|
||||
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, IMaskIndexInt32<string> dynamic_type_index) {
|
||||
if (structure == null || structure.Length == 0)
|
||||
structure = new int[1];
|
||||
structure[0] = (int)algo.CustomCase;
|
||||
int p = 1;
|
||||
_build_structure(algo, ref structure, ref p, dynamic_type_index);
|
||||
ArrayHelper.trim(ref structure, p);
|
||||
}
|
||||
|
||||
static void _build_structure(CustomOrderDefinition algo, ref int[] structure, ref int pos, IMaskIndexInt32<string> dynamic_type_index) {
|
||||
IEnumerable<(CustomOrderDefinition child_algo, int field_pos)> children;
|
||||
if (algo.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic) {
|
||||
ArrayHelper.ensure_capacity(ref structure, pos + 1);
|
||||
structure[pos++] = dynamic_type_index.to_int(algo.Dynamic.DynamicOrderType) | _dynamic_mask;
|
||||
children = algo.Dynamic.OrderParams.Where(kv => kv.Value.ValueCase == DynamicOrderParam.ValueOneofCase.AsOrderDef).Select((kv, i) => (kv.Value.AsOrderDef, i));
|
||||
} else {
|
||||
static void _build_def(int[] structure, ref int pos, ref CustomOrderDefinition algo) {
|
||||
for (int i = pos; i < structure.Length; i++) {
|
||||
var cad = OrderDataHelper.get_child_algo_data(algo);
|
||||
children = cad.Select((c, i) => (c.get_child(algo), i));
|
||||
children = children.Where(c => c.child_algo != null);
|
||||
}
|
||||
foreach ((var child, int field_pos) in children) {
|
||||
ArrayHelper.ensure_capacity(ref structure, pos + 1);
|
||||
structure[pos++] = EnumMaskIndexInt32<CustomOrderDefinition.CustomOneofCase>.to_int(child.CustomCase) | _child_algo_mask(field_pos);
|
||||
_build_structure(child, ref structure, ref pos, dynamic_type_index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a mask for a particular child-algo field of a particular algo definition.
|
||||
/// </summary>
|
||||
/// <param name="field_pos">The index wrt the deterministic enumeration of child algos for a particular definition (e.g., Hedge = 0, Child = 1).</param>
|
||||
static int _child_algo_mask(int field_pos) => 1 << (_child_mask_offset - field_pos);
|
||||
|
||||
static void _build_def(IReadOnlyList<int> structure, ref int pos, ref CustomOrderDefinition algo, DynamicOrderHelper dynamic_helper) {
|
||||
var local_algo = algo;
|
||||
IEnumerable<(Action<CustomOrderDefinition> assign_child, int field_pos)> children;
|
||||
if (algo.CustomCase == CustomOrderDefinition.CustomOneofCase.Dynamic) {
|
||||
var dyn_def = algo.Dynamic;
|
||||
dyn_def.DynamicOrderType = dynamic_helper.dynamic_type_index.from_int(structure[pos++] ^ _dynamic_mask);
|
||||
dynamic_helper.add_dynamic_params(ref dyn_def);
|
||||
children = dyn_def.OrderParams
|
||||
.Where(kv => kv.Value.ValueCase == DynamicOrderParam.ValueOneofCase.AsOrderDef)
|
||||
.Select((kv, i) => (new Action<CustomOrderDefinition>(child_algo => kv.Value.AsOrderDef = child_algo), i));
|
||||
} else {
|
||||
children = OrderDataHelper.get_child_algo_data(algo)
|
||||
.Select((c, i) => (new Action<CustomOrderDefinition>(child_algo => c.assign_child(local_algo, child_algo)), i));
|
||||
}
|
||||
using (var children_en = children.GetEnumerator()) {
|
||||
while (pos < structure.Count && children_en.MoveNext()) {
|
||||
(var assign_child, int field_pos) = children_en.Current;
|
||||
int mask = _child_algo_mask(field_pos);
|
||||
if ((structure[pos] & mask) == mask) {
|
||||
var type = EnumMaskIndexInt32<CustomOrderDefinition.CustomOneofCase>.from_int(structure[pos++] ^ mask);
|
||||
var child_algo = OrderDataHelper.create_algo(type);
|
||||
_build_def(structure, ref pos, ref child_algo, dynamic_helper);
|
||||
assign_child(child_algo);
|
||||
for (int di = 0; di < cad.Count; di++) {
|
||||
int mask = 1 << (31 - di);
|
||||
if ((structure[i] & mask) == mask) {
|
||||
var type = (CustomOrderDefinition.CustomOneofCase)(structure[i] ^ mask);
|
||||
var child_algo = OrderDataHelper.Defaults.CustomOrderDefinition(type);
|
||||
pos++;
|
||||
_build_def(structure, ref pos, ref child_algo);
|
||||
cad[di].assign_child(algo, child_algo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,18 +34,6 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public static void update_persisted_definition_wrappers(ObservableKeyedCollection<IAlgoVM, AlgoKey> target, TableUpdate<AlgoData, AlgoKey> update, Func<AlgoData, AlgoDefinitionVM> factory) {
|
||||
foreach (var d in update.removed)
|
||||
target.Remove(d.key);
|
||||
foreach (var d in update.changed)
|
||||
target[d.key].load_from(d.value);
|
||||
foreach (var d in update.added) {
|
||||
if (target.TryGetValue(d.key, out IAlgoVM def)) {
|
||||
def.load_from(d.value);
|
||||
} else target.Add(new AlgoVMWrapper(factory(d)));
|
||||
}
|
||||
}
|
||||
|
||||
static class _CompareUtils {
|
||||
public static int compare_modes(AlgoDefinitionVM a, AlgoDefinitionVM b) =>
|
||||
_compare_modes(a.key.mode, b.key.mode);
|
||||
|
||||
@@ -46,8 +46,7 @@ namespace mbts.spark {
|
||||
readonly DeferrableActionWrapper _refresh_on_local_changes;
|
||||
readonly ReactiveThresholdMonitor<AlgoDefinitionTemplateVM> _any_templates_dirty_monitor = new ReactiveThresholdMonitor<AlgoDefinitionTemplateVM>(t => t.observe_is_dirty());
|
||||
readonly Property<AlgoFormats> _selected_format;
|
||||
readonly EnumMaskInt32<AlgoEditingPermissions> _permissions_mask = new EnumMaskInt32<AlgoEditingPermissions>();
|
||||
|
||||
AlgoEditingPermissions _editing_permissions;
|
||||
int? _favorite_index;
|
||||
Brush _badge_fill;
|
||||
Snapshot? _committed;
|
||||
@@ -55,43 +54,29 @@ namespace mbts.spark {
|
||||
TableUpdateBuffer<AlgoTemplateData, AlgoKey> _template_update_buffer;
|
||||
bool _connected;
|
||||
|
||||
public static AlgoDefinitionVM create(OrdersModel model, AlgoData data, bool connect = false, AlgoFormats init_format = AlgoFormats.Exchange, AlgoEditingPermissions editing_permissions = (AlgoEditingPermissions)int.MaxValue) {
|
||||
var rv = new AlgoDefinitionVM(model, data, init_format, editing_permissions);
|
||||
public static AlgoDefinitionVM create(OrdersModel model, AlgoData data, bool connect = false, AlgoFormats init_format = AlgoFormats.Exchange) {
|
||||
var rv = new AlgoDefinitionVM(model, data, init_format);
|
||||
if (connect)
|
||||
rv.connect();
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static AlgoDefinitionVM create_placeholder(OrdersModel model, CustomOrderDefinitionImmutable def, string name, AlgoFormats init_format) =>
|
||||
new AlgoDefinitionVM(model, new AlgoData(def, name, new AlgoKey(Guid.NewGuid())), init_format, (AlgoEditingPermissions)int.MaxValue);
|
||||
new AlgoDefinitionVM(model, new AlgoData(def, name, new AlgoKey(Guid.NewGuid())), init_format);
|
||||
|
||||
AlgoDefinitionVM(OrdersModel model, AlgoData def_data, AlgoFormats init_format, AlgoEditingPermissions init_permissions) {
|
||||
AlgoDefinitionVM(OrdersModel model, AlgoData def_data, AlgoFormats init_format = AlgoFormats.Exchange) {
|
||||
// init core data
|
||||
_model = model;
|
||||
_permissions_mask.mask = init_permissions;
|
||||
_permissions_mask.raw = init_permissions;
|
||||
var resources = new CompositeDisposable();
|
||||
if (!model.algo_model.ui_configuration.can_modify_definition_values)
|
||||
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditValues | AlgoEditingPermissions.AllowSaveValues);
|
||||
if (!model.algo_model.ui_configuration.can_modify_definition_structure)
|
||||
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure);
|
||||
else if (model.algo_model.templates_enabled()) {
|
||||
resources.Add(_templates.Subscribe((s, e) => {
|
||||
if (_templates.Count == 0) {
|
||||
editing_permissions_mask |= AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure;
|
||||
} else {
|
||||
editing_permissions_mask ^= (AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure);
|
||||
}
|
||||
}));
|
||||
}
|
||||
key = def_data.key;
|
||||
algos_view = ObservableAutoWrapper.create_sorted_wrapper(_algo_roots, AlgoCollectionHelper.algo_vm_comparer);
|
||||
_template_update_buffer = new TableUpdateBuffer<AlgoTemplateData, AlgoKey>(t => t.key);
|
||||
_templates_with_custom = new ObservableAppendCollection<AlgoDefinitionTemplateVM>(templates);
|
||||
templates_view = (ListCollectionView)CollectionViewSource.GetDefaultView(_templates_with_custom);
|
||||
custom_badge_color_palettes.AddRange(ColorPalette.all_presets(model.factory).Select(p => new ColorPaletteVM(p)));
|
||||
_editing_permissions = _get_permissions();
|
||||
_version = new VersionContainer<Snapshot>(_snapshot);
|
||||
var conf = _model.algo_model.ui_configuration;
|
||||
var resources = new CompositeDisposable();
|
||||
// init backing props
|
||||
_name = backing(nameof(name), def_data.name);
|
||||
_abbreviation = backing(nameof(abbreviation), string.Empty);
|
||||
@@ -102,7 +87,7 @@ namespace mbts.spark {
|
||||
_is_selected = backing(nameof(is_selected), true);
|
||||
_is_dirty = backing(nameof(is_dirty), () => key.mode == AlgoKey.Mode.Unpersisted || !_committed.HasValue || !_committed.Value.Equals(_version.value));
|
||||
var factory_def_data = def_data.key.mode == AlgoKey.Mode.Factory ? def_data : new AlgoData(def_data.value);
|
||||
_algo_root = backing(nameof(algo_root), _algo_roots.GetOrAdd(factory_def_data.key, _ => OrderTypeVMFactory.create(factory_def_data)));
|
||||
_algo_root = backing(nameof(algo_root), _algo_roots.GetOrAdd(factory_def_data.key, _ => OrderTypeVMFactory.create(factory_def_data, _editing_permissions)));
|
||||
_visible_algo_root = backing<IAlgoVM>(nameof(visible_algo_root));
|
||||
_can_redo = backing(nameof(can_redo), () => _edit_stack.get_can_redo(_version.value));
|
||||
_can_undo = backing(nameof(can_undo), () => _edit_stack.get_can_undo(_version.value));
|
||||
@@ -128,6 +113,7 @@ namespace mbts.spark {
|
||||
as_demuxer().subscribe_many(_refresh_on_local_changes.execute, nameof(this.name), nameof(abbreviation), nameof(badge_fill)),
|
||||
_edit_pane_vs_model.add_source(VisualStateNames.BadgeSelector.GroupName, custom_badge_color.observe_nullable(true).Select(t => t.HasValue ? VisualStateNames.BadgeSelector.Active : VisualStateNames.BadgeSelector.Inactive)),
|
||||
_edit_pane_vs_model.add_source(VisualStateNames.Abbreviation.GroupName, _abbreviation.observe_current(true).Select(a => string.IsNullOrEmpty(a) ? VisualStateNames.Abbreviation.Unpopulated : VisualStateNames.Abbreviation.Populated)),
|
||||
templates.ObserveSelf().Select(_ => _get_permissions()).DistinctUntilChanged().OnDispatcher().Subscribe(on_editing_permissions),
|
||||
_algo_root.observe_current(true).SubscribeHookLatest(vm => {
|
||||
if (vm == null)
|
||||
return Disposable.empty;
|
||||
@@ -166,22 +152,16 @@ namespace mbts.spark {
|
||||
_edit_pane_vs_model.set_state(VisualStateNames.Name.GroupName, VisualStateNames.Name.Inactive);
|
||||
break;
|
||||
}
|
||||
var permission_src = _permissions_mask.observe_result();
|
||||
resources.Add(_selected_format.observe_current(true).CombineLatest(permission_src, (fmt, perms) => (fmt, perms))
|
||||
.SubscribeHookLatest(t => {
|
||||
if ((t.perms & AlgoEditingPermissions.AllowEditStructure) != 0) {
|
||||
using (_refresh_on_local_changes.defer_execution()) {
|
||||
var algo_data = model.factory.default_algos(t.fmt);
|
||||
var default_algo = new AlgoKey(OrderDataHelper.get_default_algo(t.fmt));
|
||||
return this.load_algo_data_recursive(algo_data, default_algo);
|
||||
}
|
||||
}
|
||||
return Disposable.empty;
|
||||
}));
|
||||
resources.Add(permission_src.Subscribe(_ => RaisePropertyChanged(nameof(editing_permissions))));
|
||||
resources.Add(this.connect_data(permission_src));
|
||||
resources.Add(_selected_format.observe_current(true).SubscribeHookLatest(f => {
|
||||
using (_refresh_on_local_changes.defer_execution()) {
|
||||
var algo_data = model.factory.default_algos(f);
|
||||
var default_algo = new AlgoKey(OrderDataHelper.get_default_algo(f));
|
||||
return this.load_algo_data_recursive(algo_data, default_algo);
|
||||
}
|
||||
}));
|
||||
resources.Add(this.connect_data(Observable.Return(model)));
|
||||
_dispose = resources;
|
||||
on_editing_permissions(_get_permissions());
|
||||
await algo_root.load_from_async(def_data.value);
|
||||
_version.increment_version();
|
||||
_edit_stack.reset(_version.value);
|
||||
@@ -189,6 +169,11 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
// local methods
|
||||
void on_editing_permissions(AlgoEditingPermissions eps) {
|
||||
_editing_permissions = eps;
|
||||
foreach (var vm in algo_roots)
|
||||
vm.set_permissions(eps);
|
||||
}
|
||||
void on_can_redo_changed(bool oldval, bool newval) => can_redo_changed?.Invoke();
|
||||
void on_can_undo_changed(bool oldval, bool newval) => can_undo_changed?.Invoke();
|
||||
void on_local_changed() {
|
||||
@@ -245,9 +230,6 @@ namespace mbts.spark {
|
||||
// visual states
|
||||
public ObservableCollection<string> edit_pane_visual_states => _edit_pane_vs_model.states;
|
||||
public ObservableCollection<string> tree_view_visual_states => _tree_view_vs_model.states;
|
||||
// editing permissions
|
||||
public AlgoEditingPermissions editing_permissions_mask { get => _permissions_mask.mask; set => _permissions_mask.mask = value; }
|
||||
public AlgoEditingPermissions editing_permissions => _permissions_mask.result;
|
||||
|
||||
#region IAlgoSelector
|
||||
event PropertyChange<IAlgoVM> IAlgoSelector.selected_algo_changed { add => algo_root_changed += value; remove => algo_root_changed -= value; }
|
||||
@@ -266,7 +248,7 @@ namespace mbts.spark {
|
||||
|
||||
void _load_algo_data(IReadOnlyDictionary<AlgoKey, AlgoData> algo_data, AlgoKey? default_algo) {
|
||||
var orig_algo = algo_root?.id;
|
||||
_algo_roots.SyncWithDictionary(algo_data, d => OrderTypeVMFactory.create(d, is_expanded: true));
|
||||
_algo_roots.SyncWithDictionary(algo_data, d => OrderTypeVMFactory.create(d, _editing_permissions, true));
|
||||
if (!try_select(orig_algo) && !try_select(default_algo))
|
||||
algo_root = algo_roots.FirstOrDefault();
|
||||
//
|
||||
@@ -316,19 +298,6 @@ namespace mbts.spark {
|
||||
|
||||
public IDisposable buffer_updates() => _template_update_buffer.buffer_updates();
|
||||
|
||||
public void load_def(CustomOrderDefinitionImmutable def) {
|
||||
var key = OrderDataHelper.get_shallow_factory_key(def);
|
||||
if (algo_root?.id == key) {
|
||||
algo_root.load_from(def);
|
||||
} else if (_algo_roots.TryGetValue(key, out IAlgoVM vm)) {
|
||||
vm.load_from(def);
|
||||
algo_root = vm;
|
||||
} else {
|
||||
algo_roots.Add(vm = _mk_algo_root(def));
|
||||
algo_root = vm;
|
||||
}
|
||||
}
|
||||
|
||||
public void load(AlgoData data) {
|
||||
_assert_can_load(data.key);
|
||||
if (algo_root != null)
|
||||
@@ -340,7 +309,7 @@ namespace mbts.spark {
|
||||
|
||||
// pass a copy of the persisted state - rather than use local values - because if something goes wrong, it is better to be in sync with data fresh from the server/api.
|
||||
public AlgoDefinitionVM to_persisted(AlgoData def) =>
|
||||
new AlgoDefinitionVM(_model, def, selected_format, editing_permissions_mask);
|
||||
new AlgoDefinitionVM(_model, def);
|
||||
|
||||
public AlgoData create_algo_def() =>
|
||||
new AlgoData(algo_root.definition, name, key);
|
||||
@@ -353,6 +322,16 @@ namespace mbts.spark {
|
||||
public override int GetHashCode() => key.GetHashCode();
|
||||
public override string ToString() => algo_root == null ? $"!:{name}" : $"{algo_root.order_type}:{name}";
|
||||
|
||||
AlgoEditingPermissions _get_permissions() {
|
||||
var permissions = AlgoEditingPermissions.None;
|
||||
if (_model.algo_model.ui_configuration.can_modify_definition_values) {
|
||||
permissions |= AlgoEditingPermissions.AllowEditValues | AlgoEditingPermissions.AllowSaveValues;
|
||||
}
|
||||
if (_model.algo_model.ui_configuration.can_modify_definition_structure || (_model.algo_model.templates_enabled() && _templates.Count == 0))
|
||||
permissions |= AlgoEditingPermissions.AllowEditStructure | AlgoEditingPermissions.AllowSaveStructure;
|
||||
return permissions;
|
||||
}
|
||||
|
||||
void _assert_can_load(AlgoKey data_key) {
|
||||
if (key.mode != data_key.mode)
|
||||
throw new InvalidOperationException($"{typeof(AlgoDefinitionVM)} '{nameof(key)}.{nameof(AlgoKey.mode)}' must be {data_key.mode} (actually {key.mode}), to load specified data.");
|
||||
@@ -383,7 +362,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
IAlgoVM _mk_algo_root(CustomOrderDefinitionImmutable definition) =>
|
||||
OrderTypeVMFactory.create(new AlgoData(definition), is_expanded: true);
|
||||
OrderTypeVMFactory.create(new AlgoData(definition), editing_permissions: _editing_permissions, true);
|
||||
|
||||
void _on_metadata(AlgoDefinitionMetadata metadata) {
|
||||
using (_refresh_on_local_changes.supress_execution()) {
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
class AlgoVMWrapper : DisposableViewModelBase, IAlgoVM,
|
||||
IObserver<SecurityMD?>,
|
||||
IObserver<SecurityImmutable?>,
|
||||
IObserver<OrdersModel>,
|
||||
IObserver<AlgoEditingPermissions> {
|
||||
static readonly HashSet<string>
|
||||
_algo_root_echo_props = new HashSet<string> {
|
||||
nameof(is_expanded),
|
||||
nameof(editing_permissions),
|
||||
nameof(definition),
|
||||
nameof(display_name)
|
||||
},
|
||||
_value_echo_props = new HashSet<string> {
|
||||
nameof(AlgoDefinitionVM.name),
|
||||
nameof(AlgoDefinitionVM.abbreviation),
|
||||
nameof(AlgoDefinitionVM.badge_fill),
|
||||
nameof(AlgoDefinitionVM.can_undo),
|
||||
nameof(AlgoDefinitionVM.can_redo)
|
||||
};
|
||||
readonly ObservableAggregateCollection<IAlgoSelector> _child_algo_selectors = new ObservableAggregateCollection<IAlgoSelector>();
|
||||
readonly DisposableContainer _value_hooks = new DisposableContainer();
|
||||
Action _definition_changed = () => { };
|
||||
_ObservedData? _data_cache;
|
||||
AlgoDefinitionVM _value;
|
||||
|
||||
public AlgoVMWrapper(AlgoDefinitionVM algo_definition_vm) {
|
||||
value = algo_definition_vm;
|
||||
_dispose = new CompositeDisposable(_value_hooks) {
|
||||
_child_algo_selectors.clear_collections,
|
||||
() => _value?.Dispose()
|
||||
};
|
||||
}
|
||||
|
||||
public event Action definition_changed { add => _definition_changed += value; remove => _definition_changed -= value; }
|
||||
|
||||
public AlgoDefinitionVM value {
|
||||
get => _value;
|
||||
set {
|
||||
if (_value != value) {
|
||||
_value?.Dispose();
|
||||
_value = value;
|
||||
_value_hooks.replace(_set_hooks(value));
|
||||
RaisePropertyChanged(nameof(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
public AlgoKey id => _value.key;
|
||||
public CustomOrderDefinition.CustomOneofCase order_type => _algo_root?.order_type ?? CustomOrderDefinition.CustomOneofCase.None;
|
||||
public bool is_bts_type => _algo_root?.is_bts_type ?? false;
|
||||
public OrderMetadata meta {
|
||||
get {
|
||||
if (_value != null) {
|
||||
if (_value.custom_badge_color.enabled)
|
||||
return new OrderMetadata { Color = value.custom_badge_color.value.rgb_color(), KeyBinding = value.abbreviation.if_null_or_empty(value.name) };
|
||||
else if (_value.algo_root != null)
|
||||
return _value.algo_root.meta;
|
||||
}
|
||||
return OrderDataHelper.get_metadata(order_type);
|
||||
}
|
||||
}
|
||||
public AlgoEditingPermissions editing_permissions => _value?.editing_permissions ?? AlgoEditingPermissions.None;
|
||||
public bool can_expand => _algo_root?.can_expand ?? false;
|
||||
public bool is_expanded {
|
||||
get => _value.algo_root?.is_expanded ?? false;
|
||||
set {
|
||||
if (_value != null)
|
||||
_value.is_expanded = value;
|
||||
}
|
||||
}
|
||||
public string display_name => _value?.name ?? string.Empty;
|
||||
public CustomOrderDefinitionImmutable definition => _algo_root?.definition ?? new CustomOrderDefinitionImmutable(new CustomOrderDefinition());
|
||||
public ObservableCollection<IAlgoSelector> child_algo_selectors => _child_algo_selectors;
|
||||
public dynamic children_vm => _algo_root?.children_vm;
|
||||
|
||||
IAlgoVM _algo_root => _value?.algo_root;
|
||||
|
||||
public void assign_to(CustomOrderDefinition value) => _algo_root?.assign_to(value);
|
||||
public bool key_match(KeyEventArgs e) => _algo_root?.key_match(e) ?? false;
|
||||
public void load_from(CustomOrderDefinitionImmutable value) => _algo_root?.load_from(value);
|
||||
public Task<CustomOrderDefinitionImmutable?> load_from_async(CustomOrderDefinitionImmutable definition) => _algo_root is IAlgoVM vm ? vm.load_from_async(definition) : Task.FromResult<CustomOrderDefinitionImmutable?>(null);
|
||||
|
||||
IDisposable _set_hooks(AlgoDefinitionVM vm) {
|
||||
var algo_root_subs = new DisposableContainer();
|
||||
vm.PropertyChanged += on_value_prop_changed;
|
||||
return new CompositeDisposable(
|
||||
vm.observe_algo_root().Subscribe(r => {
|
||||
algo_root_subs.replace(null);
|
||||
_child_algo_selectors.clear_collections();
|
||||
if (r != null) {
|
||||
if (_data_cache.HasValue) {
|
||||
_data_cache.Value.forward_to(r);
|
||||
}
|
||||
_child_algo_selectors.add_collection(r.child_algo_selectors);
|
||||
((INotifyPropertyChanged)r).PropertyChanged += on_algo_root_prop_changed;
|
||||
algo_root_subs.replace(new CompositeDisposable(
|
||||
r.subscribe_definition().Subscribe(_ => _definition_changed?.Invoke())) {
|
||||
() => ((INotifyPropertyChanged)r).PropertyChanged -= on_algo_root_prop_changed
|
||||
});
|
||||
}
|
||||
}),
|
||||
algo_root_subs) {
|
||||
() => vm.PropertyChanged -= on_value_prop_changed
|
||||
};
|
||||
void on_algo_root_prop_changed(object sender, PropertyChangedEventArgs e) {
|
||||
if (_algo_root_echo_props.Contains(e.PropertyName))
|
||||
RaisePropertyChanged(e.PropertyName);
|
||||
}
|
||||
void on_value_prop_changed(object sender, PropertyChangedEventArgs e) {
|
||||
if (_value_echo_props.Contains(e.PropertyName))
|
||||
RaisePropertyChanged(e.PropertyName);
|
||||
}
|
||||
}
|
||||
|
||||
// permissions
|
||||
void IObserver<AlgoEditingPermissions>.OnNext(AlgoEditingPermissions value) {
|
||||
_data_cache = _data_cache?.with(value) ?? new _ObservedData(value, default, default, default);
|
||||
(_algo_root as IObserver<AlgoEditingPermissions>)?.OnNext(value);
|
||||
}
|
||||
void IObserver<AlgoEditingPermissions>.OnError(Exception error) { }
|
||||
void IObserver<AlgoEditingPermissions>.OnCompleted() { }
|
||||
// model
|
||||
void IObserver<OrdersModel>.OnNext(OrdersModel value) {
|
||||
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, value, default, default);
|
||||
(_algo_root as IObserver<OrdersModel>)?.OnNext(value);
|
||||
}
|
||||
void IObserver<OrdersModel>.OnError(Exception error) { }
|
||||
void IObserver<OrdersModel>.OnCompleted() { }
|
||||
// security
|
||||
void IObserver<SecurityImmutable?>.OnNext(SecurityImmutable? value) {
|
||||
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, default, value, default);
|
||||
(_algo_root as IObserver<SecurityImmutable?>)?.OnNext(value);
|
||||
}
|
||||
void IObserver<SecurityImmutable?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityImmutable?>.OnCompleted() { }
|
||||
// security md
|
||||
void IObserver<SecurityMD?>.OnNext(SecurityMD? value) {
|
||||
_data_cache = _data_cache?.with(value) ?? new _ObservedData(default, default, default, value);
|
||||
(_algo_root as IObserver<SecurityMD?>)?.OnNext(value);
|
||||
}
|
||||
void IObserver<SecurityMD?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityMD?>.OnCompleted() { }
|
||||
|
||||
struct _ObservedData {
|
||||
public readonly AlgoEditingPermissions permissions;
|
||||
public readonly OrdersModel model;
|
||||
public readonly SecurityImmutable? security;
|
||||
public readonly SecurityMD? security_md;
|
||||
|
||||
public _ObservedData(AlgoEditingPermissions permissions, OrdersModel model, SecurityImmutable? security, SecurityMD? security_md) {
|
||||
this.permissions = permissions;
|
||||
this.model = model;
|
||||
this.security = security;
|
||||
this.security_md = security_md;
|
||||
}
|
||||
|
||||
public void forward_to(IAlgoVM recipient) {
|
||||
(recipient as IObserver<AlgoEditingPermissions>)?.OnNext(permissions);
|
||||
if (model != null)
|
||||
(recipient as IObserver<OrdersModel>)?.OnNext(model);
|
||||
if (security_md.HasValue)
|
||||
(recipient as IObserver<SecurityMD?>)?.OnNext(security_md);
|
||||
if (security.HasValue)
|
||||
(recipient as IObserver<SecurityImmutable?>)?.OnNext(security);
|
||||
}
|
||||
|
||||
public _ObservedData with(AlgoEditingPermissions? permissions) =>
|
||||
new _ObservedData(permissions ?? this.permissions, model, security, security_md);
|
||||
|
||||
public _ObservedData with(OrdersModel model) =>
|
||||
new _ObservedData(permissions, model, security, security_md);
|
||||
|
||||
public _ObservedData with(SecurityImmutable? security) =>
|
||||
new _ObservedData(permissions, model, security, security_md);
|
||||
|
||||
public _ObservedData with(SecurityMD? security_md) =>
|
||||
new _ObservedData(permissions, model, security, security_md);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.AlgoEditorView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoPickerNodeView.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Order/AlgoContentTemplateSelector.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!--Primitive Constants-->
|
||||
<sys:Double x:Key="HorizontalPush1">4</sys:Double>
|
||||
<sys:Double x:Key="HorizontalPush2">8</sys:Double>
|
||||
<sys:Double x:Key="HorizontalPush3">12</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush1">4</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush2">8</sys:Double>
|
||||
<sys:Double x:Key="VerticalPush3">12</sys:Double>
|
||||
<Thickness x:Key="Margin.LP1" Left="{StaticResource HorizontalPush1}"/>
|
||||
<Thickness x:Key="Margin.LP2" Left="{StaticResource HorizontalPush2}"/>
|
||||
<Thickness x:Key="Margin.LP3" Left="{StaticResource HorizontalPush3}"/>
|
||||
<Thickness x:Key="Margin.TP1" Top="{StaticResource VerticalPush1}"/>
|
||||
<Thickness x:Key="Margin.TP2" Top="{StaticResource VerticalPush2}"/>
|
||||
<!--Converters-->
|
||||
<uspcv:OrderTypeBrushConverter x:Key="OrderTypeBrushConverter" DefaultBrush="Transparent"/>
|
||||
<!--Default Styles-->
|
||||
<Style x:Key="ContentHostGroupBoxStyle" TargetType="GroupBox">
|
||||
<Setter Property="Padding" Value="8"/>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="u:TextBoxBehavior.EnterKeyBehavior" Value="CommitAndRelease"/>
|
||||
<Setter Property="u:TextBoxBehavior.ReleaseOnEscape" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="uag:AlgoPickerNodeView" BasedOn="{StaticResource {x:Type uag:AlgoPickerNodeView}}">
|
||||
<Setter Property="DropDownStyleKey" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=usp:AlgoEditorView}, Converter={x:Static usp:AlgoContentTemplateSelector.AlgoDropDownStyleKeyConverter}}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<GroupBox
|
||||
x:Name="content_host"
|
||||
Grid.Row="2"
|
||||
Background="Transparent"
|
||||
BorderBrush="{Binding algo_root, Converter={StaticResource OrderTypeBrushConverter}}"
|
||||
Margin="0,6,0,0"
|
||||
Padding="12,8"
|
||||
>
|
||||
<GroupBox.Style>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="BorderBrush" Value="Transparent">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
</Trigger>
|
||||
<Trigger Property="usp:AlgoEditorView.IsMouseOverNodeContent" Value="True">
|
||||
<Setter Property="BorderThickness" Value="2"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</GroupBox.Style>
|
||||
<GroupBox.Header>
|
||||
<ComboBox
|
||||
ToolTipService.ShowOnDisabled="True"
|
||||
Width="140"
|
||||
Margin="{StaticResource Margin.LP1}"
|
||||
ItemsSource="{Binding algos_view}"
|
||||
SelectedItem="{Binding algo_root}"
|
||||
>
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:True}">
|
||||
<Setter Property="IsEnabled" Value="True"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding templates.Count, Converter={StaticResource IntIsZeroConverter}, FallbackValue={u:False}}" Value="{u:False}">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
<Setter Property="ToolTip" Value="Cannot change structure while templates are present."/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.Style>
|
||||
<ComboBox.IsEnabled>
|
||||
<Binding Path="editing_permissions">
|
||||
<Binding.Converter>
|
||||
<cv:EnumHasFlagsConverter>
|
||||
<sp:AlgoEditingPermissions>AllowEditStructure</sp:AlgoEditingPermissions>
|
||||
</cv:EnumHasFlagsConverter>
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
</ComboBox.IsEnabled>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<usp:OrderTypeItem DisplayName="{Binding display_name}" BadgeFill="{Binding meta.Color, Converter={StaticResource ColorToBrushConverter}}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</GroupBox.Header>
|
||||
<ContentControl
|
||||
Grid.IsSharedSizeScope="True"
|
||||
Content="{Binding algo_root, Converter={x:Static usp:AlgoContentTemplateSelector.DataContextConverter}}"
|
||||
ContentTemplateSelector="{StaticResource AlgoTemplateSelector}"
|
||||
Margin="{StaticResource Margin.TP1}"
|
||||
Visibility="{Binding algo_root.can_expand, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
>
|
||||
<ContentControl.Resources>
|
||||
<Style TargetType="GroupBox" BasedOn="{StaticResource ContentHostGroupBoxStyle}"/>
|
||||
</ContentControl.Resources>
|
||||
</ContentControl>
|
||||
</GroupBox>
|
||||
</UserControl>
|
||||
@@ -1,28 +0,0 @@
|
||||
using mbts.ui.extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
partial class AlgoEditorView : UserControl {
|
||||
public AlgoEditorView() {
|
||||
InitializeComponent();
|
||||
content_host.PreviewMouseMove += content_host_PreviewMouseMove;
|
||||
content_host.MouseEnter += content_host_MouseEnter;
|
||||
content_host.MouseLeave += content_host_MouseLeave;
|
||||
// local methods
|
||||
void content_host_PreviewMouseMove(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void content_host_MouseEnter(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void content_host_MouseLeave(object sender, MouseEventArgs e) => refresh_is_mouse_over_content_host(e);
|
||||
void refresh_is_mouse_over_content_host(MouseEventArgs e) => content_host.set_current_if_changed(IsMouseOverNodeContentProperty, this.ChildAtPoint<GroupBox>(e.GetPosition(this)) == content_host);
|
||||
}
|
||||
|
||||
#region IsMouseOverNodeContent
|
||||
public static readonly DependencyProperty IsMouseOverNodeContentProperty =
|
||||
DependencyProperty.RegisterAttached("IsMouseOverNodeContent", typeof(bool), typeof(AlgoEditorView), new PropertyMetadata(false));
|
||||
|
||||
public static bool GetIsMouseOverNodeContent(DependencyObject d) => (bool)d.GetValue(IsMouseOverNodeContentProperty);
|
||||
public static void SetIsMouseOverNodeContent(DependencyObject d, bool value) => d.SetValue(IsMouseOverNodeContentProperty, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,10 @@ using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows.Data;
|
||||
@@ -45,7 +42,7 @@ namespace mbts.spark {
|
||||
public FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IWindowSettingsManager<GridSettings> settings_manager)
|
||||
: this(bus, model, state_obs, _get_observable(bus, model, state_obs), settings_manager) { }
|
||||
|
||||
public FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IObservable<TableUpdate<FillTuple, string>> fills_source, IWindowSettingsManager<GridSettings> settings_manager) {
|
||||
FillsVM(IBus bus, OrdersModel model, IObservable<StartupData> state_obs, IConnectableObservable<TableUpdate<FillTuple, string>> fills_source, IWindowSettingsManager<GridSettings> settings_manager) {
|
||||
_model = model;
|
||||
_settings_manager = settings_manager;
|
||||
/// Columns
|
||||
@@ -101,17 +98,16 @@ namespace mbts.spark {
|
||||
// grid settings
|
||||
grid_settings_manager = new SimpleGridSettingsManager<Row>(columns, new Dictionary<string, GridColumnSettingsFeatureFlags> { }, rows);
|
||||
// this will initialize the immutable properties that correspond to the col_settings_flags above (e.g. Bid/Ask font_color_override, etc.)
|
||||
if (_settings_manager != null)
|
||||
grid_settings_manager.load_from(_settings_manager.current_settings());
|
||||
grid_settings_manager.load_from(settings_manager.current_settings());
|
||||
//
|
||||
_dispose = new CompositeDisposable(
|
||||
_dispose = Disposable.Create(
|
||||
ObservableAutoWrapper.connect(col_tups, columns, ct => ct.col),
|
||||
fills_source.OnDispatcher().Subscribe(t => {
|
||||
_rows.update(t);
|
||||
Set(ref _total_row_count, t.state.Count, nameof(total_row_count));
|
||||
}),
|
||||
state_obs.OnDispatcher().Subscribe(s => {
|
||||
if (s.grid != null && _settings_manager != null) {
|
||||
if (s.grid != null) {
|
||||
// this means we are not using defaults,
|
||||
// Note: settings_manager in turn notifies grid_settings_manager of this update
|
||||
_settings_manager.instance_settings = s.grid;
|
||||
@@ -124,8 +120,8 @@ namespace mbts.spark {
|
||||
};
|
||||
rows_manager.on_settings(fs, s.sorting_criterion);
|
||||
}),
|
||||
settings_manager == null ? Disposable.empty : settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
(fills_source as IConnectableObservable<TableUpdate<FillTuple, string>>)?.Connect() ?? Disposable.empty
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
fills_source.Connect()
|
||||
);
|
||||
// local methods
|
||||
void add_col<TFilter>(Columns col_key, string header, Func<Row, string> get_text, IDataGridColumnPersistentFilter<FillTuple, TFilter> filter, string filter_template, double width) =>
|
||||
@@ -157,12 +153,12 @@ namespace mbts.spark {
|
||||
public IEnumerable<SortingCriteria> get_sorting_criterion() =>
|
||||
_sort_manager.create();
|
||||
|
||||
public void on_sorting(object sender, DIYGridSortingEventArgs e) =>
|
||||
_sort_manager.on_sorting(e);
|
||||
|
||||
static Columns _col_key(ColumnBase column) =>
|
||||
((IKeyedColumn<Columns>)column).key;
|
||||
|
||||
void IHandleGridSorting.on_sorting(object sender, DIYGridSortingEventArgs e) =>
|
||||
_sort_manager.on_sorting(e);
|
||||
|
||||
public FilterStates get_filters() =>
|
||||
_filters_manager.create();
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using System;
|
||||
|
||||
namespace mbts.spark {
|
||||
/// <summary>
|
||||
/// This class provides a stable DataContext for a fills view, with an internal <see cref="FillsVM"/> that may be disposed and recreated as needed.
|
||||
/// </summary>
|
||||
class FillsVMContainer : IHandleGridSorting, IDisposable, IWillLetYouKnowWhenIDispose {
|
||||
bool _disposed;
|
||||
FillsVM _value;
|
||||
|
||||
public event Action<bool> OnDisposed;
|
||||
|
||||
public FillsVM value {
|
||||
get => _value;
|
||||
set {
|
||||
if (value != _value) {
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(FillsVMContainer));
|
||||
Disposable.replace(ref _value, value);
|
||||
if (_value != null) {
|
||||
rows.add_collection(_value.rows);
|
||||
columns.add_collection(_value.columns);
|
||||
} else {
|
||||
rows.clear_collections();
|
||||
columns.clear_collections();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableAggregateCollection<FillsVM.Row> rows { get; } = new ObservableAggregateCollection<FillsVM.Row>();
|
||||
public ObservableAggregateCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableAggregateCollection<DIYGrid.ColumnBase>();
|
||||
|
||||
public void Dispose() {
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(FillsVMContainer));
|
||||
value = null;
|
||||
OnDisposed?.Invoke(true);
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void on_sorting(object sender, DIYGridSortingEventArgs e) {
|
||||
if (!_disposed)
|
||||
_value?.on_sorting(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ using StartupData = bts.shell.FillsWindowFactory.StartupData;
|
||||
namespace mbts.spark {
|
||||
class FillsWindow : SparkWindow<StartupData>, ISharedWindow {
|
||||
public const string window_type_name = "Fills";
|
||||
const string _thread_id = "Fills";
|
||||
readonly AllowOnce _init_guard = new AllowOnce("Fills");
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly IWindowSettingsManager<GridSettings> _settings_manager;
|
||||
@@ -32,7 +33,6 @@ namespace mbts.spark {
|
||||
_orders_model = orders_model;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().fills ? "Fills" : null;
|
||||
}
|
||||
|
||||
event Action<FillsWindowFactory.StartupData> _state_changed;
|
||||
@@ -48,7 +48,7 @@ namespace mbts.spark {
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
|
||||
public string thread_id { get; }
|
||||
public string thread_id => _thread_id;
|
||||
|
||||
public void thread_init() {
|
||||
_init_guard.check_access();
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace bts.shell {
|
||||
_settings_hub = settings_hub;
|
||||
_orders_model = orders_model;
|
||||
_md_auxiliary_model = md_auxiliary_model;
|
||||
_ladder_conf = new LadderConfig(Configuration.get<bts.config.LadderKnobs>(), Configuration.get<bts.config.MultiThreadedWindows>().ladder);
|
||||
_ladder_conf = new LadderConfig(Configuration.get<bts.config.LadderKnobs>(), Configuration.get<bts.config.toggles.LaddersOnThreads>().enabled);
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(s => _securities = s),
|
||||
bus.subscribe(SparkTags.ShowLadderTag).OnDispatcher().Subscribe(s => show_windows(new[] { s })),
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
|
||||
<Style x:Key="{x:Static sp:OrderBookGridView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Style x:Key="{x:Static sp:OrderBookView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookGridView.ColumnKeys> {
|
||||
class MiniOrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
|
||||
public MiniOrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,14 @@ using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using static bts.shell.OrderBookWindowFactory;
|
||||
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
|
||||
using static mbts.spark.OrderBookView;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using OrderType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
|
||||
namespace mbts.spark {
|
||||
// This interface is used to hide ColumnBase.SortDirection, and offer a SortDirection member that does NOT require a DependencyProperty lookup.
|
||||
interface ICVOrderBookColumn : IKeyedColumn<Columns>, IGridColumn {
|
||||
string sort_member_path { get; }
|
||||
ListSortDirection? SortDirection { get; set; }
|
||||
IDataGridColumnFilter<OrderSecurityAlgoMeta> filter { get; }
|
||||
IComparer<OrderBookRowVM> comparer { get; }
|
||||
@@ -77,6 +78,7 @@ namespace mbts.spark {
|
||||
Add(symbol_column = new SimpleTextColumn<string[]>(
|
||||
Columns.Symbol,
|
||||
DefaultColumnHeaders.Symbol,
|
||||
nameof(OrderBookRowVM.symbol),
|
||||
r => r.symbol,
|
||||
DefaultColumnWidths.Symbol,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, string>(
|
||||
@@ -90,6 +92,7 @@ namespace mbts.spark {
|
||||
Add(time_created_column = new SimpleTextColumn<TimeFilterTuple>(
|
||||
Columns.Time,
|
||||
"Time",
|
||||
nameof(OrderBookRowVM.time_created),
|
||||
r => r.time_created_text,
|
||||
DefaultColumnWidths.Time,
|
||||
new TimeFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -101,6 +104,7 @@ namespace mbts.spark {
|
||||
Add(last_change_column = new SimpleTextColumn<TimeFilterTuple>(
|
||||
Columns.LastChange,
|
||||
"Last Change",
|
||||
nameof(OrderBookRowVM.last_change),
|
||||
r => r.last_change_text,
|
||||
DefaultColumnWidths.Time,
|
||||
new TimeFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -112,6 +116,7 @@ namespace mbts.spark {
|
||||
Add(user_column = new SimpleTextColumn<string[]>(
|
||||
Columns.User,
|
||||
DefaultColumnHeaders.User,
|
||||
nameof(OrderBookRowVM.user),
|
||||
r => r.user,
|
||||
DefaultColumnWidths.User,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, string>(
|
||||
@@ -124,6 +129,7 @@ namespace mbts.spark {
|
||||
Add(account_column = new SimpleTextColumn<string[]>(
|
||||
Columns.Account,
|
||||
DefaultColumnHeaders.Account,
|
||||
nameof(OrderBookRowVM.account),
|
||||
r => r.account,
|
||||
DefaultColumnWidths.Account,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, string, string[]>(
|
||||
@@ -138,6 +144,7 @@ namespace mbts.spark {
|
||||
Add(exchange_column = new SimpleTextColumn<Venue[]>(
|
||||
Columns.Exchange,
|
||||
DefaultColumnHeaders.Exchange,
|
||||
nameof(OrderBookRowVM.exchange),
|
||||
r => r.exchange.ToString(),
|
||||
DefaultColumnWidths.Exchange,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, Venue>(
|
||||
@@ -150,6 +157,7 @@ namespace mbts.spark {
|
||||
Add(time_in_force_column = new SimpleTextColumn<TimeInForce[]>(
|
||||
Columns.TimeInForce,
|
||||
DefaultColumnHeaders.TimeInForce,
|
||||
nameof(OrderBookRowVM.time_in_force),
|
||||
r => r.time_in_force.ToString(),
|
||||
DefaultColumnWidths.TimeInForce,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, TimeInForce>(
|
||||
@@ -168,13 +176,14 @@ namespace mbts.spark {
|
||||
Add(sub_type_column = new SimpleTextColumn<OrderSubType[]>(
|
||||
Columns.SubType,
|
||||
"Sub Type",
|
||||
nameof(OrderBookRowVM.order_sub_type),
|
||||
r => r.order_sub_type_text,
|
||||
80,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, OrderSubType>(
|
||||
EnumHelper.get_values<OrderSubType>().ToList(),
|
||||
ort => ort.order.sub_type(),
|
||||
nameof(OrderBookRowVM.order_sub_type)),
|
||||
OrderBookGridView.SubTypeFilterTemplateKey,
|
||||
SubTypeFilterTemplateKey,
|
||||
_wrap_comparer(r => r.order_sub_type)));
|
||||
// side
|
||||
Add(side_column = new SideColumn(
|
||||
@@ -196,6 +205,7 @@ namespace mbts.spark {
|
||||
Add(price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.Price,
|
||||
"Price",
|
||||
nameof(OrderBookRowVM.price),
|
||||
r => r.price_text,
|
||||
DefaultColumnWidths.Price,
|
||||
new DoubleMinMax<OrderSecurityAlgoMeta>(
|
||||
@@ -211,6 +221,7 @@ namespace mbts.spark {
|
||||
Add(avg_price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.AvgFilled,
|
||||
"Avg Price",
|
||||
nameof(OrderBookRowVM.avg_price),
|
||||
r => r.avg_price_text,
|
||||
DefaultColumnWidths.Price,
|
||||
new DoubleMinMax<OrderSecurityAlgoMeta>(
|
||||
@@ -222,6 +233,7 @@ namespace mbts.spark {
|
||||
Add(last_price_column = new SimpleTextColumn<DoubleMinMaxState>(
|
||||
Columns.LastPrice,
|
||||
"Last Filled",
|
||||
nameof(OrderBookRowVM.last_price),
|
||||
r => r.last_price_text,
|
||||
DefaultColumnWidths.Price,
|
||||
new DoubleMinMax<OrderSecurityAlgoMeta>(
|
||||
@@ -233,6 +245,7 @@ namespace mbts.spark {
|
||||
Add(status_column = new SimpleTextColumn<OrderStatus[]>(
|
||||
Columns.Status,
|
||||
"Status",
|
||||
nameof(OrderBookRowVM.status),
|
||||
r => r.status.ToString(),
|
||||
80,
|
||||
DiscreteValuesFilterVM.Create<OrderSecurityAlgoMeta, OrderStatus>(
|
||||
@@ -250,6 +263,7 @@ namespace mbts.spark {
|
||||
Add(security_key_column = new SimpleTextColumn<string>(
|
||||
Columns.SecurityKey,
|
||||
DefaultColumnHeaders.SecurityKey,
|
||||
nameof(OrderBookRowVM.security_key),
|
||||
r => r.security_key.ToString(),
|
||||
DefaultColumnWidths.SecurityKey,
|
||||
new RegexFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -261,6 +275,7 @@ namespace mbts.spark {
|
||||
Add(order_key_column = new SimpleTextColumn<string>(
|
||||
Columns.OrderKey,
|
||||
DefaultColumnHeaders.OrderKey,
|
||||
nameof(OrderBookRowVM.order_key),
|
||||
r => r.order_key.ToString(),
|
||||
DefaultColumnWidths.OrderKey,
|
||||
new RegexFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -272,6 +287,7 @@ namespace mbts.spark {
|
||||
Add(parent_key_column = new SimpleTextColumn<string>(
|
||||
Columns.ParentKey,
|
||||
"Parent Key",
|
||||
nameof(OrderBookRowVM.parent_order_key),
|
||||
r => r.parent_order_key.ToString(),
|
||||
DefaultColumnWidths.ParentKey,
|
||||
new RegexFilterVM<OrderSecurityAlgoMeta>(
|
||||
@@ -279,17 +295,6 @@ namespace mbts.spark {
|
||||
nameof(OrderBookRowVM.parent_order_key)),
|
||||
DataGridFilterKnownTemplates.Regex,
|
||||
_wrap_comparer(r => r.parent_order_key)));
|
||||
// attached target order key
|
||||
Add(attached_target_order_key_column = new SimpleTextColumn<string>(
|
||||
Columns.AttachedTargetOrderKey,
|
||||
DefaultColumnHeaders.AttachedTargetOrderKey,
|
||||
r => r.order.attached_target_order_key().ToString(),
|
||||
DefaultColumnWidths.AttachedTargetOrderKey,
|
||||
new RegexFilterVM<OrderSecurityAlgoMeta>(
|
||||
ort => ort.order.attached_target_order_key().ToString(),
|
||||
nameof(OrderBookRowVM.parent_order_key)),
|
||||
DataGridFilterKnownTemplates.Regex,
|
||||
_wrap_comparer(r => r.order.attached_target_order_key())));
|
||||
}
|
||||
|
||||
public event Action filter_changed;
|
||||
@@ -318,13 +323,10 @@ namespace mbts.spark {
|
||||
public SimpleTextColumn<string> security_key_column { get; }
|
||||
public SimpleTextColumn<string> order_key_column { get; }
|
||||
public SimpleTextColumn<string> parent_key_column { get; }
|
||||
public SimpleTextColumn<string> attached_target_order_key_column { get; }
|
||||
|
||||
public (FilterStates filters, IReadOnlyList<SortingCriteria> sorting) create() => (
|
||||
new FilterStates {
|
||||
order_key_regex = order_key_column.filter.get_state(),
|
||||
parent_key_regex = parent_key_column.filter.get_state(),
|
||||
attached_target_order_key_regex = attached_target_order_key_column.filter.get_state(),
|
||||
time_created = time_created_column.filter.get_state(),
|
||||
users = user_column.filter.get_state(),
|
||||
accounts = account_column.filter.get_state(),
|
||||
@@ -362,8 +364,6 @@ namespace mbts.spark {
|
||||
time_in_force_column.filter.load_state(fs.tifs);
|
||||
exchange_column.filter.load_state(fs.exchanges);
|
||||
order_key_column.filter.load_state(fs.order_key_regex);
|
||||
parent_key_column.filter.load_state(fs.parent_key_regex);
|
||||
attached_target_order_key_column.filter.load_state(fs.attached_target_order_key_regex);
|
||||
avg_price_column.filter.load_state(fs.avg_price);
|
||||
last_price_column.filter.load_state(fs.last_price);
|
||||
status_column.filter.load_state(fs.statuses);
|
||||
@@ -480,12 +480,13 @@ namespace mbts.spark {
|
||||
protected readonly Func<OrderBookRowVM, string> _get_text;
|
||||
GlyphContext _root_glyph_context, _child_glyph_context;
|
||||
|
||||
protected OrderbookColumnBase(Columns col_key, string header, Func<OrderBookRowVM, string> get_text, double width,
|
||||
protected OrderbookColumnBase(Columns col_key, string header, Func<OrderBookRowVM, string> get_text, string sort_prop, double width,
|
||||
IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, T> filter, string filter_template, IComparer<OrderBookRowVM> comparer) : base(col_key) {
|
||||
id = key.ToString();
|
||||
Width = width;
|
||||
Header = header;
|
||||
_get_text = get_text;
|
||||
sort_member_path = sort_prop;
|
||||
this.filter = filter;
|
||||
_filter_template = filter_template;
|
||||
_cached_sort_direction = SortDirection;
|
||||
@@ -514,9 +515,9 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public class SimpleTextColumn<TFilterState> : OrderbookColumnBase<TFilterState>, IRenderColumn<OrderBookRowVM> {
|
||||
public SimpleTextColumn(Columns key, string header, Func<OrderBookRowVM, string> get_text, double width,
|
||||
public SimpleTextColumn(Columns key, string header, string sort_prop, Func<OrderBookRowVM, string> get_text, double width,
|
||||
IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, TFilterState> filter, string filter_template, IComparer<OrderBookRowVM> comparer)
|
||||
: base(key, header, get_text, width, filter, filter_template, comparer) { }
|
||||
: base(key, header, get_text, sort_prop, width, filter, filter_template, comparer) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, _get_text(row), position, row_height,
|
||||
@@ -526,7 +527,7 @@ namespace mbts.spark {
|
||||
#region Explicit Column Defs
|
||||
public class SideColumn : OrderbookColumnBase<Side[]>, IRenderColumn<OrderBookRowVM> {
|
||||
public SideColumn(IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, Side[]> filter)
|
||||
: base(Columns.Side, "B/S", r => r.side.ToString(), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues, _wrap_comparer(r => r.side)) { }
|
||||
: base(Columns.Side, "B/S", r => r.side.ToString(), nameof(OrderBookRowVM.side), DefaultColumnWidths.Side, filter, DataGridFilterKnownTemplates.EnumValues, _wrap_comparer(r => r.side)) { }
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.side.ToString(), position, row_height,
|
||||
@@ -535,7 +536,9 @@ namespace mbts.spark {
|
||||
|
||||
public class SizeColumn : OrderbookColumnBase<UIntMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public SizeColumn(IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, UIntMinMaxState> filter)
|
||||
: base(Columns.Size, "Qty", r => r.quantity.ToString(), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.quantity)) { }
|
||||
: base(Columns.Size, "Qty", r => r.quantity.ToString(), nameof(OrderBookRowVM.quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.quantity)) {
|
||||
sort_member_path = nameof(OrderBookRowVM.quantity);
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.quantity.ToString(), position, row_height,
|
||||
@@ -544,7 +547,9 @@ namespace mbts.spark {
|
||||
|
||||
public class FilledColumn : OrderbookColumnBase<UIntMinMaxState>, IRenderColumn<OrderBookRowVM> {
|
||||
public FilledColumn(IDataGridColumnPersistentFilter<OrderSecurityAlgoMeta, UIntMinMaxState> filter)
|
||||
: base(Columns.Filled, "Filled Qty", r => r.filled_quantity.ToString(), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.filled_quantity)) { }
|
||||
: base(Columns.Filled, "Filled Qty", r => r.filled_quantity.ToString(), nameof(OrderBookRowVM.filled_quantity), DefaultColumnWidths.Size, filter, DataGridFilterKnownTemplates.UIntMinMax, _wrap_comparer(r => r.filled_quantity)) {
|
||||
sort_member_path = nameof(OrderBookRowVM.filled_quantity);
|
||||
}
|
||||
|
||||
public void draw(OrderBookRowVM row, DrawingContext dc, ColumnGeometry position, double row_height) =>
|
||||
CellRender.draw_text(dc, _get_glyph_context(row), font_size, row.filled_quantity.ToString(), position, row_height,
|
||||
@@ -557,6 +562,7 @@ namespace mbts.spark {
|
||||
: base(Columns.OrderType, new Binding(nameof(OrderBookRowVM.badge_fill)), new Binding(nameof(OrderBookRowVM.display_name)), filter) {
|
||||
id = key.ToString();
|
||||
this.filter = filter;
|
||||
sort_member_path = nameof(OrderBookRowVM.order_type);
|
||||
font_weight_binding = RenderUtils.get_font_weight_binding();
|
||||
_cached_sort_direction = SortDirection;
|
||||
}
|
||||
@@ -581,7 +587,8 @@ namespace mbts.spark {
|
||||
id = key.ToString();
|
||||
this.filter = filter;
|
||||
Header = "Last Message";
|
||||
Width = DefaultColumnWidths.LastMessage;
|
||||
Width = DIYGrid.DefaultColumnWidths.LastMessage;
|
||||
sort_member_path = nameof(OrderBookRowVM.last_msg);
|
||||
tooltip_binding = new Binding(nameof(OrderBookRowVM.last_msg));
|
||||
}
|
||||
|
||||
@@ -609,6 +616,7 @@ namespace mbts.spark {
|
||||
this.filter = filter;
|
||||
Header = DefaultColumnHeaders.Security;
|
||||
Width = DefaultColumnWidths.Security;
|
||||
sort_member_path = nameof(OrderBookRowVM.security);
|
||||
_cached_sort_direction = SortDirection;
|
||||
}
|
||||
|
||||
@@ -626,7 +634,7 @@ namespace mbts.spark {
|
||||
toggle_button.SetBinding(ToggleButton.IsCheckedProperty, new Binding(nameof(OrderBookRowVM.is_expanded)));
|
||||
toggle_button.SetBinding(ToggleButton.MarginProperty, new Binding(nameof(OrderBookRowVM.depth_level)) { Converter = _margin_converter });
|
||||
toggle_button.SetBinding(ToggleButton.VisibilityProperty, new Binding(nameof(OrderBookRowVM.is_parent)) { Converter = _expander_visiblilty_converter });
|
||||
toggle_button.SetResourceReference(ToggleButton.StyleProperty, OrderBookGridView.ExpandCollapseToggleStyleKey);
|
||||
toggle_button.SetResourceReference(ToggleButton.StyleProperty, ExpandCollapseToggleStyleKey);
|
||||
//
|
||||
var security_view = new TextBlock { Foreground = font_brush };
|
||||
security_view.SetBinding(TextBlock.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
|
||||
@@ -643,7 +651,6 @@ namespace mbts.spark {
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
grid.Children.Add(toggle_button);
|
||||
grid.Children.Add(security_view);
|
||||
|
||||
grid.VerticalAlignment = VerticalAlignment.Center;
|
||||
//
|
||||
var border = new Border { Child = grid };
|
||||
|
||||
@@ -15,7 +15,7 @@ using System.Windows.Media;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class OrderBookRowVM : DisposableViewModelBase, IEquatable<OrderBookRowVM>, DIYGrid.IRowVM {
|
||||
partial class OrderBookRowVM : DisposableViewModelBase, IEquatable<OrderBookRowVM>, DIYGrid.IRowVM, DIYGrid.IActionableRowVM {
|
||||
const string _unknown_security_price_format_string = "0.#########";
|
||||
const int _unknown_security_price_num_fractional_digits = 9;
|
||||
readonly OrderBookVM _owner;
|
||||
@@ -65,7 +65,6 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public event Action invalidated;
|
||||
public event Action<(OrderSecurityAlgoMeta oldval, OrderSecurityAlgoMeta newval)> updated;
|
||||
|
||||
// Core
|
||||
public OrderSecurityAlgoMeta order_tuple { get; private set; }
|
||||
@@ -143,8 +142,8 @@ namespace mbts.spark {
|
||||
public ICommand toggle_command => _toggle_expanded_cmd ?? (
|
||||
_toggle_expanded_cmd = new DelegateCommand(() => is_expanded = !is_expanded));
|
||||
|
||||
public ICommand show_order_modify_cmd =>
|
||||
_show_order_modify_cmd ?? (_show_order_modify_cmd = new DelegateCommand(() => _owner.show_order_modify(this), () => status == OrderStatus.Working));
|
||||
ICommand DIYGrid.IActionableRowVM.default_command => _show_order_modify_cmd ??
|
||||
(_show_order_modify_cmd = new DelegateCommand(() => _owner.show_order_modify(this), () => status == OrderStatus.Working));
|
||||
|
||||
public ICommand cancel_command => _cancel_cmd ??
|
||||
(_cancel_cmd = new DelegateCommand(() => _owner.cancel_order(this), () => status == OrderStatus.Working));
|
||||
@@ -186,10 +185,10 @@ namespace mbts.spark {
|
||||
public void update(OrderSecurityAlgoMeta order_tuple) {
|
||||
Log.assert(order_tuple.order.Key == this.order.Key, "OrderBookRowVM: update_order was called with an order with the wrong key.");
|
||||
bool had_security = _security.HasValue;
|
||||
var old_order_tuple = this.order_tuple;
|
||||
var old_meta = this.order_tuple.meta;
|
||||
// _show_order_modify_cmd is lazy (instantiated in the default_command getter); it might be null, don't not force instantiation
|
||||
bool? could_modify_execute = _show_order_modify_cmd?.CanExecute(null);
|
||||
OrderRecordImmutable old_order = old_order_tuple.order;
|
||||
OrderRecordImmutable old_order = order;
|
||||
this.order_tuple = order_tuple;
|
||||
// most cells are "render-cells," which don't use databindings - the only way to update such cells is to raise the invalidated event
|
||||
bool invalidate = !(
|
||||
@@ -216,12 +215,10 @@ namespace mbts.spark {
|
||||
// need to raise invalidated to refresh the price cells (the security.ContractSpec determines how they are formatted - Ticks!)
|
||||
invalidate = true;
|
||||
}
|
||||
if (old_order_tuple.meta?.badge_color != order_tuple.meta?.badge_color) {
|
||||
if (old_meta?.badge_color != order_tuple.meta?.badge_color) {
|
||||
_badge_fill = null;
|
||||
RaisePropertyChanged(nameof(badge_fill));
|
||||
}
|
||||
if (old_order_tuple.display_name() != order_tuple.display_name())
|
||||
RaisePropertyChanged(nameof(display_name));
|
||||
if (old_order.status() != order.status()) {
|
||||
invalidate = true;
|
||||
RaisePropertyChanged(nameof(status));
|
||||
@@ -231,7 +228,6 @@ namespace mbts.spark {
|
||||
}
|
||||
if (could_modify_execute.HasValue && could_modify_execute.Value != _show_order_modify_cmd.CanExecute(null))
|
||||
_show_order_modify_cmd.RaiseCanExecuteChanged();
|
||||
updated?.Invoke((old_order_tuple, order_tuple));
|
||||
if (invalidate)
|
||||
raise_invalidated();
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace mbts.spark {
|
||||
}),
|
||||
_columns,
|
||||
_current_row.SubscribeHookLatest(
|
||||
r => r.newval.subscribe_data().Subscribe(
|
||||
r => r.newval?.subscribe_data().Subscribe(
|
||||
data => selected_order_changed?.Invoke(data.order)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
<UserControl x:Class="mbts.spark.OrderBookView" x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:uc="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:order="clr-namespace:bts.api.orderman;assembly=spark_api"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600" d:DesignWidth="1080"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<uspcv:TickDisplayConverter x:Key="TickDisplayConverter"/>
|
||||
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
|
||||
<DataTemplate x:Key="{x:Static sp:OrderBookView.SubTypeFilterTemplateKey}">
|
||||
<usp:FilterContentControl
|
||||
ApplyCommandParameter="{Binding}"
|
||||
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
|
||||
ClearCommandParameter="{Binding}"
|
||||
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
|
||||
>
|
||||
<c:ListBoxEx
|
||||
ItemsSource="{Binding items}"
|
||||
Selection="{Binding selected_items}"
|
||||
SelectionMode="Multiple"
|
||||
ItemContainerStyle="{StaticResource ListBoxItemCheckBoxStyle}"
|
||||
>
|
||||
<c:ListBoxEx.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource OrderSubTypeLongDisplayTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</c:ListBoxEx.ItemTemplate>
|
||||
</c:ListBoxEx>
|
||||
</usp:FilterContentControl>
|
||||
</DataTemplate>
|
||||
|
||||
<Style x:Key="{x:Static sp:OrderBookView.RowStyleKey}" TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{uc:Enum Working, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={uc:Nested uc:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<DataTemplate x:Key="{x:Static sp:OrderBookView.RowTemplateKey}">
|
||||
<sp:OrderBookRowView/>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:DIYGrid
|
||||
x:Name="_grid"
|
||||
Grid.Row="0"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Rows="{Binding rows}"
|
||||
RowStyle="{StaticResource {x:Static sp:OrderBookView.RowStyleKey}}"
|
||||
RowTemplate="{StaticResource {x:Static sp:OrderBookView.RowTemplateKey}}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
|
||||
>
|
||||
<c:DIYGrid.Resources>
|
||||
<ResourceDictionary>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
|
||||
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
|
||||
<Style x:Key="{x:Static sp:OrderBookView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Border Background="Transparent" Height="16" Padding="0,5,5,5" Width="16">
|
||||
<Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="RenderTransform" TargetName="ExpandPath">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
<Condition Property="IsChecked" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToolTip}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="Content" Value="{x:Static sys:String.Empty}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="Content" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</c:DIYGrid.Resources>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
</c:DIYGrid.InputBindings>
|
||||
<c:DIYGrid.CommandBindings>
|
||||
<CommandBinding
|
||||
Command="{x:Static c:DIYGrid.DefaultRowCommand}"
|
||||
Executed="{x:Static c:DIYGrid.DefaultRowActionExecuted}"
|
||||
CanExecute="{x:Static c:DIYGrid.DefaultRowActionCanExecute}"
|
||||
/>
|
||||
</c:DIYGrid.CommandBindings>
|
||||
</c:DIYGrid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<TextBlock>
|
||||
<Run Text="Orders:"/>
|
||||
<Run Text="{Binding rows.Count, Mode=OneWay}" />
|
||||
<Run Text="of"/>
|
||||
<Run Text="{Binding rows.total_row_count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
+9
-33
@@ -1,7 +1,11 @@
|
||||
using mbts.ui.controls;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class OrderBookGridView : DIYGrid {
|
||||
internal partial class OrderBookView : UserControl {
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum Columns { Security, Symbol, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Cancel, EditableQty, EditablePrice }
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
@@ -10,41 +14,13 @@ namespace mbts.spark {
|
||||
CVRowStyleKey = "CVRowStyle",
|
||||
CVRowTemplateKey = "CVRowTemplateKey";
|
||||
|
||||
// IMPORTANT: these enums are stored as strings in the general-purpose GridColumnSettings proto, and are used to ID THE COLUMNS!
|
||||
// Do NOT RENAME them! However, if you must, be sure to write an IShellWindowMigrationRule or something to account for backwards compatability.
|
||||
public enum ColumnKeys {
|
||||
Security,
|
||||
Symbol,
|
||||
Time,
|
||||
LastChange,
|
||||
User,
|
||||
Account,
|
||||
Exchange,
|
||||
TimeInForce,
|
||||
OrderType,
|
||||
SubType,
|
||||
Side,
|
||||
Size,
|
||||
Price,
|
||||
Filled,
|
||||
AvgFilled,
|
||||
LastPrice,
|
||||
Status,
|
||||
LastMessage,
|
||||
SecurityKey,
|
||||
OrderKey,
|
||||
ParentKey,
|
||||
AttachedTargetOrderKey,
|
||||
Cancel,
|
||||
EditableQty,
|
||||
EditablePrice
|
||||
}
|
||||
|
||||
public OrderBookGridView() =>
|
||||
public OrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
DataContext = vm;
|
||||
}
|
||||
}
|
||||
|
||||
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookGridView.ColumnKeys> {
|
||||
class OrderBookRowView : DIYGrid.HighPerformanceRow<OrderBookRowVM, OrderBookView.Columns> {
|
||||
public OrderBookRowView() : base() { }
|
||||
}
|
||||
}
|
||||
@@ -2,31 +2,27 @@
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using bts.view;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
//using IOrderBookWSM = mbts.persist.IWindowSettingsManager<mbts.spark.GridSettingsWithNotifications<mbts.spark.OrderRecordTuple, mbts.OrderRecordTuplePredicate>>;
|
||||
using OrderBookGridSettings = bts.shell.OrderBookWindowFactory.OrderBookGridSettings;
|
||||
using OrderBookGridSettingsVM = mbts.spark.GridSettingsVM<bts.api.orderman.OrderSecurity, mbts.OrderRecordTuplePredicate, bts.shell.OrderBookWindowFactory.OrderBookGridSettings>;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal class OrderBookWindow : SparkWindow<OrderBookWindowFactory.OrderBookStartupData>, ISharedWindow {
|
||||
const string _window_type = "OrderBook", _default_title = "Order Book";
|
||||
const string _window_type = "OrderBook", ORDERBOOK_THREAD_ID = "OrderBook", _default_title = "Order Book";
|
||||
readonly INotificationService _notification_service;
|
||||
readonly IWindowSettingsManager<OrderBookGridSettings> _settings_manager;
|
||||
IDisposable _attachment_exit_sub;
|
||||
OrderBookWindowFactory.OrderBookStartupData _state;
|
||||
OrdersModel _model;
|
||||
OrderBookVM _vm;
|
||||
@@ -34,6 +30,17 @@ namespace mbts.spark {
|
||||
OrderBookWindowFactory _factory;
|
||||
SparkCommand[] _commands;
|
||||
|
||||
SparkCommand _show_fills_command
|
||||
, _modify_order_command
|
||||
, _cancel_buy_orders_command
|
||||
, _cancel_sell_orders_command
|
||||
, _cancel_all_orders_command
|
||||
, _cancel_selected_orders_command
|
||||
, _recreate_selected_orders_command
|
||||
, _toggle_collapse_all_command
|
||||
, _toggle_expand_all_command
|
||||
, _show_grid_settings_command;
|
||||
|
||||
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, INotificationService notification_service,
|
||||
OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon, IWindowSettingsManager<OrderBookGridSettings> settings_manager)
|
||||
: base(_default_title, bus, factory, SparkCommands.ShowOrders.icon) {
|
||||
@@ -42,7 +49,6 @@ namespace mbts.spark {
|
||||
_notification_service = notification_service;
|
||||
_state = state;
|
||||
_settings_manager = settings_manager;
|
||||
thread_id = config.Configuration.get<bts.config.MultiThreadedWindows>().order_book ? "OrderBook" : null;
|
||||
}
|
||||
|
||||
public override OrderBookWindowFactory.OrderBookStartupData SaveState => _state = _vm?.create();
|
||||
@@ -55,11 +61,10 @@ namespace mbts.spark {
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH
|
||||
+ EXTRA_FOR_NO_HORIZONTAL_SCROLLBAR_WIDTH;
|
||||
public string thread_id { get; }
|
||||
public string thread_id => ORDERBOOK_THREAD_ID;
|
||||
|
||||
public override void Dispose() {
|
||||
_commands.OfType<IDisposable>().DisposeAll();
|
||||
Disposable.dispose(ref _attachment_exit_sub);
|
||||
Disposable.dispose(ref _vm);
|
||||
}
|
||||
|
||||
@@ -68,15 +73,19 @@ namespace mbts.spark {
|
||||
_vm.load_from(_state);
|
||||
_view = new OrderBookView(_vm);
|
||||
|
||||
var show_fills_command = SparkCommands.ShowFills.complete(() => new FillsWindowFactory.StartupData(_vm.selected_orders.Select(r => r.Key).ToArray()));
|
||||
_show_fills_command = SparkCommands.ShowFills.complete(() => new FillsWindowFactory.StartupData(_vm.selected_orders.Select(r => r.Key).ToArray()));
|
||||
|
||||
var toggle_right_panel_is_visible_command = new SparkCommandCommand(
|
||||
get_toggle_algo_editor_cmd_title(),
|
||||
Icons.ModifyOrder,
|
||||
_vm.toggle_algo_editor_is_visible_cmd,
|
||||
new KeyGesture(Key.M, ModifierKeys.Control | ModifierKeys.Shift));
|
||||
_modify_order_command = SparkCommands.ShowModifyOrder.complete(
|
||||
() => _vm.current_order.Value.Key,
|
||||
can_execute: () => {
|
||||
if (_vm.current_order.HasValue) {
|
||||
return _vm.current_order.Value.State.Status == bts.api.orderman.OrderStatus.Working;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
var cancel_buy_orders_command = SparkCommands.CancelBuyOrders.complete(
|
||||
_cancel_buy_orders_command = SparkCommands.CancelBuyOrders.complete(
|
||||
_model.working_orders.Select(os => os.Values.Where(o => o.order.side() == Side.Buy).Select(o => o.order.Key)),
|
||||
wbids => wbids.Any(),
|
||||
() => {
|
||||
@@ -89,7 +98,7 @@ namespace mbts.spark {
|
||||
}
|
||||
);
|
||||
|
||||
var cancel_sell_orders_command = SparkCommands.CancelSellOrders.complete(
|
||||
_cancel_sell_orders_command = SparkCommands.CancelSellOrders.complete(
|
||||
_model.working_orders.Select(os => os.Values.Where(o => o.order.side() == Side.Sell).Select(o => o.order.Key)),
|
||||
wasks => wasks.Any(),
|
||||
() => {
|
||||
@@ -102,7 +111,7 @@ namespace mbts.spark {
|
||||
}
|
||||
);
|
||||
|
||||
var cancel_all_orders_command = SparkCommands.CancelOrders.complete(
|
||||
_cancel_all_orders_command = SparkCommands.CancelOrders.complete(
|
||||
_model.working_orders.Select(os => os.Select(o => o.Key)),
|
||||
worders => worders.Any(),
|
||||
() => {
|
||||
@@ -115,39 +124,27 @@ namespace mbts.spark {
|
||||
}
|
||||
);
|
||||
|
||||
var cancel_selected_orders_command = SparkCommands.CancelSelectedOrders.complete(
|
||||
_cancel_selected_orders_command = SparkCommands.CancelSelectedOrders.complete(
|
||||
() => _vm.selected_orders.Where(x => x.status() == OrderStatus.Working).Select(x => x.Key),
|
||||
can_execute: () => _vm.selected_orders.Any(x => x.status() == OrderStatus.Working)
|
||||
);
|
||||
|
||||
var recreate_selected_orders_command = SparkCommands.RecreateSelectedOrders.complete(
|
||||
_recreate_selected_orders_command = SparkCommands.RecreateSelectedOrders.complete(
|
||||
() =>
|
||||
_vm.selected_orders.Where(x => passes_recreate_search_criteria(x))
|
||||
_vm.selected_orders.Where(x => _passes_recreate_search_criteria(x))
|
||||
.Select(x => (x.Def, x.State.Price, x.State.Quantity - x.State.FilledQuantity)),
|
||||
can_execute: () => _vm.selected_orders.Any(x => passes_recreate_search_criteria(x))
|
||||
can_execute: () => _vm.selected_orders.Any(x => _passes_recreate_search_criteria(x))
|
||||
);
|
||||
|
||||
var attach_selected_orders_command = new SparkCommandCommand(
|
||||
"Attach To Selected Orders",
|
||||
Icons.AttachAlgos,
|
||||
new DelegateCommand(_vm.begin_attach_algos, () => _vm.selected_rows.Count > 0),
|
||||
new KeyGesture(Key.A, ModifierKeys.Control | ModifierKeys.Shift));
|
||||
|
||||
var detach_selected_orders_command = new SparkCommandCommand(
|
||||
"Detach And Cancel Selected Orders",
|
||||
Icons.DetachAlgos,
|
||||
new DelegateCommand(_vm.detach_selected_algos, () => _vm.selected_rows.Count > 0),
|
||||
new KeyGesture(Key.D, ModifierKeys.Control | ModifierKeys.Shift));
|
||||
|
||||
var toggle_collapse_all_command = new SparkCommandCommand("Collapse All",
|
||||
_toggle_collapse_all_command = new SparkCommandCommand("Collapse All",
|
||||
Icons.RightArrow,
|
||||
_vm.collapse_all_rows_command);
|
||||
|
||||
var toggle_expand_all_command = new SparkCommandCommand("Expand All",
|
||||
_toggle_expand_all_command = new SparkCommandCommand("Expand All",
|
||||
Icons.DownArrow,
|
||||
_vm.expand_all_rows_command);
|
||||
|
||||
var show_grid_settings_command = SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
_show_grid_settings_command = SparkCommands.window_settings_command(new DialogCommand(() => {
|
||||
var gs_vm = new WindowSettingsDialogVM<OrderBookGridSettings, OrderBookGridSettingsVM>(
|
||||
_settings_manager,
|
||||
us => us.order_book_grid,
|
||||
@@ -172,44 +169,31 @@ namespace mbts.spark {
|
||||
return dialog;
|
||||
}));
|
||||
|
||||
_commands = new SparkCommand[] {
|
||||
show_fills_command,
|
||||
toggle_right_panel_is_visible_command,
|
||||
cancel_selected_orders_command,
|
||||
cancel_buy_orders_command,
|
||||
cancel_sell_orders_command,
|
||||
cancel_all_orders_command,
|
||||
recreate_selected_orders_command,
|
||||
attach_selected_orders_command,
|
||||
detach_selected_orders_command,
|
||||
toggle_collapse_all_command,
|
||||
toggle_expand_all_command,
|
||||
show_grid_settings_command
|
||||
_commands = new SparkCommand[] {
|
||||
_show_fills_command,
|
||||
_modify_order_command,
|
||||
_cancel_selected_orders_command,
|
||||
_cancel_buy_orders_command,
|
||||
_cancel_sell_orders_command,
|
||||
_cancel_all_orders_command,
|
||||
_recreate_selected_orders_command,
|
||||
_toggle_collapse_all_command,
|
||||
_toggle_expand_all_command,
|
||||
_show_grid_settings_command
|
||||
};
|
||||
_vm.as_demuxer().subscribe<bool>(nameof(OrderBookVM.algo_editor_is_visible))
|
||||
.Subscribe(_ => toggle_right_panel_is_visible_command.title = get_toggle_algo_editor_cmd_title());
|
||||
_vm.selected_order_changed += vm_selected_order_changed;
|
||||
// local methods
|
||||
string get_toggle_algo_editor_cmd_title() =>
|
||||
$"{(_vm.algo_editor_is_visible ? "Hide" : "Show")} Order Params";
|
||||
|
||||
bool passes_recreate_search_criteria(OrderRecordImmutable record) =>
|
||||
(record.status() == OrderStatus.Canceled || record.status() == OrderStatus.Error) &&
|
||||
!record.has_parent() &&
|
||||
record.State.Quantity > record.State.FilledQuantity;
|
||||
|
||||
void vm_selected_order_changed(OrderRecordImmutable? e) {
|
||||
cancel_selected_orders_command.raiseCanExecuteChanged();
|
||||
recreate_selected_orders_command.raiseCanExecuteChanged();
|
||||
attach_selected_orders_command.raiseCanExecuteChanged();
|
||||
detach_selected_orders_command.raiseCanExecuteChanged();
|
||||
}
|
||||
_vm.selected_order_changed += _vm_selected_order_changed;
|
||||
}
|
||||
|
||||
protected override void OnParentViewCreated(SparkWindowView parent_view) =>
|
||||
Disposable.replace(ref _attachment_exit_sub, parent_view.SubscribePreviewKeyDown((s, e) => {
|
||||
if (e.Key == Key.Escape || (e.Key == Key.Z && KeyboardExtensions.IsCtrlDown()))
|
||||
_vm.is_attaching_algos = false;
|
||||
}));
|
||||
private bool _passes_recreate_search_criteria(OrderRecordImmutable record) {
|
||||
return (record.status() == OrderStatus.Canceled || record.status() == OrderStatus.Error)
|
||||
&& !record.has_parent()
|
||||
&& record.State.Quantity > record.State.FilledQuantity;
|
||||
}
|
||||
|
||||
private void _vm_selected_order_changed(OrderRecordImmutable? e) {
|
||||
_modify_order_command.raiseCanExecuteChanged();
|
||||
_cancel_selected_orders_command.raiseCanExecuteChanged();
|
||||
_recreate_selected_orders_command.raiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,10 @@ using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
|
||||
using static mbts.spark.OrderBookView;
|
||||
|
||||
namespace bts.shell {
|
||||
internal class OrderBookWindowFactory : ShellWindowFactory<OrderBookWindowFactory.OrderBookStartupData> {
|
||||
@@ -50,57 +49,24 @@ namespace bts.shell {
|
||||
|
||||
[ProtoContract]
|
||||
public class OrderBookStartupData {
|
||||
const bool _default_algo_editor_is_visible = true;
|
||||
const double
|
||||
_default_right_panel_width = 400,
|
||||
_default_fills_slave_grid_height = 138;
|
||||
// ALREADY USED TAGS
|
||||
// [ProtoMember(5)]
|
||||
// public readonly List<SortingCriteria> sort_criteria;
|
||||
|
||||
// filters
|
||||
//[ProtoMember(5)]
|
||||
//public readonly List<SortingCriteria> sort_criteria;
|
||||
[ProtoMember(10)]
|
||||
public FilterStates filters;
|
||||
// grid
|
||||
[ProtoMember(15)]
|
||||
[Obsolete]
|
||||
public GridSettings grid;
|
||||
// algo editor is visible
|
||||
[ProtoMember(16)]
|
||||
[DefaultValue(_default_algo_editor_is_visible)]
|
||||
public bool algo_editor_is_visible { get; set; }
|
||||
// right panel width
|
||||
[ProtoMember(17)]
|
||||
[DefaultValue(_default_right_panel_width)]
|
||||
public double right_panel_width { get; set; }
|
||||
// fills slave grid height
|
||||
[ProtoMember(18)]
|
||||
[DefaultValue(_default_fills_slave_grid_height)]
|
||||
public double fills_slave_grid_height { get; set; }
|
||||
// fills slave grid is visible
|
||||
[ProtoMember(19)]
|
||||
public bool fills_slave_grid_is_visible { get; set; }
|
||||
// predicate animations
|
||||
[ProtoMember(20)]
|
||||
[Obsolete]
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
// use defaults
|
||||
[ProtoMember(25)]
|
||||
public bool use_defaults;
|
||||
|
||||
public OrderBookStartupData() {
|
||||
algo_editor_is_visible = _default_algo_editor_is_visible;
|
||||
right_panel_width = _default_right_panel_width;
|
||||
fills_slave_grid_height = _default_fills_slave_grid_height;
|
||||
}
|
||||
public OrderBookStartupData() { }
|
||||
|
||||
public OrderBookStartupData(FilterStates filters, GridSettings grid = default, bool algo_editor_is_visible = false, double right_panel_width = 0, double fills_slave_grid_height = 0, bool fills_slave_grid_is_visible = false, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null, bool use_defaults = false) {
|
||||
public OrderBookStartupData(FilterStates filters, GridSettings grid = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null, bool use_defaults = false) {
|
||||
this.filters = filters;
|
||||
this.use_defaults = use_defaults;
|
||||
this.algo_editor_is_visible = algo_editor_is_visible;
|
||||
this.right_panel_width = right_panel_width;
|
||||
this.fills_slave_grid_height = fills_slave_grid_height;
|
||||
this.fills_slave_grid_is_visible = fills_slave_grid_is_visible;
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
this.grid = grid;
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
@@ -117,12 +83,9 @@ namespace bts.shell {
|
||||
|
||||
public static OrderBookStartupData Default =>
|
||||
new OrderBookStartupData(
|
||||
new FilterStates { statuses = new[] { OrderStatus.Working } },
|
||||
_default_general_grid,
|
||||
_default_algo_editor_is_visible,
|
||||
_default_right_panel_width,
|
||||
_default_fills_slave_grid_height,
|
||||
use_defaults: true);
|
||||
new FilterStates {
|
||||
statuses = new[] { OrderStatus.Working }
|
||||
}, _default_general_grid, use_defaults: true);
|
||||
|
||||
public static OrderBookGridSettings DefaultGrid => new OrderBookGridSettings(_default_general_grid);
|
||||
|
||||
@@ -152,7 +115,6 @@ namespace bts.shell {
|
||||
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.ToString(), DIYGrid.DefaultColumnHeaders.SecurityKey, is_visible: false, width: DIYGrid.DefaultColumnWidths.SecurityKey);
|
||||
public static GridColumnSettings order_key => new GridColumnSettings(Columns.OrderKey.ToString(), DIYGrid.DefaultColumnHeaders.OrderKey, width: DIYGrid.DefaultColumnWidths.OrderKey);
|
||||
public static GridColumnSettings parent_key => new GridColumnSettings(Columns.ParentKey.ToString(), "Parent Key", width: DIYGrid.DefaultColumnWidths.ParentKey);
|
||||
public static GridColumnSettings attached_target_order_key => new GridColumnSettings(Columns.AttachedTargetOrderKey.ToString(), DIYGrid.DefaultColumnHeaders.AttachedTargetOrderKey, width: DIYGrid.DefaultColumnWidths.ParentKey);
|
||||
|
||||
public static IEnumerable<SortingCriteria> sorting_criterion {
|
||||
get {
|
||||
@@ -183,12 +145,11 @@ namespace bts.shell {
|
||||
yield return security_key;
|
||||
yield return order_key;
|
||||
yield return parent_key;
|
||||
yield return attached_target_order_key;
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<GridColumnSettings> _mini_mode_columns_to_hide = new HashSet<GridColumnSettings> {
|
||||
time_created, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key, attached_target_order_key
|
||||
time_created, exchange, sub_type, quantity, price, avg_price, last_price, status, last_message, security_key, order_key, parent_key
|
||||
};
|
||||
|
||||
public static IEnumerable<GridColumnSettings> mini {
|
||||
@@ -247,17 +208,11 @@ namespace bts.shell {
|
||||
public api.orderman.OrderSubType[] order_sub_types;
|
||||
[ProtoMember(20)]
|
||||
public string[] symbols;
|
||||
[ProtoMember(21)]
|
||||
public string parent_key_regex;
|
||||
[ProtoMember(22)]
|
||||
public string attached_target_order_key_regex;
|
||||
|
||||
public bool Equals(FilterStates other) =>
|
||||
other != null &&
|
||||
security_criteria.safe_equals(other.security_criteria) &&
|
||||
order_key_regex == other.order_key_regex &&
|
||||
parent_key_regex == other.parent_key_regex &&
|
||||
attached_target_order_key_regex == other.attached_target_order_key_regex &&
|
||||
time_created.Equals(other.time_created) &&
|
||||
EqualityComparer.safe_sequence_equals(users, other.users) &&
|
||||
EqualityComparer.safe_sequence_equals(accounts, other.accounts) &&
|
||||
@@ -275,32 +230,6 @@ namespace bts.shell {
|
||||
last_change.Equals(other.last_change) &&
|
||||
EqualityComparer.safe_sequence_equals(order_sub_types, other.order_sub_types) &&
|
||||
EqualityComparer.safe_sequence_equals(symbols, other.symbols);
|
||||
|
||||
public override bool Equals(object obj) => Equals(obj as FilterStates);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
security_criteria?.GetHashCode() ?? 0,
|
||||
order_key_regex?.GetHashCode() ?? 0,
|
||||
parent_key_regex?.GetHashCode() ?? 0,
|
||||
attached_target_order_key_regex?.GetHashCode() ?? 0,
|
||||
time_created.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(users),
|
||||
EqualityComparer.safe_aggregate_hash(accounts),
|
||||
EqualityComparer.safe_aggregate_hash(exchanges),
|
||||
EqualityComparer.safe_aggregate_hash(tifs),
|
||||
EqualityComparer.safe_aggregate_hash(order_types),
|
||||
EqualityComparer.safe_aggregate_hash(sides),
|
||||
qty?.GetHashCode() ?? 0,
|
||||
price?.GetHashCode() ?? 0,
|
||||
qty_filled?.GetHashCode() ?? 0,
|
||||
avg_price?.GetHashCode() ?? 0,
|
||||
last_price?.GetHashCode() ?? 0,
|
||||
EqualityComparer.safe_aggregate_hash(statuses),
|
||||
exchange_message_regex?.GetHashCode() ?? 0,
|
||||
last_change.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(order_sub_types),
|
||||
EqualityComparer.safe_aggregate_hash(symbols));
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
@@ -311,7 +240,7 @@ namespace bts.shell {
|
||||
List<OrderRecordTuplePredicateAnimations> _predicate_animations;
|
||||
|
||||
public OrderBookGridSettings() { }
|
||||
public OrderBookGridSettings(GridSettings general = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null, double right_panel_width = double.NaN) {
|
||||
public OrderBookGridSettings(GridSettings general = default, IEnumerable<OrderRecordTuplePredicateAnimations> predicate_animations = null) {
|
||||
this.general = general;
|
||||
_predicate_animations = new List<OrderRecordTuplePredicateAnimations>();
|
||||
if (predicate_animations != null)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.controls.adorners;
|
||||
using mbts.ui.extensions;
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class OrderBookView {
|
||||
public class OrderBookAttachmentTargetAdorner : DIYGridRowHighlighterAdorner, IDisposable {
|
||||
IDisposable _attach_op;
|
||||
bool _is_active, _is_disposed;
|
||||
|
||||
#region IsActive
|
||||
public static readonly DependencyProperty IsActiveProperty =
|
||||
WPFHelper.create_dp<bool, OrderBookAttachmentTargetAdorner>(
|
||||
nameof(IsActive),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => {
|
||||
o._is_active = v;
|
||||
Disposable.dispose(ref o._attach_op);
|
||||
if (v && !o._is_disposed) {
|
||||
o.IsHitTestVisible = true;
|
||||
o._attach_op = Disposable.Create(
|
||||
o.SubscribePreviewMouseDown((s, e) => {
|
||||
if (e.ChangedButton == MouseButton.Left) {
|
||||
if (e.ClickCount > 1 && o.TargetRow is OrderBookRowVM r) {
|
||||
e.Handled = true;
|
||||
o._raise_target_row_selected(r);
|
||||
}
|
||||
}
|
||||
}),
|
||||
o.AdornedElement.SubscribeMouseHoverRow<OrderBookRowVM>().Subscribe(row => o.TargetRow = row));
|
||||
} else {
|
||||
o.IsHitTestVisible = false;
|
||||
o.TargetRow = null;
|
||||
}
|
||||
},
|
||||
false);
|
||||
|
||||
public bool IsActive {
|
||||
get => (bool)GetValue(IsActiveProperty);
|
||||
set => SetValue(IsActiveProperty, value);
|
||||
}
|
||||
|
||||
public event Action<OrderBookRowVM> TargetRowSelected;
|
||||
#endregion
|
||||
|
||||
public OrderBookAttachmentTargetAdorner(DIYGrid grid) : base(grid) { }
|
||||
|
||||
public void Dispose() {
|
||||
if (!_is_disposed) {
|
||||
_is_disposed = true;
|
||||
Disposable.dispose(ref _attach_op);
|
||||
}
|
||||
}
|
||||
|
||||
void _raise_target_row_selected(OrderBookRowVM row) => TargetRowSelected?.Invoke(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
<c:DIYGrid
|
||||
x:Class="mbts.spark.OrderBookGridView"
|
||||
x:ClassModifier="internal"
|
||||
x:Name="_grid"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:order="clr-namespace:bts.api.orderman;assembly=spark_api"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<c:DIYGrid.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!--Primitive Resources (Brushes, Colors, etc.)-->
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
|
||||
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
|
||||
<!--Converters-->
|
||||
<uspcv:OrderSubTypeDisplayTextConverter x:Key="OrderSubTypeLongDisplayTextConverter" UseLongName="True" />
|
||||
<!--DataTemplates-->
|
||||
<DataTemplate x:Key="{x:Static sp:OrderBookGridView.SubTypeFilterTemplateKey}">
|
||||
<usp:FilterContentControl
|
||||
ApplyCommandParameter="{Binding}"
|
||||
ApplyCommand="{x:Static c:DataGridFilterProperties.CommitCommand}"
|
||||
ClearCommandParameter="{Binding}"
|
||||
ClearCommand="{x:Static c:DataGridFilterProperties.ClearCommand}"
|
||||
>
|
||||
<c:ListBoxEx
|
||||
ItemsSource="{Binding items}"
|
||||
Selection="{Binding selected_items}"
|
||||
SelectionMode="Multiple"
|
||||
ItemContainerStyle="{StaticResource ListBoxItemCheckBoxStyle}"
|
||||
>
|
||||
<c:ListBoxEx.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource OrderSubTypeLongDisplayTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
</c:ListBoxEx.ItemTemplate>
|
||||
</c:ListBoxEx>
|
||||
</usp:FilterContentControl>
|
||||
</DataTemplate>
|
||||
<!--Styles-->
|
||||
<Style x:Key="{x:Static sp:OrderBookGridView.ExpandCollapseToggleStyleKey}" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="Width" Value="16"/>
|
||||
<Setter Property="Height" Value="16"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Border Background="Transparent" Height="16" Padding="0,5,5,5" Width="16">
|
||||
<Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter Property="RenderTransform" TargetName="ExpandPath">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="180" CenterY="3" CenterX="3"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"/>
|
||||
<Condition Property="IsChecked" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
|
||||
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ToolTip}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="Content" Value="{x:Static sys:String.Empty}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="Content" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</c:DIYGrid.Resources>
|
||||
<c:DIYGrid.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
<KeyBinding Key="Return" Command="{x:Static c:DIYGrid.DefaultRowCommand}"/>
|
||||
</c:DIYGrid.InputBindings>
|
||||
<c:DIYGrid.RowStyle>
|
||||
<Style TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}">
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
|
||||
<Condition Binding="{Binding status}" Value="{u:Enum Working, Type=order:OrderStatus}"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Background" Value="{x:Static Member=working_order_background_brush, MemberType={u:Nested u:BTSColors.Orders}}"/>
|
||||
</MultiDataTrigger.Setters>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</c:DIYGrid.RowStyle>
|
||||
<c:DIYGrid.RowTemplate>
|
||||
<DataTemplate>
|
||||
<sp:OrderBookRowView/>
|
||||
</DataTemplate>
|
||||
</c:DIYGrid.RowTemplate>
|
||||
</c:DIYGrid>
|
||||
@@ -1,407 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="mbts.spark.OrderBookView"
|
||||
x:ClassModifier="internal"
|
||||
x:Name="Root"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
Grid.Row="0"
|
||||
FocusManager.IsFocusScope="True"
|
||||
Background="{StaticResource CommonBackground}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DataGridEx/Filters/Views/FilterTemplates.xaml"/>
|
||||
<ResourceDictionary Source="/Modules/Trading/OrderBook/View/OrderParamsPanel.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<sys:Double x:Key="GridSplitterWidth">5</sys:Double>
|
||||
<sys:Double x:Key="FillsSlaveGridMinHeight">64</sys:Double>
|
||||
<SolidColorBrush x:Key="AlgoAttachmentBackground" Color="#55009500"/>
|
||||
<cv:GridLengthConverter x:Key="GridLengthPixelConverter" UnitType="Pixel"/>
|
||||
<Style TargetType="GridSplitter">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{StaticResource Control.MediumGrayBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Control.MediumGrayBrush}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.BestBidPriceButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<ToolTip DataContext="{Binding PlacementTarget.Parent.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<TextBlock Text="{Binding security, StringFormat='Best Bid Price ({0})'}"/>
|
||||
</ToolTip>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.BestBidSizeButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Command" Value="{Binding DataContext.size_calculator.set_value_command, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding BestBidSize, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<ToolTip DataContext="{Binding PlacementTarget.Parent.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<TextBlock Text="{Binding security, StringFormat='Best Bid Size ({0})'}"/>
|
||||
</ToolTip>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.BestAskPriceButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<ToolTip DataContext="{Binding PlacementTarget.Parent.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<TextBlock Text="{Binding security, StringFormat='Best Ask Price ({0})'}"/>
|
||||
</ToolTip>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.BestAskSizeButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Command" Value="{Binding DataContext.size_calculator.set_value_command, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding BestAskSize, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="ToolTip">
|
||||
<Setter.Value>
|
||||
<ToolTip DataContext="{Binding PlacementTarget.Parent.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
<TextBlock Text="{Binding security, StringFormat='Best Ask Size ({0})'}"/>
|
||||
</ToolTip>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="{x:Static usp:OrderParamsPanel.SizeCalculatorButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Command" Value="{Binding DataContext.size_calculator.click, RelativeSource={RelativeSource AncestorType=usp:OrderParamsPanel}}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition>
|
||||
<MultiBinding Converter="{x:Static sp:OrderBookView.RightPanelVisualStateConverter}">
|
||||
<Binding Path="DataContext.algo_editor_is_visible" Source="{u:DirectReference ContentRoot}"/>
|
||||
<Binding Path="DataContext.is_attaching_algos" Source="{u:DirectReference ContentRoot}"/>
|
||||
</MultiBinding>
|
||||
</u:StateDefinition>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested sp:OrderBookView.VisualStateNames.RightPanel}}">
|
||||
<!--Normal State-->
|
||||
<VisualState Name="{x:Static Normal, MemberType={u:Nested sp:OrderBookView.VisualStateNames.RightPanel}}"/>
|
||||
<!--Attach Orders State-->
|
||||
<VisualState Name="{x:Static AttachOrders, MemberType={u:Nested sp:OrderBookView.VisualStateNames.RightPanel}}">
|
||||
<Storyboard>
|
||||
<!--Orders Grid-->
|
||||
<Int32Animation Storyboard.TargetName="orders_grid" Storyboard.TargetProperty="(Grid.ColumnSpan)" BeginTime="0" Duration="0" To="1"/>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="orders_grid" Storyboard.TargetProperty="(FrameworkElement.Cursor)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Cursors.Pen}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="attachment_grid_overlay_border" Storyboard.TargetProperty="(UIElement.Opacity)">
|
||||
<DiscreteDoubleKeyFrame KeyTime="0" Value="1"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<!--Right Panel-->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="right_panel_host" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="right_panel_width_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<!--Modify Orders Controls-->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="order_params_group_box" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="modify_orders_button" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<!--Fills Slave Grid-->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="fills_toggle_hyperlink" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<!--Attach Orders Controls-->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="attach_orders_dialog_skirt" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="attachment_content_root" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<!--Modify Orders State-->
|
||||
<VisualState Name="{x:Static ModifyOrders, MemberType={u:Nested sp:OrderBookView.VisualStateNames.RightPanel}}">
|
||||
<Storyboard>
|
||||
<!--Orders Grid-->
|
||||
<Int32Animation Storyboard.TargetName="orders_grid" Storyboard.TargetProperty="(Grid.ColumnSpan)" BeginTime="0" Duration="0" To="1"/>
|
||||
<!--Right Panel-->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="right_panel_host" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="right_panel_width_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="{Binding right_panel_width, Converter={StaticResource GridLengthPixelConverter}, Mode=TwoWay}"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<sp:OrderBookGridView
|
||||
x:Name="orders_grid"
|
||||
Grid.ColumnSpan="2"
|
||||
Background="Transparent"
|
||||
Columns="{Binding columns}"
|
||||
CurrentRow="{Binding current_row, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Rows="{Binding rows}"
|
||||
SelectionHighlight="{Binding grid_settings_manager.selection_highlight, Mode=OneWay}"
|
||||
Cursor="Arrow"
|
||||
/>
|
||||
<Border
|
||||
x:Name="attachment_grid_overlay_border"
|
||||
Panel.ZIndex="1"
|
||||
Width="{Binding ActualWidth, ElementName=orders_grid}"
|
||||
Height="{Binding ActualHeight, ElementName=orders_grid}"
|
||||
Background="{StaticResource AlgoAttachmentBackground}"
|
||||
Opacity="0"
|
||||
IsHitTestVisible="False"
|
||||
/>
|
||||
<GridSplitter
|
||||
x:Name="right_panel_width_splitter"
|
||||
Grid.Column="1"
|
||||
Panel.ZIndex="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Stretch"
|
||||
Width="{StaticResource GridSplitterWidth}"
|
||||
BorderThickness="1,0,0,0"
|
||||
Visibility="Collapsed"
|
||||
/>
|
||||
<Grid x:Name="right_panel_host" Grid.Column="1" Visibility="Collapsed">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="right_panel_content_root">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference right_panel_content_root}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition>
|
||||
<MultiBinding Converter="{x:Static sp:OrderBookView.FillsSlaveGridStateConverter}">
|
||||
<Binding Path="DataContext.fills_slave_grid_is_visible" Source="{u:DirectReference right_panel_content_root}"/>
|
||||
<Binding Path="DataContext.is_attaching_algos" Source="{u:DirectReference right_panel_content_root}"/>
|
||||
</MultiBinding>
|
||||
</u:StateDefinition>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested sp:OrderBookView.VisualStateNames.FillsPanel}}">
|
||||
<VisualState Name="{x:Static Active, MemberType={u:Nested sp:OrderBookView.VisualStateNames.FillsPanel}}">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="fills_grid" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="fills_order_params_splitter" Storyboard.TargetProperty="(UIElement.Visibility)">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<Int32AnimationUsingKeyFrames Storyboard.TargetName="order_params_group_box" Storyboard.TargetProperty="(Grid.Row)">
|
||||
<DiscreteInt32KeyFrame KeyTime="0" Value="2"/>
|
||||
</Int32AnimationUsingKeyFrames>
|
||||
<Int32AnimationUsingKeyFrames Storyboard.TargetName="order_params_group_box" Storyboard.TargetProperty="(Grid.RowSpan)">
|
||||
<DiscreteInt32KeyFrame KeyTime="0" Value="1"/>
|
||||
</Int32AnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static Inactive, MemberType={u:Nested sp:OrderBookView.VisualStateNames.FillsPanel}}"/>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource GridSplitterWidth}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition MinHeight="{StaticResource FillsSlaveGridMinHeight}" Height="{Binding fills_slave_grid_height, Converter={StaticResource GridLengthPixelConverter}, Mode=TwoWay}" MaxHeight="{Binding ActualHeight, ElementName=right_panel_content_root, Converter={x:Static sp:OrderBookView.FillsSlaveGridMaxHeightConverter}}"/>
|
||||
<RowDefinition MinHeight="{x:Static sp:OrderBookView.GridSplitterHeight}" Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock x:Name="fills_toggle_hyperlink" Grid.Column="1">
|
||||
<TextBlock.Margin>
|
||||
<Thickness Left="{StaticResource DialogSkirtPadding.Left}" Top="4"/>
|
||||
</TextBlock.Margin>
|
||||
<Hyperlink Command="{Binding toggle_fills_slave_grid_is_visible_cmd}">
|
||||
<Hyperlink.Inlines>
|
||||
<Run Text="{Binding current_row, Converter={x:Static sp:OrderBookView.OrderDescriptionConverter}, Mode=OneWay}"/>
|
||||
</Hyperlink.Inlines>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
|
||||
<c:DIYGrid x:Name="fills_grid" Grid.Column="1" Grid.Row="1" Columns="{Binding fills_vm.columns}" Rows="{Binding fills_vm.rows}" Visibility="Collapsed">
|
||||
<c:DIYGrid.Margin>
|
||||
<Thickness Bottom="{x:Static sp:OrderBookView.GridSplitterHeight}"/>
|
||||
</c:DIYGrid.Margin>
|
||||
<c:DIYGrid.RowTemplate>
|
||||
<DataTemplate>
|
||||
<usp:FillsRowView/>
|
||||
</DataTemplate>
|
||||
</c:DIYGrid.RowTemplate>
|
||||
</c:DIYGrid>
|
||||
<GridSplitter
|
||||
x:Name="fills_order_params_splitter"
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Panel.ZIndex="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Height="{x:Static sp:OrderBookView.GridSplitterHeight}"
|
||||
BorderThickness="0,1,0,0"
|
||||
Visibility="Collapsed"
|
||||
/>
|
||||
<GroupBox
|
||||
x:Name="order_params_group_box"
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Grid.RowSpan="2"
|
||||
Padding="12"
|
||||
BorderThickness="1"
|
||||
HorizontalAlignment="Left"
|
||||
Header="Order Parameters"
|
||||
HeaderTemplate="{StaticResource BoldHeaderTemplate}"
|
||||
>
|
||||
<usp:OrderParamsPanel
|
||||
x:Name="order_params_panel"
|
||||
DataContext="{Binding order_mod_vm}"
|
||||
Price="{Binding price}"
|
||||
Size="{Binding size_calculator.value}"
|
||||
Accounts="{Binding accounts}"
|
||||
SelectedAccount="{Binding selected_account}"
|
||||
BestBidPrice="{Binding bbo.bid}"
|
||||
BestBidSize="{Binding bbo.bid_size}"
|
||||
BestAskPrice="{Binding bbo.ask}"
|
||||
BestAskSize="{Binding bbo.ask_size}"
|
||||
SizePresets="{Binding size_calculator.buttons}"
|
||||
Security="{Binding security}"
|
||||
AlgoVM="{Binding algo_vm}"
|
||||
IsEnabled="{Binding IsEnabled, ElementName=modify_orders_button}"
|
||||
>
|
||||
<usp:OrderParamsPanel.CommandBindings>
|
||||
<CommandBinding Command="{x:Static usp:OrderParamsPanel.SetPriceCommand}" Executed="{x:Static usp:OrderParamsPanel.SetPriceExecuted}"/>
|
||||
</usp:OrderParamsPanel.CommandBindings>
|
||||
</usp:OrderParamsPanel>
|
||||
</GroupBox>
|
||||
<GroupBox
|
||||
x:Name="attachment_content_root"
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Grid.RowSpan="2"
|
||||
Padding="12"
|
||||
BorderThickness="1"
|
||||
HorizontalAlignment="Left"
|
||||
Header="Attached Order Parameters"
|
||||
HeaderTemplate="{StaticResource BoldHeaderTemplate}"
|
||||
Margin="0,4,0,0"
|
||||
Visibility="Collapsed"
|
||||
>
|
||||
<usp:OrderParamsPanel
|
||||
x:Name="attached_order_params_panel"
|
||||
DataContext="{Binding attach_vm}"
|
||||
Price="{Binding price}"
|
||||
Size="{Binding size_calculator.value}"
|
||||
Accounts="{Binding accounts}"
|
||||
SelectedAccount="{Binding selected_account}"
|
||||
BestBidPrice="{Binding bbo.bid}"
|
||||
BestBidSize="{Binding bbo.bid_size}"
|
||||
BestAskPrice="{Binding bbo.ask}"
|
||||
BestAskSize="{Binding bbo.ask_size}"
|
||||
SizePresets="{Binding size_calculator.buttons}"
|
||||
Security="{Binding security}"
|
||||
AlgoVM="{Binding algo_vm}"
|
||||
IsEnabled="{Binding IsEnabled, ElementName=send_attached_orders_button}"
|
||||
>
|
||||
<usp:OrderParamsPanel.CommandBindings>
|
||||
<CommandBinding Command="{x:Static usp:OrderParamsPanel.SetPriceCommand}" Executed="{x:Static usp:OrderParamsPanel.SetPriceExecuted}"/>
|
||||
</usp:OrderParamsPanel.CommandBindings>
|
||||
</usp:OrderParamsPanel>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
<Canvas Grid.Row="1" HorizontalAlignment="Left" Width="{Binding ActualWidth, ElementName=order_params_group_box}" Height="30">
|
||||
<Button
|
||||
x:Name="modify_orders_button"
|
||||
Canvas.Top="{StaticResource DialogSkirtPadding.Top}"
|
||||
Command="{Binding modify_selected_algos_command}"
|
||||
>
|
||||
<Canvas.Left>
|
||||
<MultiBinding Converter="{x:Static sp:OrderBookView.RightPanelDialogSkirtLeftAnchorConverter}">
|
||||
<Binding Path="Width" RelativeSource="{RelativeSource AncestorType=Canvas}"/>
|
||||
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}"/>
|
||||
</MultiBinding>
|
||||
</Canvas.Left>
|
||||
<TextBlock>
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="{Binding selected_rows.Count, StringFormat='Modify Orders ({0})'}"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType=Button}}" Value="False">
|
||||
<Setter Property="Text" Value="Modify Orders"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
</Canvas>
|
||||
<Canvas Grid.Row="1" HorizontalAlignment="Left" Width="{Binding ActualWidth, ElementName=attachment_content_root}" Height="30">
|
||||
<StackPanel
|
||||
x:Name="attach_orders_dialog_skirt"
|
||||
Orientation="Horizontal"
|
||||
Canvas.Top="{StaticResource DialogSkirtPadding.Top}"
|
||||
Visibility="Collapsed"
|
||||
>
|
||||
<Canvas.Left>
|
||||
<MultiBinding Converter="{x:Static sp:OrderBookView.RightPanelDialogSkirtLeftAnchorConverter}">
|
||||
<Binding Path="Width" RelativeSource="{RelativeSource AncestorType=Canvas}"/>
|
||||
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}"/>
|
||||
</MultiBinding>
|
||||
</Canvas.Left>
|
||||
<Button x:Name="send_attached_orders_button" Command="{Binding send_attached_orders_cmd}" Style="{StaticResource {x:Static c:DialogSkirt.DialogSkirtButtonStyleKey}}">
|
||||
<TextBlock>
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="{Binding selected_rows.Count, StringFormat='Attach To Orders ({0})'}"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType=Button}}" Value="False">
|
||||
<Setter Property="Text" Value="Attach To Orders"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Button>
|
||||
<Button u:Behavior.CloseWindow="False" Margin="{StaticResource DialogSkirtButtonMargin}" Command="{Binding cancel_attach_orders_cmd}" Style="{StaticResource CancelButton}"/>
|
||||
</StackPanel>
|
||||
</Canvas>
|
||||
|
||||
</Grid>
|
||||
<StatusBar Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem HorizontalAlignment="Right">
|
||||
<TextBlock>
|
||||
<Run Text="Orders:"/>
|
||||
<Run Text="{Binding rows.Count, Mode=OneWay}" />
|
||||
<Run Text="of"/>
|
||||
<Run Text="{Binding rows.total_row_count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
@@ -1,151 +0,0 @@
|
||||
//#define EXPAND_ATTACHMENT_PANEL
|
||||
using mbts.ui;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class OrderBookView : UserControl {
|
||||
public const double
|
||||
GridSplitterWidth = 5,
|
||||
GridSplitterHeight = 5,
|
||||
MinAlgoEditorHeight = 64,
|
||||
MinAttachOrderParamsPanelWidth = 340,
|
||||
AttachOrderParamsPanelWidthRatio = 0.33;
|
||||
|
||||
public static readonly IValueConverter
|
||||
OrderDescriptionConverter = ValueConverter.create<OrderBookRowVM, string>(row => row == null ? string.Empty : row.order.short_description(row.security)),
|
||||
FillsSlaveGridMaxHeightConverter = ValueConverter.create<double, double>(right_panel_height => right_panel_height - (GridSplitterHeight + MinAlgoEditorHeight)),
|
||||
FillsPanelVisualStateConverter = ValueConverter.create<bool, string>(show => show ? VisualStateNames.FillsPanel.Active : VisualStateNames.FillsPanel.Inactive),
|
||||
AttachmentTargetCursorConverter = ValueConverter.create<bool, Cursor>(is_active => is_active ? Cursors.Pen : Cursors.Arrow);
|
||||
|
||||
public static readonly IMultiValueConverter
|
||||
RightPanelVisualStateConverter = ValueConverter.create<bool, bool, string>((is_modifying, is_attaching) =>
|
||||
is_attaching ? VisualStateNames.RightPanel.AttachOrders :
|
||||
(is_modifying ? VisualStateNames.RightPanel.ModifyOrders : VisualStateNames.RightPanel.Normal)),
|
||||
RightPanelDialogSkirtLeftAnchorConverter = ValueConverter.create<double, double, double>((canvas_width, skirt_width) => canvas_width - skirt_width + GridSplitterWidth),
|
||||
FillsSlaveGridStateConverter = ValueConverter.create<bool, bool, string>((is_fills_grid_visible, is_attaching) => is_fills_grid_visible && !is_attaching ? VisualStateNames.FillsPanel.Active : VisualStateNames.FillsPanel.Inactive);
|
||||
|
||||
public OrderBookView(OrderBookVM vm) {
|
||||
InitializeComponent();
|
||||
DataContext = vm;
|
||||
#if EXPAND_ATTACHMENT_PANEL
|
||||
(RightPanelState right_panel, IDisposable hooks)? attach_op = null;
|
||||
vm.begin_attaching += on_begin_attaching;
|
||||
vm.end_attaching += on_end_attaching;
|
||||
vm.SubscribeDisposed(() => {
|
||||
if (attach_op.HasValue) {
|
||||
vm.right_panel_width = attach_op.Value.right_panel.raw_width;
|
||||
vm.fills_slave_grid_is_visible = attach_op.Value.right_panel.fills_slave_grid_is_visible;
|
||||
}
|
||||
vm.begin_attaching -= on_begin_attaching;
|
||||
vm.end_attaching -= on_end_attaching;
|
||||
});
|
||||
const int delay_ms = 25;
|
||||
async void on_begin_attaching() {
|
||||
if (attach_op.HasValue) {
|
||||
// begin-attaching is already underway (prob won't happen in practice as this event is triggered by user interaction)
|
||||
return;
|
||||
}
|
||||
attach_op = (new RightPanelState(vm.right_panel_is_visible, vm.fills_slave_grid_is_visible, vm.raw_right_panel_width), orders_grid.SubscribePreviewMouseDown(cancel_on_grid_click));
|
||||
vm.fills_slave_grid_is_visible = false;
|
||||
vm.algo_editor_is_visible = true;
|
||||
double target_width = Math.Max(ActualWidth * AttachOrderParamsPanelWidthRatio, MinAttachOrderParamsPanelWidth);
|
||||
if (vm.right_panel_width < target_width) {
|
||||
foreach (double incr in _get_incremental_widths(attach_op.Value.right_panel.is_visible ? attach_op.Value.right_panel.raw_width : 0, target_width)) {
|
||||
vm.right_panel_width = incr;
|
||||
await Task.Delay(delay_ms);
|
||||
if (!attach_op.HasValue)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
async void on_end_attaching() {
|
||||
if (!attach_op.HasValue) {
|
||||
// end-attaching is already underway (prob won't happen in practice as this event is triggered by user interaction)
|
||||
return;
|
||||
}
|
||||
attach_op.Value.hooks.Dispose();
|
||||
var incr_widths = _get_incremental_widths(vm.right_panel_width, attach_op.Value.right_panel.raw_width);
|
||||
vm.algo_editor_is_visible = attach_op.Value.right_panel.is_visible;
|
||||
vm.fills_slave_grid_is_visible = attach_op.Value.right_panel.fills_slave_grid_is_visible;
|
||||
vm.right_panel_width = attach_op.Value.right_panel.raw_width;
|
||||
attach_op = null;
|
||||
foreach (double incr in incr_widths) {
|
||||
vm.right_panel_width = incr;
|
||||
await Task.Delay(delay_ms);
|
||||
if (attach_op.HasValue)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
IDisposable attach_op = null;
|
||||
vm.begin_attaching += on_begin_attaching;
|
||||
vm.end_attaching += on_end_attaching;
|
||||
void on_begin_attaching() => Disposable.replace(ref attach_op, orders_grid.SubscribePreviewMouseDown(cancel_on_grid_click));
|
||||
void on_end_attaching() => Disposable.dispose(ref attach_op);
|
||||
#endif
|
||||
void cancel_on_grid_click(object sender, MouseButtonEventArgs e) {
|
||||
if (!KeyboardExtensions.IsCtrlDown() && !KeyboardExtensions.IsShiftDown())
|
||||
vm.is_attaching_algos = false;
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerable<double> _get_incremental_widths(double from, double to) {
|
||||
var list = new List<double> { to };
|
||||
int incr = 1;
|
||||
if (from < to) {
|
||||
do {
|
||||
list.Add(to - (incr * 0.6666));
|
||||
list.Add(to - incr);
|
||||
from += incr;
|
||||
incr += incr;
|
||||
} while (from < to);
|
||||
} else if (from > to) {
|
||||
do {
|
||||
list.Add(to + incr);
|
||||
incr += incr;
|
||||
to += incr;
|
||||
} while (from > to);
|
||||
}
|
||||
for (int i = list.Count - 1; i >= 0; i--) {
|
||||
yield return list[i];
|
||||
}
|
||||
}
|
||||
|
||||
struct RightPanelState {
|
||||
public readonly bool is_visible, fills_slave_grid_is_visible;
|
||||
public readonly double raw_width;
|
||||
|
||||
public RightPanelState(bool right_panel_is_visible, bool fills_slave_grid_is_visible, double raw_right_panel_width) {
|
||||
this.is_visible = right_panel_is_visible;
|
||||
this.fills_slave_grid_is_visible = fills_slave_grid_is_visible;
|
||||
this.raw_width = raw_right_panel_width;
|
||||
}
|
||||
}
|
||||
|
||||
public static class VisualStateNames {
|
||||
public static class RightPanel {
|
||||
public const string GroupName = nameof(RightPanel);
|
||||
|
||||
public static readonly string
|
||||
Normal = $"{GroupName}{nameof(Normal)}",
|
||||
ModifyOrders = $"{GroupName}{nameof(ModifyOrders)}",
|
||||
AttachOrders = $"{GroupName}{nameof(AttachOrders)}";
|
||||
}
|
||||
|
||||
public static class FillsPanel {
|
||||
public const string GroupName = nameof(FillsPanel);
|
||||
|
||||
public static readonly string
|
||||
Active = $"{GroupName}{VisualStates.Active}",
|
||||
Inactive = $"{GroupName}{VisualStates.Inactive}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:order="clr-namespace:bts.api.orderman;assembly=spark_api"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<ControlTemplate x:Key="OrderParamsPanelTemplate" TargetType="usp:OrderParamsPanel">
|
||||
<Grid Grid.IsSharedSizeScope="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush2}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<usp:PriceEntryControl
|
||||
ContractSpec="{Binding ContractSpec, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Value="{Binding Price, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
|
||||
ToolTip="Price"
|
||||
/>
|
||||
<Button
|
||||
x:Name="best_bid_button"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{x:Static usp:OrderParamsPanel.SetPriceCommand}"
|
||||
CommandParameter="{Binding BestBidPrice, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{u:BTSColor MarketData.bid}"
|
||||
FontWeight="Bold"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.BestBidPriceButtonStyleKey}}"
|
||||
>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="BestBidPrice" RelativeSource="{RelativeSource TemplatedParent}"/>
|
||||
<Binding Path="ContractSpec" RelativeSource="{RelativeSource TemplatedParent}"/>
|
||||
</MultiBinding>
|
||||
</Button>
|
||||
<Button
|
||||
x:Name="best_ask_button"
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{x:Static usp:OrderParamsPanel.SetPriceCommand}"
|
||||
CommandParameter="{Binding BestAskPrice, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{u:BTSColor MarketData.ask}"
|
||||
FontWeight="Bold"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.BestAskPriceButtonStyleKey}}"
|
||||
>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="BestAskPrice" RelativeSource="{RelativeSource TemplatedParent}"/>
|
||||
<Binding Path="ContractSpec" RelativeSource="{RelativeSource TemplatedParent}"/>
|
||||
</MultiBinding>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush2}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:TickUpDownNullableUInt Value="{Binding Size, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="Size"/>
|
||||
<Button
|
||||
x:Name="best_bid_size_button"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
Content="{Binding BestBidSize, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{u:BTSColor MarketData.bid}"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.BestBidSizeButtonStyleKey}}"
|
||||
/>
|
||||
<Button
|
||||
x:Name="best_ask_size_button"
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
Content="{Binding BestAskSize, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Foreground="{u:BTSColor MarketData.ask}"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.BestAskSizeButtonStyleKey}}"
|
||||
/>
|
||||
<ItemsControl Grid.Row="2" Grid.RowSpan="2" Margin="-2,-2,-2,0" ItemsSource="{Binding SizePresets, RelativeSource={RelativeSource TemplatedParent}}" Focusable="False">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="3" Focusable="False"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="2,4,2,0"
|
||||
Content="{Binding}"
|
||||
Focusable="False"
|
||||
Style="{DynamicResource {x:Static usp:OrderParamsPanel.SizeCalculatorButtonStyleKey}}"
|
||||
/>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<Grid Grid.Row="2" Grid.RowSpan="2" Grid.Column="2" Grid.ColumnSpan="3">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<ComboBox Margin="0,2,0,4" ItemsSource="{Binding Accounts, RelativeSource={RelativeSource TemplatedParent}}" SelectedItem="{Binding SelectedAccount, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="Choose Account"/>
|
||||
<ComboBox Grid.Row="1">
|
||||
<ComboBox.Style>
|
||||
<Style TargetType="ComboBox" BasedOn="{StaticResource DisabledPlaceholderComboBox}">
|
||||
<Setter Property="ItemsSource">
|
||||
<Setter.Value>
|
||||
<x:ArrayExtension Type="{x:Type sys:String}">
|
||||
<sys:String>Time In Force (N/A)</sys:String>
|
||||
</x:ArrayExtension>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ComboBox.Style>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<usp:AlgoEditorView x:Name="algo_editor" Grid.Row="4" DataContext="{Binding AlgoVM, RelativeSource={RelativeSource TemplatedParent}}"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
|
||||
<Style TargetType="usp:OrderParamsPanel">
|
||||
<Setter Property="MinWidth" Value="300"/>
|
||||
<Setter Property="Template" Value="{StaticResource OrderParamsPanelTemplate}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -1,188 +0,0 @@
|
||||
using bts.api;
|
||||
using mbts.spark;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
class OrderParamsPanel : Control {
|
||||
public const string
|
||||
BestBidPriceButtonStyleKey = nameof(BestBidPriceButtonStyleKey),
|
||||
BestBidSizeButtonStyleKey = nameof(BestBidSizeButtonStyleKey),
|
||||
BestAskPriceButtonStyleKey = nameof(BestAskPriceButtonStyleKey),
|
||||
BestAskSizeButtonStyleKey = nameof(BestAskSizeButtonStyleKey),
|
||||
SizeCalculatorButtonStyleKey = nameof(SizeCalculatorButtonStyleKey);
|
||||
|
||||
public static readonly RoutedCommand SetPriceCommand = new RoutedCommand();
|
||||
|
||||
public static readonly ExecutedRoutedEventHandler SetPriceExecuted =
|
||||
(s, e) => {
|
||||
if (s is OrderParamsPanel opp)
|
||||
opp.SetCurrentValue(PriceProperty, ConvertUtils.try_convert(e.Parameter, out double d) ? d : double.NaN);
|
||||
};
|
||||
|
||||
public static readonly CanExecuteRoutedEventHandler
|
||||
SetPriceCanExecute = (s, e) => e.CanExecute = e.Parameter is double,
|
||||
SetSizeCanExecute = (s, e) => e.CanExecute = e.Parameter is uint;
|
||||
|
||||
static OrderParamsPanel() {
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(OrderParamsPanel), new FrameworkPropertyMetadata(typeof(OrderParamsPanel)));
|
||||
}
|
||||
|
||||
#region Price
|
||||
public static readonly DependencyProperty PriceProperty =
|
||||
WPFHelper.create_dp<double, OrderParamsPanel>(nameof(Price), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, double.NaN);
|
||||
|
||||
public double Price {
|
||||
get => (double)GetValue(PriceProperty);
|
||||
set => SetValue(PriceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Size
|
||||
public static readonly DependencyProperty SizeProperty =
|
||||
WPFHelper.create_dp<uint?, OrderParamsPanel>(nameof(Size), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
|
||||
|
||||
public uint? Size {
|
||||
get => GetValue(SizeProperty) as uint?;
|
||||
set => SetValue(SizeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SizePresets
|
||||
public static readonly DependencyProperty SizePresetsProperty =
|
||||
WPFHelper.create_dp<IEnumerable, OrderParamsPanel>(nameof(SizePresets), Enumerable.Empty<string>());
|
||||
|
||||
public IEnumerable SizePresets {
|
||||
get => GetValue(SizePresetsProperty) as IEnumerable;
|
||||
set => SetValue(SizePresetsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BestBidPrice
|
||||
public static readonly DependencyProperty BestBidPriceProperty =
|
||||
WPFHelper.create_dp<double?, OrderParamsPanel>(nameof(BestBidPrice));
|
||||
|
||||
public double? BestBidPrice {
|
||||
get => GetValue(BestBidPriceProperty) as double?;
|
||||
set => SetValue(BestBidPriceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BestBidSize
|
||||
public static readonly DependencyProperty BestBidSizeProperty =
|
||||
WPFHelper.create_dp<uint?, OrderParamsPanel>(nameof(BestBidSize));
|
||||
|
||||
public uint? BestBidSize {
|
||||
get => GetValue(BestBidSizeProperty) as uint?;
|
||||
set => SetValue(BestBidSizeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BestAskPrice
|
||||
public static readonly DependencyProperty BestAskPriceProperty =
|
||||
WPFHelper.create_dp<double?, OrderParamsPanel>(nameof(BestAskPrice));
|
||||
|
||||
public double? BestAskPrice {
|
||||
get => GetValue(BestAskPriceProperty) as double?;
|
||||
set => SetValue(BestAskPriceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BestAskSize
|
||||
public static readonly DependencyProperty BestAskSizeProperty =
|
||||
WPFHelper.create_dp<uint?, OrderParamsPanel>(nameof(BestAskSize));
|
||||
|
||||
public uint? BestAskSize {
|
||||
get => GetValue(BestAskSizeProperty) as uint?;
|
||||
set => SetValue(BestAskSizeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Accounts
|
||||
public static readonly DependencyProperty AccountsProperty =
|
||||
WPFHelper.create_dp<IEnumerable, OrderParamsPanel>(nameof(Accounts), Enumerable.Empty<string>());
|
||||
|
||||
public IEnumerable Accounts {
|
||||
get => GetValue(AccountsProperty) as IEnumerable;
|
||||
set => SetValue(AccountsProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SelectedAccount
|
||||
public static readonly DependencyProperty SelectedAccountProperty =
|
||||
WPFHelper.create_dp<object, OrderParamsPanel>(nameof(SelectedAccount));
|
||||
|
||||
public object SelectedAccount {
|
||||
get => GetValue(SelectedAccountProperty);
|
||||
set => SetValue(SelectedAccountProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TimeInForces
|
||||
public static readonly DependencyProperty TimeInForcesProperty =
|
||||
WPFHelper.create_dp<IEnumerable, OrderParamsPanel>(nameof(TimeInForces), Enumerable.Empty<string>());
|
||||
|
||||
public IEnumerable TimeInForces {
|
||||
get => GetValue(TimeInForcesProperty) as IEnumerable;
|
||||
set => SetValue(TimeInForcesProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SelectedTimeInForce
|
||||
public static readonly DependencyProperty SelectedTimeInForceProperty =
|
||||
WPFHelper.create_dp<object, OrderParamsPanel>(nameof(SelectedTimeInForce));
|
||||
|
||||
public object SelectedTimeInForce {
|
||||
get => GetValue(SelectedTimeInForceProperty);
|
||||
set => SetValue(SelectedTimeInForceProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Security
|
||||
public static readonly DependencyProperty SecurityProperty =
|
||||
WPFHelper.create_dp<SecurityImmutable?, OrderParamsPanel>(
|
||||
nameof(Security),
|
||||
(o, v) => o.CoerceValue(ContractSpecProperty));
|
||||
|
||||
public SecurityImmutable? Security {
|
||||
get => GetValue(SecurityProperty) as SecurityImmutable?;
|
||||
set => SetValue(SecurityProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ContractSpec
|
||||
public static readonly DependencyProperty ContractSpecProperty =
|
||||
WPFHelper.create_dp<ContractSpecificationImmutable?, OrderParamsPanel>(
|
||||
nameof(ContractSpec),
|
||||
null,
|
||||
(d, base_value) => d.Security?.ContractSpec ?? base_value ?? SecurityDataHelper.Defaults.ContractSpecificationImmutable);
|
||||
|
||||
public ContractSpecificationImmutable? ContractSpec {
|
||||
get => GetValue(ContractSpecProperty) as ContractSpecificationImmutable?;
|
||||
set => SetValue(ContractSpecProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AlgoVM
|
||||
public static readonly DependencyProperty AlgoVMProperty =
|
||||
WPFHelper.create_dp<AlgoDefinitionVM, OrderParamsPanel>(
|
||||
nameof(AlgoVM),
|
||||
(o, v) => o._algo_editor?.SetCurrentValue(VisibilityProperty, v != null ? Visibility.Visible : Visibility.Collapsed));
|
||||
|
||||
public AlgoDefinitionVM AlgoVM {
|
||||
get => GetValue(AlgoVMProperty) as AlgoDefinitionVM;
|
||||
set => SetValue(AlgoVMProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
AlgoEditorView _algo_editor;
|
||||
public override void OnApplyTemplate() {
|
||||
_algo_editor = GetTemplateChild("algo_editor") as AlgoEditorView;
|
||||
_algo_editor?.SetCurrentValue(VisibilityProperty, AlgoVM != null ? Visibility.Visible : Visibility.Collapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace bts.shell {
|
||||
void _on_activity(Activity activity) =>
|
||||
_activity_notifications.process_item(activity);
|
||||
|
||||
void _on_orders(TableUpdate<(SecurityImmutable?,OrderRecordImmutable),uint> update) {
|
||||
void _on_orders(TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint> update) {
|
||||
// must update notification_service_data_cache first, some of the predicate/triggers (esp. ToastNotifications) will use the notification_service_data_cache to lookup order-history
|
||||
_data_cache.update(update.as_iincremental());
|
||||
foreach (var o in update.added.Concat(update.changed))
|
||||
|
||||
@@ -79,12 +79,10 @@ namespace mbts.spark {
|
||||
RequestClose?.Invoke(this, new EventArgs());
|
||||
}
|
||||
});
|
||||
bbo = new BBOVM();
|
||||
_dispose = Disposable.Create(
|
||||
size_calculator,
|
||||
properties_vm,
|
||||
security_md_source.OnDispatcher().Subscribe(smd => bbo.security_md = smd),
|
||||
bbo,
|
||||
bbo = new BBOVM(security_md_source),
|
||||
order_source.OnDispatcher().Subscribe(ort => {
|
||||
key = ort.order.Key;
|
||||
if (DoubleHelper.IsNaNOrInfinity(price))
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace mbts.spark {
|
||||
) {
|
||||
_security = backing(nameof(security), security_md_source.Select(smd => smd.security), SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_md_source, props_source.Select(p => p.order_props));
|
||||
bbo = new BBOVM();
|
||||
bbo = new BBOVM(security_md_source);
|
||||
send_order = new DelegateCommand<Side>(side => {
|
||||
bool price_valid = _validate_price(), size_valid = size_calculator.validate();
|
||||
if (price_valid && size_valid && !HasErrors && !properties_vm.HasErrors) {
|
||||
@@ -59,8 +59,6 @@ namespace mbts.spark {
|
||||
});
|
||||
set_price_command = new DelegateCommand<double>(set_price);
|
||||
_dispose = Disposable.Create(
|
||||
bbo,
|
||||
security_md_source.OnDispatcher().Subscribe(smd => bbo.security_md = smd),
|
||||
size_calculator = new CalculatorLogic(0, calculator_buttons),
|
||||
props_source.OnDispatcher().Subscribe(p => on_props(p.price, p.size))
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.shell;
|
||||
using bts.utils;
|
||||
using mbts;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls;
|
||||
@@ -28,7 +27,7 @@ namespace bts.spark {
|
||||
static readonly IEqualityComparer<OrderRecordImmutable> _id_comparer =
|
||||
bts.utils.EqualityComparer.Create<OrderRecordImmutable, uint>(a => a.Key);
|
||||
|
||||
public OrderActivitySummarizer(IBus bus, IObservable<TableUpdate<(SecurityImmutable? sec,OrderRecordImmutable order), uint>> orders) {
|
||||
public OrderActivitySummarizer(IBus bus, IObservable<TableUpdate<(SecurityImmutable? sec, OrderRecordImmutable order), uint>> orders) {
|
||||
_bus = bus;
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).Subscribe(sec => _security = sec),
|
||||
@@ -84,18 +83,25 @@ namespace bts.spark {
|
||||
public static string modified_description(OrderRecordImmutable old_order, OrderRecordImmutable new_order, ContractSpecificationImmutable? spec) {
|
||||
double old_price = old_order.State.Price, new_price = new_order.State.Price;
|
||||
uint old_size = old_order.State.Quantity, new_size = new_order.State.Quantity;
|
||||
uint old_attached_target_key = old_order.attached_target_order_key(), new_attached_target_key = new_order.attached_target_order_key();
|
||||
return StringBuilderEx.join(prefix, change_descs());
|
||||
//
|
||||
string prefix() => $"({old_order.Key}) Changed";
|
||||
IEnumerable<string> change_descs() {
|
||||
if (old_price != new_price)
|
||||
yield return $"price from {spec.format(old_price)} to {spec.format(new_price)}";
|
||||
if (old_size != new_size)
|
||||
yield return $"size from {old_size} to {new_size}, working {new_order.working_quantity()}";
|
||||
if (old_attached_target_key != new_attached_target_key)
|
||||
yield return $"attached target from {old_attached_target_key} to {new_attached_target_key}";
|
||||
string modified_size_partial_description(int working_size) {
|
||||
return $"size from {old_size} to {new_size}, working {working_size}";
|
||||
}
|
||||
string modified_price_partial_description() {
|
||||
return $"price from {spec.format(old_price)} to {spec.format(new_price)}";
|
||||
}
|
||||
if (old_price != new_price || old_size != new_size) {
|
||||
StringBuilder sb = new StringBuilder($"({old_order.Key}) Changed ");
|
||||
if (old_price != new_price) {
|
||||
sb.Append(modified_price_partial_description());
|
||||
if (old_size != new_size)
|
||||
sb.Append($", {modified_size_partial_description(new_order.working_quantity())}");
|
||||
}
|
||||
else
|
||||
sb.Append(modified_size_partial_description(new_order.working_quantity()));
|
||||
return sb.ToString();
|
||||
}
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
static string _header(uint key) => $"({key})";
|
||||
@@ -178,31 +184,21 @@ namespace bts.spark {
|
||||
format_cancel_error(order, security?.ContractSpec, error));
|
||||
}
|
||||
|
||||
internal void update_order(OrderRecordImmutable order, double price, uint size, bool algo_changed) =>
|
||||
_log_order_update(order, price, size, algo_changed, "change from");
|
||||
|
||||
internal void update_order_failed(OrderRecordImmutable order, double price, uint size, bool algo_changed) =>
|
||||
_log_order_update(order, price, size, algo_changed, "failed to change from");
|
||||
|
||||
void _log_order_update(OrderRecordImmutable order, double price, uint size, bool algo_changed, string change_desc_prefix) {
|
||||
if (price == order.price() && size == order.working_quantity() && !algo_changed) {
|
||||
// nothing actually changed
|
||||
return;
|
||||
}
|
||||
const string algo_change_text = "change algo parameters";
|
||||
internal void update_order(OrderRecordImmutable order, double price, uint size) {
|
||||
var security = try_get_security(order.Def.SecurityKey);
|
||||
var spec = security?.ContractSpec;
|
||||
var desc_builder = new StringBuilderEx();
|
||||
if (price != order.price() || size != order.working_quantity()) {
|
||||
desc_builder.Add(order.State.Quantity.ToString(), "@", spec.format(order.State.Price), "to ", size.ToString(), "@", spec.format(price));
|
||||
if (algo_changed)
|
||||
desc_builder.Add("; ", algo_change_text);
|
||||
} else if (algo_changed) {
|
||||
desc_builder.Add(algo_change_text);
|
||||
}
|
||||
desc_builder.Insert(0, $"{change_desc_prefix} ");
|
||||
desc_builder.Insert(0, $"({order.Key}) ");
|
||||
log_status(ActivityLogCategory.modify, security, desc_builder.ToString());
|
||||
log_status(ActivityLogCategory.modify,
|
||||
security,
|
||||
$"({order.Key}) change from {order.State.Quantity}@{spec.format(order.State.Price)} to {size}@{spec.format(price)}");
|
||||
}
|
||||
|
||||
internal void update_order_failed(OrderRecordImmutable order, double price, uint size) {
|
||||
var security = try_get_security(order.Def.SecurityKey);
|
||||
var spec = security?.ContractSpec;
|
||||
log_status(ActivityLogCategory.modify,
|
||||
security,
|
||||
$"({order.Key}) failed to change from {order.State.Quantity}@{spec.format(order.State.Price)} to {size}@{spec.format(price)}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -346,9 +346,9 @@ namespace mbts.spark {
|
||||
_time_in_force.observe_current(start_immediately);
|
||||
|
||||
public IObservable<IAlgoVM> observe_order_type(bool start_immediately = false) =>
|
||||
_model.algo_model.templates_enabled() ?
|
||||
_selected_algo_template.observe_current(start_immediately).Select(t => t?.algo_root) :
|
||||
_selected_algo_definition.observe_selected_item(start_immediately).Select(d => d?.algo_root);
|
||||
_model.algo_model.templates_enabled() ?
|
||||
_selected_algo_template.observe_current(start_immediately).Select(t => t?.algo_root) :
|
||||
_selected_algo_definition.observe_selected_item(start_immediately).Select(d => d?.algo_root);
|
||||
|
||||
public OrderDefinition create(Side side) {
|
||||
if (HasErrors)
|
||||
|
||||
@@ -7,18 +7,31 @@ namespace bts.api.orderman {
|
||||
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order) {
|
||||
this.security = sec;
|
||||
this.order = order;
|
||||
piq = null;
|
||||
}
|
||||
public OrderSecurity((SecurityImmutable? sec, OrderRecordImmutable order) t) {
|
||||
security = t.sec;
|
||||
order = t.order;
|
||||
piq = null;
|
||||
}
|
||||
public OrderSecurity(SecurityImmutable? sec, OrderRecordImmutable order, uint? piq) {
|
||||
this.security = sec;
|
||||
this.order = order;
|
||||
this.piq = piq;
|
||||
}
|
||||
//public OrderSecurity(OrderSecurity t) {
|
||||
// security = t.security;
|
||||
// order = t.order;
|
||||
// piq = t.piq;
|
||||
//}
|
||||
public static implicit operator OrderSecurity(ValueTuple<SecurityImmutable?, OrderRecordImmutable> x) => new OrderSecurity(x);
|
||||
public SecurityImmutable? security { get; }
|
||||
public OrderRecordImmutable order { get; }
|
||||
public uint? piq { get; }
|
||||
|
||||
public override bool Equals(object obj) => (obj is OrderSecurity os) && Equals(os);
|
||||
public bool Equals(OrderSecurity other) => order.Equals(other.order) && security == other.security;
|
||||
public override int GetHashCode() => (security, order).GetHashCode();
|
||||
public bool Equals(OrderSecurity other) => order.Equals(other.order) && security == other.security && piq == other.piq;
|
||||
public override int GetHashCode() => (security, order, piq).GetHashCode();
|
||||
|
||||
public static bool operator == (OrderSecurity left, OrderSecurity right) => left.Equals(right);
|
||||
public static bool operator != (OrderSecurity left, OrderSecurity right) => !left.Equals(right);
|
||||
|
||||
@@ -11,7 +11,7 @@ using AlgoDefi = bts.api.orderman.CustomOrderDefinitionImmutable;
|
||||
using AlgoType = bts.api.orderman.CustomOrderDefinition.CustomOneofCase;
|
||||
|
||||
namespace mbts.spark {
|
||||
class DefaultAlgoVM : DisposableViewModelBase, IAlgoVM, IDynamicAlgoVMImpl, IObserver<SecurityMD?>, IObserver<OrdersModel>, IObserver<AlgoEditingPermissions> {
|
||||
class DefaultAlgoVM : DisposableViewModelBase, IAlgoVM, IDynamicAlgoVMImpl, IObserver<SecurityMD?>, IObserver<OrdersModel> {
|
||||
static readonly AlgoDefi _empty = new AlgoDefi(new AlgoDef());
|
||||
readonly VersionContainer<AlgoDefi> _definition;
|
||||
AlgoFormats _format;
|
||||
@@ -37,15 +37,21 @@ namespace mbts.spark {
|
||||
impl.is_expanded = value;
|
||||
}
|
||||
}
|
||||
public AlgoEditingPermissions editing_permissions => _editing_permissions;
|
||||
public bool can_edit_values => impl?.can_edit_values ?? false;
|
||||
public bool can_edit_structure => impl?.can_edit_structure ?? false;
|
||||
public bool can_save_values => impl?.can_save_values ?? false;
|
||||
public bool can_save_structure => impl?.can_save_structure ?? false;
|
||||
public string display_name => impl?.display_name ?? "(Default)";
|
||||
public AlgoDefi definition => _definition.value;
|
||||
public ObservableCollection<IAlgoSelector> child_algo_selectors { get; } = new ObservableCollection<IAlgoSelector>();
|
||||
public dynamic children_vm => impl?.children_vm;
|
||||
|
||||
public void assign_to(AlgoDef value) => value.MergeFrom(definition.CloneToMutable());
|
||||
public bool key_match(KeyEventArgs e) => false;
|
||||
public void load_from(AlgoDefi value) => impl?.load_from(value);
|
||||
public void set_permissions(AlgoEditingPermissions editing_permissions) {
|
||||
_editing_permissions = editing_permissions;
|
||||
impl?.set_permissions(editing_permissions);
|
||||
}
|
||||
public Task<AlgoDefi?> load_from_async(AlgoDefi definition) =>
|
||||
impl?.load_from_async(definition) ?? Task.FromResult<AlgoDefi?>(definition);
|
||||
|
||||
@@ -92,13 +98,6 @@ namespace mbts.spark {
|
||||
_on_impl_definition_changed();
|
||||
}
|
||||
|
||||
void IObserver<AlgoEditingPermissions>.OnCompleted() { }
|
||||
void IObserver<AlgoEditingPermissions>.OnError(Exception error) { }
|
||||
void IObserver<AlgoEditingPermissions>.OnNext(AlgoEditingPermissions editing_permissions) {
|
||||
Set(ref _editing_permissions, editing_permissions, nameof(this.editing_permissions));
|
||||
(impl as IObserver<AlgoEditingPermissions>)?.OnNext(editing_permissions);
|
||||
}
|
||||
|
||||
void _on_impl_definition_changed() {
|
||||
var orig_def = definition;
|
||||
_definition.increment_version();
|
||||
@@ -106,7 +105,6 @@ namespace mbts.spark {
|
||||
definition_changed?.Invoke();
|
||||
if (order_type != orig_def.CustomCase)
|
||||
RaisePropertyChanged(nameof(order_type), nameof(meta));
|
||||
RaisePropertyChanged(nameof(children_vm));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.utils;
|
||||
using Google.Protobuf.Reflection;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -12,9 +11,8 @@ using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
[Export]
|
||||
class DynamicOrderHelper {
|
||||
public readonly IMaskIndexInt32<string> dynamic_type_index;
|
||||
|
||||
public class DynamicOrderHelper
|
||||
{
|
||||
static readonly IReadOnlyDictionary<DynamicOrderParam.ValueOneofCase, (FieldDescriptor field_descriptor, Type field_type)> _descriptor_case_index =
|
||||
ProtoHelper.get_oneof_descriptor_index<DynamicOrderParam, DynamicOrderParam.ValueOneofCase>(nameof(DynamicOrderParam.ValueCase));
|
||||
|
||||
@@ -23,7 +21,6 @@ namespace mbts.spark {
|
||||
private DynamicOrderHelper(OrderService order_service)
|
||||
{
|
||||
_dynamic_types = order_service.get_registered_dynamic_order_types(new VoidMessage());
|
||||
dynamic_type_index = new MaskIndexInt32<string>(_dynamic_types.DynamicOrderDesc.Select(d => d.DynamicOrderType).ToList());
|
||||
}
|
||||
|
||||
public void add_dynamic_order_defs(List<CustomOrderDefinitionImmutable> defs,
|
||||
@@ -33,9 +30,9 @@ namespace mbts.spark {
|
||||
{
|
||||
var dorder = factory(CustomOrderDefinition.CustomOneofCase.Dynamic);
|
||||
dorder.Dynamic.DynamicOrderType = d.DynamicOrderType;
|
||||
var ddef = dorder.Dynamic;
|
||||
//Add only the descriptor here for reflection later
|
||||
add_dynamic_params(ref ddef);
|
||||
foreach(var p in d.ParamDesc)
|
||||
dorder.Dynamic.OrderParams.Add( p.ParamName, new DynamicOrderParam() );
|
||||
defs.Add(new CustomOrderDefinitionImmutable(dorder));
|
||||
}
|
||||
}
|
||||
@@ -45,35 +42,39 @@ namespace mbts.spark {
|
||||
|
||||
internal DynamicOrderDefinition create_def(string dynamic_type) {
|
||||
var desc = get_descriptor(dynamic_type);
|
||||
var def = new DynamicOrderDefinition { DynamicOrderType = dynamic_type };
|
||||
add_dynamic_params(ref def);
|
||||
var def = new DynamicOrderDefinition();
|
||||
foreach (var p in desc.ParamDesc) {
|
||||
if (_is_child_algo_desc(p)) {
|
||||
def.OrderParams[p.ParamName] = new DynamicOrderParam { AsOrderDef = new CustomOrderDefinition() };
|
||||
} else if (_is_value_param_desc(p)) {
|
||||
var value_case = _get_value_case(p.ParamType.Primitive);
|
||||
if (_descriptor_case_index.TryGetValue(value_case, out (FieldDescriptor fd, Type type) tup)) {
|
||||
var dop = new DynamicOrderParam();
|
||||
tup.fd.Accessor.SetValue(dop, ConvertUtils.get_default(tup.type));
|
||||
def.OrderParams[p.ParamName] = dop;
|
||||
}
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
internal void add_dynamic_params(ref DynamicOrderDefinition dynamic_def) {
|
||||
var desc = get_descriptor(dynamic_def.DynamicOrderType);
|
||||
foreach (var p in desc.ParamDesc)
|
||||
dynamic_def.OrderParams[p.ParamName] = init_param(p);
|
||||
static DynamicOrderParam.ValueOneofCase _get_value_case(DynamicOrderPrimitiveDescriptor primitive_descriptor_case) {
|
||||
switch (primitive_descriptor_case) {
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveBool: return DynamicOrderParam.ValueOneofCase.AsBool;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveDouble: return DynamicOrderParam.ValueOneofCase.AsBool;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt: return DynamicOrderParam.ValueOneofCase.AsInt32;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: return DynamicOrderParam.ValueOneofCase.AsUint32;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: return DynamicOrderParam.ValueOneofCase.AsString;
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(primitive_descriptor_case, nameof(primitive_descriptor_case));
|
||||
}
|
||||
}
|
||||
|
||||
internal static DynamicOrderParam init_param(DynamicOrderParamDescriptor descriptor) {
|
||||
var param = new DynamicOrderParam();
|
||||
switch (descriptor.ParamType.DescriptorCase) {
|
||||
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Array: param.AsArray = new DynamicOrderArray(); break;
|
||||
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated: param.AsEnumerated = new DynamicOrderEnumerated { EnumValue = descriptor.ParamType.Enumerated.Element.FirstOrDefault() }; break;
|
||||
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive:
|
||||
switch (descriptor.ParamType.Primitive) {
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveBool: param.AsBool = false; break;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveDouble: param.AsDouble = 0.0; break;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveInt: param.AsInt32 = 0; break;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef: param.AsOrderDef = new CustomOrderDefinition(); break;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: param.AsString = string.Empty; break;
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveUint: param.AsUint32 = 0u; break;
|
||||
}
|
||||
break;
|
||||
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Structure: param.AsStruct = new DynamicOrderStruct(); break;
|
||||
}
|
||||
return param;
|
||||
}
|
||||
static bool _is_child_algo_desc(DynamicOrderParamDescriptor desc) =>
|
||||
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive &&
|
||||
desc.ParamType.Primitive == DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef;
|
||||
|
||||
static bool _is_value_param_desc(DynamicOrderParamDescriptor desc) =>
|
||||
desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated ||
|
||||
(desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive && desc.ParamType.Primitive != DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,13 @@ using Google.Protobuf.Reflection;
|
||||
using mbts.ui.shell;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
abstract class DynamicOrderParamVM : DisposableViewModelBase {
|
||||
DynamicOrderParam _param;
|
||||
abstract class DynamicOrderParamVM : ViewModelBase {
|
||||
DynamicOrderParam _param = new DynamicOrderParam();
|
||||
|
||||
protected DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor)
|
||||
: this(descriptor, DynamicOrderHelper.init_param(descriptor)) { }
|
||||
|
||||
protected DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor, DynamicOrderParam param) {
|
||||
protected DynamicOrderParamVM(DynamicOrderParamDescriptor descriptor) =>
|
||||
this.descriptor = descriptor;
|
||||
_param = param;
|
||||
}
|
||||
|
||||
public DynamicOrderParamDescriptor descriptor { get; set; }
|
||||
public DynamicOrderParam param {
|
||||
@@ -42,7 +34,6 @@ namespace mbts.spark {
|
||||
case DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveString: return new DynamicOrderParamVM<string>(descriptor, DynamicOrderParam.AsStringFieldNumber);
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(descriptor.ParamType.Primitive, nameof(descriptor));
|
||||
}
|
||||
case DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated: return new DynamicOrderEnumeratedVM(descriptor);
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(descriptor.ParamType.DescriptorCase, nameof(descriptor));
|
||||
}
|
||||
}
|
||||
@@ -64,41 +55,4 @@ namespace mbts.spark {
|
||||
|
||||
protected override void on_param_changed() => RaisePropertyChanged(nameof(value));
|
||||
}
|
||||
|
||||
class DynamicOrderEnumeratedVM : DynamicOrderParamVM {
|
||||
static readonly FieldDescriptor _field_desc = DynamicOrderParam.Descriptor.FindFieldByNumber(DynamicOrderParam.AsEnumeratedFieldNumber);
|
||||
|
||||
public DynamicOrderEnumeratedVM(DynamicOrderParamDescriptor descriptor)
|
||||
: base(descriptor) =>
|
||||
elements = new ObservableCollection<string>(_get_elements(descriptor));
|
||||
|
||||
public ObservableCollection<string> elements { get; }
|
||||
|
||||
public string selected_element {
|
||||
get => param?.ValueCase == DynamicOrderParam.ValueOneofCase.AsEnumerated ? param.AsEnumerated.EnumValue : null;
|
||||
set {
|
||||
if (param == null) {
|
||||
param = new DynamicOrderParam { AsEnumerated = new DynamicOrderEnumerated() };
|
||||
} else if (param.ValueCase != DynamicOrderParam.ValueOneofCase.AsEnumerated) {
|
||||
param.AsEnumerated = new DynamicOrderEnumerated();
|
||||
}
|
||||
if (param.AsEnumerated.EnumValue != value) {
|
||||
param.AsEnumerated.EnumValue = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void on_param_changed() {
|
||||
_sync_elements();
|
||||
RaisePropertyChanged(nameof(selected_element));
|
||||
}
|
||||
|
||||
static IEnumerable<string> _get_elements(DynamicOrderParamDescriptor descriptor) =>
|
||||
descriptor.ParamType?.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Enumerated ?
|
||||
descriptor.ParamType.Enumerated.Element : Enumerable.Empty<string>();
|
||||
|
||||
void _sync_elements() =>
|
||||
elements.sync_with(_get_elements(descriptor));
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,7 @@ namespace mbts.spark {
|
||||
}),
|
||||
ObservableAutoWrapper.connect_item_hooks(_parameters, p => {
|
||||
p.PropertyChanged += on_param_prop_changed;
|
||||
return Disposable.Create(() => {
|
||||
p.PropertyChanged -= on_param_prop_changed;
|
||||
p.Dispose();
|
||||
});
|
||||
return Disposable.Create(() => p.PropertyChanged -= on_param_prop_changed);
|
||||
}, true));
|
||||
void on_param_prop_changed(object sender, PropertyChangedEventArgs e) => raise_definition_changed();
|
||||
}
|
||||
@@ -64,4 +61,4 @@ namespace mbts.spark {
|
||||
(desc.ParamType.DescriptorCase == DynamicOrderTypeDescriptor.DescriptorOneofCase.Primitive && desc.ParamType.Primitive != DynamicOrderPrimitiveDescriptor.DynamicOrderPrimitiveOrderDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,14 +22,17 @@ namespace mbts.spark {
|
||||
CustomOrderDefinition.CustomOneofCase order_type { get; }
|
||||
bool is_bts_type { get; }
|
||||
OrderMetadata meta { get; }
|
||||
AlgoEditingPermissions editing_permissions { get; }
|
||||
bool can_expand { get; }
|
||||
bool is_expanded { get; set; }
|
||||
bool can_edit_values { get; }
|
||||
bool can_edit_structure { get; }
|
||||
bool can_save_values { get; }
|
||||
bool can_save_structure { get; }
|
||||
string display_name { get; }
|
||||
CustomOrderDefinitionImmutable definition { get; }
|
||||
ObservableCollection<IAlgoSelector> child_algo_selectors { get; }
|
||||
dynamic children_vm { get; }
|
||||
bool key_match(KeyEventArgs e);
|
||||
void set_permissions(AlgoEditingPermissions editing_permissions);
|
||||
Task<CustomOrderDefinitionImmutable?> load_from_async(CustomOrderDefinitionImmutable definition);
|
||||
}
|
||||
|
||||
@@ -88,4 +91,4 @@ namespace mbts.spark {
|
||||
ObservableAutoWrapper.connect_item_hooks(vm.child_algo_selectors, s => load_algo_data_recursive(s, algo_data, default_algo)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,8 @@ namespace mbts.spark {
|
||||
public static IAlgoVM create(AlgoData def_data, AlgoEditingPermissions editing_permissions = default_editing_permissions, bool is_expanded = false) {
|
||||
var vm = create(def_data.key, def_data.name, def_data.value.CustomCase, editing_permissions, is_expanded);
|
||||
vm.load_from(def_data.value);
|
||||
//vm.is_expanded = is_expanded && vm.can_expand;
|
||||
vm.is_expanded = is_expanded && vm.can_expand;
|
||||
return vm;
|
||||
}
|
||||
|
||||
@@ -82,7 +84,6 @@ namespace mbts.spark {
|
||||
case AlgoType.None: vm = new DefaultAlgoVM(); break;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
vm.is_expanded = is_expanded && vm.can_expand;
|
||||
return vm;
|
||||
}
|
||||
|
||||
@@ -94,32 +95,38 @@ namespace mbts.spark {
|
||||
public static FieldDescriptor find_descriptor<T>() =>
|
||||
_algo_type_index.TryGetValue(typeof(T), out FieldDescriptor fd) ?
|
||||
fd : throw new NotSupportedException($"No {nameof(AlgoDef)} {nameof(FieldDescriptor)} exists for type '{typeof(T)}.'");
|
||||
|
||||
public static Func<TMutable, TImmutable> immutable_factory<TMutable, TImmutable>() where TMutable : IMessage, new() where TImmutable : IImmutableMessage =>
|
||||
_ImmutableActivator<TMutable, TImmutable>.factory;
|
||||
|
||||
static class _ImmutableActivator<TMutable, TImmutable> where TMutable : IMessage, new() where TImmutable : IImmutableMessage {
|
||||
public static readonly Func<TMutable, TImmutable> factory =
|
||||
m => (TImmutable)Activator.CreateInstance(typeof(TImmutable), m);
|
||||
}
|
||||
}
|
||||
|
||||
#region OrderTypeVM Base Classes
|
||||
abstract class OrderTypeVMBase<TMutable, TImmutable> : DisposableViewModelBase, IAlgoVM,
|
||||
IObserver<SecurityMD?>,
|
||||
IObserver<SecurityImmutable?>,
|
||||
IObserver<OrdersModel>,
|
||||
IObserver<AlgoEditingPermissions> where TMutable : class, IMessage, new() where TImmutable : struct, IImmutableMessage {
|
||||
abstract class OrderTypeVMBase<TMutable, TImmutable> : DisposableViewModelBase, IAlgoVM, IObserver<SecurityMD?>, IObserver<OrdersModel> where TMutable : class, IMessage, new() where TImmutable : struct, IImmutableMessage {
|
||||
/// TODO: maybe use explicit delegates rather than rely on reflection <see cref="_descriptor"/> <see cref="_create_immutable"/>.
|
||||
static readonly FieldDescriptor _descriptor = OrderTypeVMUtils.find_descriptor<TMutable>();
|
||||
static readonly Func<TMutable, TImmutable> _create_immutable = OrderTypeVMUtils.immutable_factory<TMutable, TImmutable>();
|
||||
readonly IDictionary<string, object> _children_vm = new ExpandoObject();
|
||||
protected CompositeDisposable _resources = new CompositeDisposable();
|
||||
|
||||
readonly ObservableKeyedCollection<(string name, Action<TMutable> assign, Action<TMutable> load), string> _auto_props = new ObservableKeyedCollection<(string name, Action<TMutable> assign, Action<TMutable> load), string>(t => t.name);
|
||||
readonly ObservableKeyedCollection<AlgoSelectorData, string> _child_algo_selector_data = new ObservableKeyedCollection<AlgoSelectorData, string>(sd => sd.prop_name);
|
||||
readonly ObservableAutoWrapper<IAlgoSelector, AlgoSelectorData> _child_algo_selectors;
|
||||
readonly Property<bool> _is_expanded;
|
||||
readonly Property<bool> _is_expanded, _can_save_values, _can_save_structure;
|
||||
readonly DeferrableActionWrapper _def_change_notifier;
|
||||
SecurityInput _security_input;
|
||||
AlgoEditingPermissions _editing_permissions;
|
||||
IDisposable _pending_load;
|
||||
|
||||
protected OrderTypeVMBase(AlgoKey id, string display_name, AlgoEditingPermissions editing_permissions) {
|
||||
this.id = id;
|
||||
this.display_name = display_name;
|
||||
_editing_permissions = editing_permissions;
|
||||
this.editing_permissions = editing_permissions;
|
||||
_child_algo_selectors = new ObservableAutoWrapper<IAlgoSelector, AlgoSelectorData>(_child_algo_selector_data, sd => sd.selector);
|
||||
_can_save_values = backing(nameof(can_save_values), true);
|
||||
_can_save_structure = backing(nameof(can_save_structure), true);
|
||||
_is_expanded = backing<bool>(nameof(is_expanded));
|
||||
meta = OrderDataHelper.get_metadata(order_type);
|
||||
_def_change_notifier = new DeferrableActionWrapper(() => definition_changed?.Invoke());
|
||||
@@ -127,6 +134,10 @@ namespace mbts.spark {
|
||||
switch (e.PropertyName) {
|
||||
// ignore props that don't affect the resultant AlgoDef
|
||||
case nameof(is_expanded):
|
||||
case nameof(can_edit_values):
|
||||
case nameof(can_edit_structure):
|
||||
case nameof(can_save_values):
|
||||
case nameof(can_save_structure):
|
||||
return;
|
||||
default:
|
||||
_def_change_notifier.execute();
|
||||
@@ -137,7 +148,10 @@ namespace mbts.spark {
|
||||
CompositeDisposable def_sub = null;
|
||||
sd.selector.selected_algo_changed += subscribe_new_algo;
|
||||
return new CompositeDisposable(
|
||||
ObservableAutoWrapper.try_dispose_on_removed(sd.selector.algos, true)) {
|
||||
ObservableAutoWrapper.connect_item_hooks(sd.selector.algos, a => {
|
||||
a.set_permissions(this.editing_permissions);
|
||||
return a as IDisposable ?? Disposable.empty;
|
||||
}, true)) {
|
||||
() => {
|
||||
sd.selector.selected_algo_changed -= subscribe_new_algo;
|
||||
Disposable.dispose(ref def_sub);
|
||||
@@ -156,7 +170,6 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public event Action definition_changed;
|
||||
event Action<SecurityImmutable?> _security_changed;
|
||||
// core
|
||||
public AlgoDefi definition => new AlgoDefi(this.created_assigned());
|
||||
public AlgoType order_type => OrderDataHelper.get_order_type(new TMutable());
|
||||
@@ -169,14 +182,18 @@ namespace mbts.spark {
|
||||
public dynamic children_vm => _children_vm;
|
||||
public ObservableCollection<IAlgoSelector> child_algo_selectors => _child_algo_selectors;
|
||||
// editing permissions
|
||||
public AlgoEditingPermissions editing_permissions { get => _editing_permissions; private set => Set(ref _editing_permissions, value); }
|
||||
// expand
|
||||
public AlgoEditingPermissions editing_permissions { get; private set; }
|
||||
public bool can_edit_values => EnumHelper.includes_flag(editing_permissions, AlgoEditingPermissions.AllowEditValues);
|
||||
public bool can_edit_structure => EnumHelper.includes_flag(editing_permissions, AlgoEditingPermissions.AllowEditStructure);
|
||||
public bool can_save_values => _can_save_values.Value;
|
||||
public bool can_save_structure => _can_save_structure.Value;
|
||||
// misc
|
||||
public virtual bool can_expand => !meta.NoProps;
|
||||
public virtual bool is_expanded { get => _is_expanded.Value; set => _is_expanded.Value = value; }
|
||||
// protected (observed props)
|
||||
protected OrdersModel model { get; private set; }
|
||||
protected SecurityMD? security_md => _security_input.security_md;
|
||||
protected SecurityImmutable? security => _security_input.security;
|
||||
protected SecurityMD? security_md { get; private set; }
|
||||
protected SecurityImmutable? security => security_md?.security;
|
||||
|
||||
public void assign_to(AlgoDef value) {
|
||||
var algo = new TMutable();
|
||||
@@ -189,6 +206,12 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public void set_permissions(AlgoEditingPermissions editing_permissions) {
|
||||
using (detect_property_changes(nameof(can_edit_values), nameof(can_edit_structure), nameof(can_save_values), nameof(can_save_structure)))
|
||||
this.editing_permissions = editing_permissions;
|
||||
on_permissions_changed();
|
||||
}
|
||||
|
||||
public bool key_match(KeyEventArgs key) =>
|
||||
!string.IsNullOrEmpty(display_name) && key.Key == (Key.A + display_name[0] - 'A');
|
||||
|
||||
@@ -196,31 +219,22 @@ namespace mbts.spark {
|
||||
if (value.CustomCase != order_type)
|
||||
return;
|
||||
var algom = _descriptor.Accessor.GetValue(value.CloneToMutable()) as TMutable ?? new TMutable();
|
||||
var security_src = Observable.Create<SecurityImmutable?>(o => {
|
||||
o.OnNext(security);
|
||||
_security_changed += o.OnNext;
|
||||
return Disposable.Create(() => _security_changed -= o.OnNext);
|
||||
});
|
||||
Disposable.replace(ref _pending_load,
|
||||
Disposable.Create(
|
||||
ObservableAutoWrapper.connect_item_hooks(
|
||||
_child_algo_selector_data.AsCollection(),
|
||||
sd => security_src.Subscribe(_ => ObservableAutoWrapper.connect_item_actions(_child_algo_selector_data.AsCollection(), selector_load_impl))),
|
||||
new CompositeDisposable(
|
||||
ObservableAutoWrapper.connect_item_actions(_child_algo_selector_data.AsCollection(), sd => {
|
||||
var inner_def = sd.get_order(value);
|
||||
if (inner_def.HasValue) {
|
||||
if (security.HasValue) {
|
||||
inner_def = OrderDataHelper.fit_to_type(inner_def.Value, security.Value.ContractCase);
|
||||
if (!inner_def.HasValue)
|
||||
return;
|
||||
}
|
||||
var vm = sd.selector.algos.GetOrAdd(OrderDataHelper.get_shallow_factory_key(inner_def.Value), k => create(k, inner_def.Value.display_name(), inner_def.Value.CustomCase));
|
||||
vm.load_from(inner_def.Value);
|
||||
sd.selector.selected_algo = vm;
|
||||
}
|
||||
}),
|
||||
ObservableAutoWrapper.connect_item_actions(_auto_props.AsCollection(), p => p.load(algom))));
|
||||
|
||||
void selector_load_impl(AlgoSelectorData sd) {
|
||||
var inner_def = sd.get_order(value);
|
||||
if (inner_def.HasValue) {
|
||||
if (security.HasValue) {
|
||||
inner_def = OrderDataHelper.fit_to_type(inner_def.Value, security.Value.ContractCase);
|
||||
if (!inner_def.HasValue)
|
||||
return;
|
||||
}
|
||||
var vm = sd.selector.algos.GetOrAdd(OrderDataHelper.get_shallow_factory_key(inner_def.Value), k => create(k, inner_def.Value.display_name(), inner_def.Value.CustomCase, is_expanded: is_expanded));
|
||||
vm.load_from(inner_def.Value);
|
||||
sd.selector.selected_algo = vm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task<AlgoDefi?> load_from_async(AlgoDefi defi) {
|
||||
@@ -232,7 +246,7 @@ namespace mbts.spark {
|
||||
foreach (var sd in _child_algo_selector_data) {
|
||||
var inner_def = sd.get_order(defi);
|
||||
if (inner_def.HasValue) {
|
||||
var vm = sd.selector.algos.GetOrAdd(OrderDataHelper.get_shallow_factory_key(inner_def.Value), k => create(k, inner_def.Value.display_name(), inner_def.Value.CustomCase, is_expanded: is_expanded));
|
||||
var vm = sd.selector.algos.GetOrAdd(OrderDataHelper.get_shallow_factory_key(inner_def.Value), k => create(k, inner_def.Value.display_name(), inner_def.Value.CustomCase));
|
||||
await vm.load_from_async(inner_def.Value);
|
||||
sd.selector.selected_algo = vm;
|
||||
}
|
||||
@@ -246,6 +260,11 @@ namespace mbts.spark {
|
||||
|
||||
protected virtual Task on_load_from_async(AlgoDefi defi) => Task.CompletedTask;
|
||||
|
||||
protected virtual void on_permissions_changed() {
|
||||
foreach (var vm in child_algo_selectors.SelectMany(s => s.algos.AsCollection()))
|
||||
vm.set_permissions(editing_permissions);
|
||||
}
|
||||
|
||||
protected void raise_definition_changed() =>
|
||||
_def_change_notifier.execute();
|
||||
|
||||
@@ -318,11 +337,12 @@ namespace mbts.spark {
|
||||
protected Property<TImmutableValue> add_property<TMutableValue, TImmutableValue>(string prop_name, int target_field_number, TImmutableValue init_value = default) where TMutableValue : class, IMessage, new() where TImmutableValue : IImmutableMessage {
|
||||
var prop = backing(prop_name, init_value);
|
||||
var field_desc = new TMutable().Descriptor.FindFieldByNumber(target_field_number);
|
||||
var immutable_factory = OrderTypeVMUtils.immutable_factory<TMutableValue, TImmutableValue>();
|
||||
add_property<TMutableValue>(
|
||||
prop_name,
|
||||
mdef => field_desc.Accessor.GetValue(mdef) as TMutableValue,
|
||||
(mdef, mval) => field_desc.Accessor.SetValue(mdef, mval),
|
||||
mval => prop.set(ProtoHelper.immutable_factory<TMutableValue, TImmutableValue>(mval ?? new TMutableValue())),
|
||||
mval => prop.set(immutable_factory(mval ?? new TMutableValue())),
|
||||
() => {
|
||||
// TODO: perhaps CloneToMutable() should be a member of IImmutableMessage.
|
||||
// this method makes things so simple for the caller...but this dynamic cast is a little sketch.
|
||||
@@ -361,7 +381,6 @@ namespace mbts.spark {
|
||||
|
||||
protected virtual void on_model_internal() { }
|
||||
protected virtual void on_security_md_internal() { }
|
||||
protected virtual void on_security_internal() { }
|
||||
|
||||
// model
|
||||
void IObserver<OrdersModel>.OnNext(OrdersModel model) {
|
||||
@@ -381,40 +400,14 @@ namespace mbts.spark {
|
||||
void IObserver<OrdersModel>.OnCompleted() { }
|
||||
// security md
|
||||
void IObserver<SecurityMD?>.OnNext(SecurityMD? security_md) {
|
||||
using (_def_change_notifier.defer_execution())
|
||||
using (_update_sec_input())
|
||||
_security_input = _security_input.with_security_md(security_md);
|
||||
this.security_md = security_md;
|
||||
using (_def_change_notifier.defer_execution()) {
|
||||
on_security_md_internal();
|
||||
_populate_selectors();
|
||||
}
|
||||
}
|
||||
void IObserver<SecurityMD?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityMD?>.OnCompleted() { }
|
||||
// security
|
||||
void IObserver<SecurityImmutable?>.OnNext(SecurityImmutable? security) {
|
||||
using (_def_change_notifier.defer_execution())
|
||||
using (_update_sec_input())
|
||||
_security_input = _security_input.with_security(security);
|
||||
}
|
||||
|
||||
void IObserver<SecurityImmutable?>.OnError(Exception error) { }
|
||||
void IObserver<SecurityImmutable?>.OnCompleted() { }
|
||||
// editing permissions
|
||||
void IObserver<AlgoEditingPermissions>.OnNext(AlgoEditingPermissions editing_permissions) => this.editing_permissions = editing_permissions;
|
||||
void IObserver<AlgoEditingPermissions>.OnError(Exception error) { }
|
||||
void IObserver<AlgoEditingPermissions>.OnCompleted() { }
|
||||
|
||||
IDisposable _update_sec_input() {
|
||||
var old_sec = security;
|
||||
var old_sec_md = security_md;
|
||||
return Disposable.Create(() => {
|
||||
if (security != old_sec) {
|
||||
on_security_internal();
|
||||
if (_editing_permissions.HasFlag(AlgoEditingPermissions.AllowEditStructure))
|
||||
_populate_selectors();
|
||||
_security_changed?.Invoke(security);
|
||||
}
|
||||
if (security_md != old_sec_md)
|
||||
on_security_md_internal();
|
||||
});
|
||||
}
|
||||
|
||||
IDisposable _selector_data_hooks;
|
||||
void _populate_selectors() {
|
||||
@@ -433,40 +426,6 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
// security input may come in 2 forms:
|
||||
// 1) SecurityImmutable?
|
||||
// 2) SecurityMD?
|
||||
// This structure resolves conflicts that may arise from the different inputs
|
||||
struct SecurityInput {
|
||||
readonly SecurityMD? _raw_security_md;
|
||||
readonly SecurityImmutable? _raw_security;
|
||||
|
||||
public readonly SecurityMD? security_md;
|
||||
public readonly SecurityImmutable? security;
|
||||
|
||||
SecurityInput(SecurityImmutable? raw_security, SecurityMD? raw_security_md, SecurityImmutable? security, SecurityMD? security_md) {
|
||||
_raw_security = raw_security;
|
||||
_raw_security_md = raw_security_md;
|
||||
this.security_md = security_md;
|
||||
this.security = security;
|
||||
}
|
||||
|
||||
public SecurityInput with_security(SecurityImmutable? security) =>
|
||||
new SecurityInput(
|
||||
security,
|
||||
_raw_security_md,
|
||||
security,
|
||||
security.HasValue && _raw_security_md.HasValue && _raw_security_md.Value.security.Id != security.Value.Id ?
|
||||
null : _raw_security_md);
|
||||
|
||||
public SecurityInput with_security_md(SecurityMD? security_md) =>
|
||||
new SecurityInput(
|
||||
_raw_security,
|
||||
security_md,
|
||||
security_md?.security ?? _raw_security,
|
||||
security_md);
|
||||
}
|
||||
|
||||
struct AlgoSelectorData {
|
||||
public readonly string prop_name;
|
||||
public readonly AlgoSelector selector;
|
||||
@@ -486,15 +445,14 @@ namespace mbts.spark {
|
||||
IReadOnlyDictionary<AlgoKey, AlgoData> _pending_data;
|
||||
AlgoKey? _pending_default;
|
||||
|
||||
public AlgoSelector() {
|
||||
public AlgoSelector() =>
|
||||
algos_view = ObservableAutoWrapper.create_sorted_wrapper(algos, AlgoCollectionHelper.algo_vm_comparer);
|
||||
selected_algo_changed += (o, n) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(selected_algo)));
|
||||
}
|
||||
|
||||
public void load_algo_data(IReadOnlyDictionary<AlgoKey, AlgoData> algo_data, AlgoKey? default_algo) {
|
||||
_pending_data = algo_data;
|
||||
_pending_default = default_algo;
|
||||
_sync_formats();
|
||||
selected_algo_changed += (o, n) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(selected_algo)));
|
||||
}
|
||||
|
||||
public event PropertyChange<IAlgoVM> selected_algo_changed;
|
||||
@@ -555,4 +513,4 @@ namespace mbts.spark {
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts;
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.utility;
|
||||
@@ -12,7 +11,7 @@ using SpreaderDefi = bts.api.orderman.SpreaderOrderDefinitionImmutable;
|
||||
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
class SpreaderOrderVM : OrderTypeVMBase<SpreaderDef, SpreaderDefi>, IAlgoVM, ILazyAlgo, IObserver<OrdersModel> {
|
||||
class SpreaderOrderVM : OrderTypeVMBase<SpreaderDef, SpreaderDefi>, IAlgoVM, ILazyAlgo, IObserver<SecurityMD?>, IObserver<OrdersModel> {
|
||||
readonly Property<SpreaderDefi?> _template;
|
||||
readonly StrategyEditorWrapper _strategy_editor_wrapper;
|
||||
IDisposable _strategy_sub;
|
||||
@@ -79,15 +78,16 @@ namespace mbts.spark {
|
||||
set_error(nameof(template), "Waiting for security and/or strategy.", true);
|
||||
else clear_error(nameof(template));
|
||||
}
|
||||
|
||||
protected override void on_security_md_internal() => _reset_strategy_sub();
|
||||
protected override void on_model_internal() => _reset_strategy_sub();
|
||||
protected override void on_security_internal() => _reset_strategy_sub();
|
||||
|
||||
void _reset_strategy_sub() {
|
||||
Disposable.dispose(ref _strategy_sub);
|
||||
if (model != null) {
|
||||
if (security.HasValue) {
|
||||
_strategy_sub = model.spreader.security_strategy(security.Value.Id)
|
||||
.Select(strategy => new SpreaderDefi?(model.factory.spreader_orderi(security.Value, strategy)))
|
||||
if (security_md.HasValue) {
|
||||
_strategy_sub = model.spreader.security_strategy(security_md.Value.security.Id)
|
||||
.Select(strategy => new SpreaderDefi?(model.factory.spreader_orderi(security_md.Value.security, strategy)))
|
||||
.OnDispatcher()
|
||||
.Subscribe(s => {
|
||||
is_loaded = true;
|
||||
|
||||
@@ -130,4 +130,4 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,11 @@ using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.api.orderman;
|
||||
using bts.api.position;
|
||||
using bts.md;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
using mbts.config;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
@@ -16,8 +18,10 @@ using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading.Tasks;
|
||||
using OrderUpdate = bts.api.core.TableUpdate<(bts.api.SecurityImmutable? sec, bts.api.orderman.OrderRecordImmutable order), uint>;
|
||||
using OrderUpdatePiq = bts.api.core.TableUpdate<bts.api.orderman.OrderSecurity, uint>;
|
||||
|
||||
namespace mbts.spark {
|
||||
[Export]
|
||||
@@ -26,21 +30,26 @@ namespace mbts.spark {
|
||||
readonly OrderFactory _factory;
|
||||
readonly OrderService _service;
|
||||
readonly PositionService _position_service;
|
||||
readonly MBOService _mbo_service;
|
||||
readonly SpreaderModel _spreader;
|
||||
readonly ErrorReporter _error;
|
||||
readonly DynamicOrderHelper _dynamic_helper;
|
||||
readonly OrderActivitySummarizer _activity;
|
||||
readonly TableUpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_demuxer;
|
||||
// we need a new one of these for the time being since the ladder is subscribing to orders with piq
|
||||
readonly TableUpdateDemuxer<OrderRecordImmutable, uint, uint> _security_orders_piq_demuxer;
|
||||
readonly TableUpdateDemuxer<PositionUnitImmutable, int, uint> _security_positions_demuxer;
|
||||
IDisposable _dispose;
|
||||
static readonly bool _piq_enabled = Configuration.get<bts.config.toggles.PlacementInQueue>().enabled;
|
||||
|
||||
[ImportingConstructor]
|
||||
OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service,
|
||||
OrdersModel(IBus bus, OrderFactory factory, OrderService service, PositionService position_service, MBOService mbo_service,
|
||||
SpreaderModel spreader, DynamicOrderHelper dynamic_helper, AdminModel admin_model, IAlgoStudioModel algo_model, SecurityStore sstore, ErrorReporter error) {
|
||||
_bus = bus;
|
||||
_factory = factory;
|
||||
_service = service;
|
||||
_position_service = position_service;
|
||||
_mbo_service = mbo_service;
|
||||
_spreader = spreader;
|
||||
_error = error;
|
||||
_dynamic_helper = dynamic_helper;
|
||||
@@ -48,13 +57,34 @@ namespace mbts.spark {
|
||||
this.algo_model = algo_model;
|
||||
var scheduler = ThreadPoolScheduler.Instance;
|
||||
var interval = TimeSpan.FromMilliseconds(100);
|
||||
on_orders =
|
||||
on_orders =
|
||||
_service
|
||||
.subscribei_orders()
|
||||
.Naggle(interval, scheduler)
|
||||
.PermissionFilter(admin_model.on_admin_data)
|
||||
.join(sstore, order => order.security_key())
|
||||
.Table().Cache();
|
||||
.Table()
|
||||
.Cache();
|
||||
|
||||
if (_piq_enabled) {
|
||||
on_orders_with_piq = on_orders
|
||||
.LeftJoin<
|
||||
(SecurityImmutable?, OrderRecordImmutable),
|
||||
OrderPIQImmutable,
|
||||
OrderSecurity,
|
||||
uint
|
||||
>(mbo_service.subscribei_piq().Table(),
|
||||
si_ort => si_ort.Item2.Key,
|
||||
p => p.OrderId,
|
||||
(si_ort, r, p) => new OrderSecurity(si_ort.Item1, si_ort.Item2, p.Position),
|
||||
(si_ort, r) => new OrderSecurity(si_ort.Item1, si_ort.Item2, null),
|
||||
osp => osp.order.Key);
|
||||
} else {
|
||||
on_orders_with_piq = on_orders
|
||||
.Select((ft) => new OrderSecurity(ft.Item1, ft.Item2, null), os => os.order.Key)
|
||||
.Replay(1);
|
||||
}
|
||||
|
||||
on_positions = _position_service.subscribei_position_data().Naggle(interval, scheduler).PermissionFilter(admin_model.on_admin_data).Table().Cache();
|
||||
working_orders =
|
||||
on_orders.Scan(
|
||||
@@ -62,6 +92,13 @@ namespace mbts.spark {
|
||||
(workers, table) => _update_working_orders(workers, table))
|
||||
.Replay(1).RefCount();
|
||||
|
||||
working_orders_with_piq =
|
||||
on_orders_with_piq.Scan(
|
||||
ImmutableDictionary<uint, OrderSecurity>.Empty,
|
||||
(workers, table) => _update_working_orders(workers, table))
|
||||
.Replay(1).RefCount();
|
||||
|
||||
|
||||
on_order_securities =
|
||||
on_orders
|
||||
.as_iincremental()
|
||||
@@ -70,6 +107,7 @@ namespace mbts.spark {
|
||||
|
||||
_activity = new OrderActivitySummarizer(bus, on_orders);
|
||||
_security_orders_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
|
||||
_security_orders_piq_demuxer = new TableUpdateDemuxer<OrderRecordImmutable, uint, uint>(o => o.Key, o => o.security_key());
|
||||
_security_positions_demuxer = new TableUpdateDemuxer<PositionUnitImmutable, int, uint>(o => o.RecordId, p => p.SecurityId);
|
||||
var algo_meta_join = algo_model.definitions.LeftJoin<AlgoData, AlgoDefinitionMetadata, (AlgoData algo, AlgoDefinitionMetadata? meta), AlgoKey>(
|
||||
algo_model.metadata_service.subscribe_metadata().Table(m => m.id),
|
||||
@@ -86,7 +124,7 @@ namespace mbts.spark {
|
||||
(os, okey, am, mkey) => new OrderSecurityAlgoMeta(os.order, os.sec, am.algo, am.meta),
|
||||
(os, okey, mkey) => new OrderSecurityAlgoMeta(os.order, os.sec, null, null),
|
||||
ot => ot.order.Key);
|
||||
on_order_tuples = order_secs_algo_meta_join.Cache();
|
||||
on_order_tuples = order_secs_algo_meta_join;
|
||||
var on_fills =
|
||||
_service.subscribei_fills()
|
||||
.Naggle(interval, scheduler)
|
||||
@@ -101,14 +139,18 @@ namespace mbts.spark {
|
||||
(f, fk, o, ok) => new FillTuple(f, o),
|
||||
(f, fk, ok) => new FillTuple(f, null),
|
||||
f => f.fill.FillKey);
|
||||
on_fill_tuples = fill_order_tuples_join.Cache();
|
||||
on_fill_tuples = fill_order_tuples_join;
|
||||
_dispose = Disposable.Create(
|
||||
on_orders.Select(t => t.order, o => o.Key).Subscribe(_security_orders_demuxer),
|
||||
on_orders_with_piq.RefCount().Select(t => t.order, o => o.Key).Subscribe(_security_orders_piq_demuxer),
|
||||
on_positions.Subscribe(_security_positions_demuxer),
|
||||
order_secs_algo_meta_join.Connect(),
|
||||
algo_meta_join.Connect(),
|
||||
fill_order_tuples_join.Connect(),
|
||||
_bus.subscribe(SparkTags.CreateOrder).Subscribe(a => send_order(a.definition, a.price, a.size).ignore()),
|
||||
_bus.subscribe(SparkTags.RecreateOrder).Subscribe(a => a.ForEach(x => send_order(x.definition, x.price, x.size).ignore())),
|
||||
_bus.subscribe(SparkTags.CancelOrders).Subscribe(x => cancel_orders(x).ignore()),
|
||||
_bus.subscribe(SparkTags.DetachOrders).Subscribe(x => detach_orders(x).ignore()),
|
||||
_bus.subscribe(SparkTags.CrackOrders).Subscribe(x => crack_orders(x).ignore()),
|
||||
_bus.subscribe(SparkTags.UpdateOrder).Subscribe(x => this.update_order(x.key, x.price, x.size).ignore())
|
||||
);
|
||||
}
|
||||
@@ -118,11 +160,14 @@ namespace mbts.spark {
|
||||
public SpreaderModel spreader => _spreader;
|
||||
public DynamicOrderHelper dynamic_helper => _dynamic_helper;
|
||||
public IObservable<OrderUpdate> on_orders { get; }
|
||||
public IConnectableObservable<OrderUpdatePiq> on_orders_with_piq { get; }
|
||||
public IObservable<TableUpdate<OrderSecurityAlgoMeta, uint>> on_order_tuples { get; }
|
||||
public IObservable<IIncrementalUpdate<SecurityImmutable, uint>> on_order_securities { get; }
|
||||
public IObservable<TableUpdate<FillTuple, string>> on_fill_tuples { get; }
|
||||
public IObservable<IReadOnlyDictionary<uint,OrderSecurity>> working_orders { get; }
|
||||
public IObservable<IReadOnlyDictionary<uint, OrderSecurity>> working_orders_with_piq { get; }
|
||||
public IObservable<TableUpdate<PositionUnitImmutable, int>> on_positions { get; }
|
||||
|
||||
public AdminModel admin_model { get; }
|
||||
public IAlgoStudioModel algo_model { get; }
|
||||
|
||||
@@ -163,37 +208,27 @@ namespace mbts.spark {
|
||||
return success;
|
||||
}
|
||||
|
||||
public async Task<bool> update_order(OrderRecordImmutable order, double? new_price = null, uint? new_size = null, CustomOrderDefinitionImmutable? new_algo = null) {
|
||||
if (order.working_quantity() <= 0) {
|
||||
// order already filled to completion
|
||||
return false;
|
||||
}
|
||||
var size = new_size ?? (uint)order.working_quantity();
|
||||
var price = new_price ?? order.price();
|
||||
if (size == order.working_quantity() && price == order.price() && !new_algo.HasValue) {
|
||||
// no parameters would be changed by this update
|
||||
return false;
|
||||
}
|
||||
_activity.update_order(order, price, size, new_algo.HasValue);
|
||||
public async Task<bool> update_order(OrderRecordImmutable order, double price, uint size) {
|
||||
if (order.working_quantity() <= 0) return false;
|
||||
_activity.update_order(order, price, size);
|
||||
var result = await _service.update_order_async(new Update() {
|
||||
OrderKey = order.Key,
|
||||
Price = price,
|
||||
Quantity = size,
|
||||
Custom = new_algo?.CloneToMutable()
|
||||
Quantity = size
|
||||
});
|
||||
(var key, string msg) = _handle_error(result, "Failed to update order: {0}");
|
||||
bool success = key.HasValue;
|
||||
if (!success) {
|
||||
_activity.update_order_failed(order, price, size, new_algo.HasValue);
|
||||
_activity.update_order_failed(order, price, size);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public Task<bool[]> detach_orders(IEnumerable<uint> orders) =>
|
||||
Task.WhenAll(orders.Select(detach_order));
|
||||
public Task<bool[]> crack_orders(IEnumerable<uint> orders) =>
|
||||
Task.WhenAll(orders.Select(crack_order));
|
||||
|
||||
public async Task<bool> detach_order(uint order) {
|
||||
var result = await _service.detach_order_async(new Detach() { OrderKey = order });
|
||||
public async Task<bool> crack_order(uint order) {
|
||||
var result = await _service.crack_order_async(new Crack() { OrderKey = order });
|
||||
return _handle_error(result, "Failed to break order: {0}").key.HasValue;
|
||||
}
|
||||
|
||||
@@ -204,8 +239,30 @@ namespace mbts.spark {
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
|
||||
static ImmutableDictionary<uint, OrderSecurity>
|
||||
_update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdate table) {
|
||||
static ImmutableDictionary<uint, OrderSecurity> _update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdate table) {
|
||||
var clone = target.ToBuilder();
|
||||
foreach (var o in table.removed) {
|
||||
if (o.order.is_active()) {
|
||||
clone.Remove(o.order.Key);
|
||||
}
|
||||
}
|
||||
foreach (var o in table.changes) {
|
||||
if (o.Old.order.is_active()) {
|
||||
clone.Remove(o.Old.order.Key);
|
||||
}
|
||||
if (o.New.order.is_active()) {
|
||||
clone.Add(o.New.order.Key, o.New);
|
||||
}
|
||||
}
|
||||
foreach (var o in table.added) {
|
||||
if (o.order.is_active()) {
|
||||
clone.Add(o.order.Key, o);
|
||||
}
|
||||
}
|
||||
return clone.ToImmutableDictionary();
|
||||
}
|
||||
|
||||
static ImmutableDictionary<uint, OrderSecurity> _update_working_orders(ImmutableDictionary<uint, OrderSecurity> target, OrderUpdatePiq table) {
|
||||
var clone = target.ToBuilder();
|
||||
foreach (var o in table.removed) {
|
||||
if (o.order.is_active()) {
|
||||
@@ -229,6 +286,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// usinig new OrdersModel extensions to reduce the amount of code change needed to switch uis over to the new OrdersModel,
|
||||
// without changing any code in the actual new OrdersModel class.
|
||||
static class OrdersModelExtensions {
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace mbts {
|
||||
// old order will be a copy of new order, except with filledQuantity set to 0
|
||||
var old_state = ort.order.State.CloneToMutable();
|
||||
old_state.FilledQuantity = 0;
|
||||
return (new OrderSecurity(ort.security, ort.order.with(state: old_state)), ort);
|
||||
return (new OrderSecurity(ort.security, ort.order.with(state: old_state), ort.piq), ort);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace mbts {
|
||||
SecurityPredicate _security;
|
||||
SidePredicate _side;
|
||||
DoublePredicate _price, _last_price, _avg_price;
|
||||
UIntPredicate _quantity, _filled_quantity, _security_key, _order_key, _parent_key, _attached_target_order_key;
|
||||
UIntPredicate _quantity, _filled_quantity, _security_key, _order_key, _parent_key;
|
||||
OrderStatusPredicate _order_status;
|
||||
OrderTypePredicate _order_type;
|
||||
OrderSubTypePredicate _order_sub_type;
|
||||
@@ -125,9 +125,7 @@ namespace mbts {
|
||||
[PredicateCase("When order is completed", PredicateCategory.Event)]
|
||||
CompletedEvent,
|
||||
[PredicateCase("When order is canceled", PredicateCategory.Event)]
|
||||
CanceledEvent,
|
||||
[PredicateCase(is_toastable:false)]
|
||||
AttachedTargetOrderKey
|
||||
CanceledEvent
|
||||
}
|
||||
|
||||
[ProtoMember(1)]
|
||||
@@ -178,8 +176,6 @@ namespace mbts {
|
||||
public OrderCanceledEventPredicate canceled_event { get => _canceled_event; set => _set(ref _canceled_event, value); }
|
||||
[ProtoMember(24)]
|
||||
public bool is_toast_enabled { get; set; }
|
||||
[ProtoMember(25)]
|
||||
public UIntPredicate attached_order_target_key { get => _attached_target_order_key; set => _set(ref _attached_target_order_key, value); }
|
||||
|
||||
public PTOC predicate_type_case {
|
||||
get {
|
||||
@@ -206,7 +202,6 @@ namespace mbts {
|
||||
if (_filled_event != null) return PTOC.FilledEvent;
|
||||
if (_completed_event != null) return PTOC.CompletedEvent;
|
||||
if (_canceled_event != null) return PTOC.CanceledEvent;
|
||||
if (_attached_target_order_key != null) return PTOC.AttachedTargetOrderKey;
|
||||
return PTOC.None;
|
||||
}
|
||||
}
|
||||
@@ -236,7 +231,6 @@ namespace mbts {
|
||||
case PTOC.FilledEvent: return _filled_event.create_predicate(data_cache);
|
||||
case PTOC.CompletedEvent: return _completed_event.create_predicate(data_cache);
|
||||
case PTOC.CanceledEvent: return _canceled_event.create(data_cache);
|
||||
case PTOC.AttachedTargetOrderKey: return wrap(_attached_target_order_key, r => r.order.attached_target_order_key());
|
||||
default: return null;
|
||||
}
|
||||
//
|
||||
@@ -285,7 +279,6 @@ namespace mbts {
|
||||
case PTOC.FilledEvent: return _filled_event.Equals(other._filled_event);
|
||||
case PTOC.CompletedEvent: return _completed_event.Equals(other._completed_event);
|
||||
case PTOC.CanceledEvent: return _canceled_event.Equals(other._canceled_event);
|
||||
case PTOC.AttachedTargetOrderKey: return _attached_target_order_key.Equals(other._attached_target_order_key);
|
||||
case PTOC.None: return true;
|
||||
default: throw ExceptionHelper.unexpected_oneof_case(ptoc);
|
||||
}
|
||||
@@ -320,7 +313,6 @@ namespace mbts {
|
||||
case PTOC.FilledEvent: raw_hash = _filled_event.GetHashCode(); break;
|
||||
case PTOC.CompletedEvent: raw_hash = _completed_event.GetHashCode(); break;
|
||||
case PTOC.CanceledEvent: raw_hash = _canceled_event.GetHashCode(); break;
|
||||
case PTOC.AttachedTargetOrderKey: raw_hash = _attached_target_order_key.GetHashCode(); break;
|
||||
case PTOC.None: raw_hash = 0; break;
|
||||
default: throw ExceptionHelper.unexpected_oneof_case(ptoc);
|
||||
}
|
||||
@@ -355,7 +347,6 @@ namespace mbts {
|
||||
_filled_event = null;
|
||||
_completed_event = null;
|
||||
_canceled_event = null;
|
||||
_attached_target_order_key = null;
|
||||
}
|
||||
field = value;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
<Authors>Blue Trading Systems</Authors>
|
||||
<Company>Blue Trading Systems</Company>
|
||||
<Product>Spark</Product>
|
||||
<Description>The game has changed. Make your Move.</Description>
|
||||
<Description>Advanced Derivatives Trading</Description>
|
||||
<ApplicationManifest>spark.manifest</ApplicationManifest>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>x64</Platforms>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:bv="clr-namespace:bts.view"
|
||||
xmlns:mbts="clr-namespace:mbts.ui"
|
||||
xmlns:control="clr-namespace:mbts.ui.controls"
|
||||
>
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mbts="clr-namespace:mbts.ui"
|
||||
xmlns:control="clr-namespace:mbts.ui.controls">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/Images/Vector/Icons.xaml" />
|
||||
<ResourceDictionary Source="/view/Resources/Converters.xaml" />
|
||||
@@ -519,16 +516,9 @@
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<control:SparkMenuItem
|
||||
Command="{Binding command}" Style="{StaticResource SparkMenuButton}" Icon="{Binding icon}"
|
||||
Content="{Binding title}" Command="{Binding command}" Style="{StaticResource SparkMenuButton}" Icon="{Binding icon}"
|
||||
Collapsed="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type control:SparkMenu}}, Path=ShowText, Converter={StaticResource NotConverter}}"
|
||||
>
|
||||
<control:SparkMenuItem.Content>
|
||||
<MultiBinding Converter="{x:Static bv:SparkWindowView.HeaderConverter}">
|
||||
<Binding Path="title"/>
|
||||
<Binding Path="key_gesture"/>
|
||||
</MultiBinding>
|
||||
</control:SparkMenuItem.Content>
|
||||
</control:SparkMenuItem>
|
||||
/>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using mbts;
|
||||
using mbts.ui;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -12,15 +11,13 @@ using static mbts.spark.SparkTags;
|
||||
|
||||
namespace bts.spark {
|
||||
public abstract class SparkCommand {
|
||||
public readonly KeyGesture key_gesture;
|
||||
string _title;
|
||||
bool _show_in_toolbar, _show_in_context_menu;
|
||||
public SparkCommand(string title, ImageSource icon, bool show_in_toolbar = true, bool show_in_context_menu = true, KeyGesture key_gesture = null) {
|
||||
public SparkCommand(string title, ImageSource icon, bool show_in_toolbar = true, bool show_in_context_menu = true) {
|
||||
_title = title;
|
||||
this.icon = icon;
|
||||
_show_in_toolbar = show_in_toolbar;
|
||||
_show_in_context_menu = show_in_context_menu;
|
||||
this.key_gesture = key_gesture;
|
||||
}
|
||||
|
||||
public event Action title_changed, show_in_toolbar_changed, show_in_context_menu_changed;
|
||||
@@ -210,8 +207,7 @@ namespace bts.spark {
|
||||
public class SparkCommandCommand : SparkCommand {
|
||||
readonly ICommand _a;
|
||||
|
||||
public SparkCommandCommand(string title, ImageSource icon, ICommand a, KeyGesture key_gesture = null)
|
||||
: base(title, icon, key_gesture: key_gesture) {
|
||||
public SparkCommandCommand(string title, ImageSource icon, ICommand a) : base(title, icon) {
|
||||
_a = a;
|
||||
}
|
||||
|
||||
@@ -329,7 +325,6 @@ namespace bts.spark {
|
||||
}
|
||||
|
||||
public string title => _cmd.title;
|
||||
public KeyGesture key_gesture => _cmd.key_gesture;
|
||||
public ImageSource icon => _cmd.icon;
|
||||
public bool show_in_toolbar => _cmd.show_in_toolbar;
|
||||
public bool show_in_context_menu => _cmd.show_in_context_menu;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace mbts.spark
|
||||
public static ITag<IEnumerable<(OrderDefinitionImmutable definition, double price, uint size)>> RecreateOrder { get; } = new SparkTag<IEnumerable<(OrderDefinitionImmutable, double, uint)>>();
|
||||
|
||||
public static ITag<IEnumerable<uint>> CancelOrders { get; } = new SparkTag<IEnumerable<uint>>();
|
||||
public static ITag<IEnumerable<uint>> DetachOrders { get; } = new SparkTag<IEnumerable<uint>>();
|
||||
public static ITag<IEnumerable<uint>> CrackOrders { get; } = new SparkTag<IEnumerable<uint>>();
|
||||
public static ITag<(uint key, double price, uint size)> UpdateOrder { get; } = new SparkTag<(uint, double, uint)>();
|
||||
|
||||
public static ITag<uint> ShowModifyOrderTag { get; } = new SparkTag<uint>(nameof(ShowModifyOrderTag));
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace mbts.ui.controls {
|
||||
var rv = new ObservableAggregateCollection<TInner>();
|
||||
var aggregator = new Aggregator<TInner>(rv);
|
||||
foreach (var o in source)
|
||||
aggregator.Add(select_inner(o));
|
||||
aggregator.add_collection(select_inner(o));
|
||||
source.CollectionChanged += _mk_outter_collection_changed_handler(aggregator, select_inner);
|
||||
return rv;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace mbts.ui.controls {
|
||||
aggregate.Clear();
|
||||
var aggregator = new Aggregator<TInner>(aggregate);
|
||||
foreach (var o in source)
|
||||
aggregator.Add(select_inner(o));
|
||||
aggregator.add_collection(select_inner(o));
|
||||
var handler = _mk_outter_collection_changed_handler(aggregator, select_inner);
|
||||
source.CollectionChanged += handler;
|
||||
return Disposable.Create(() => source.CollectionChanged -= handler);
|
||||
@@ -37,23 +37,23 @@ namespace mbts.ui.controls {
|
||||
add_collections(e.NewStartingIndex, e.NewItems);
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
aggregator.Clear();
|
||||
aggregator.clear_collections();
|
||||
foreach (TOutter o in (IEnumerable<TOutter>)s)
|
||||
aggregator.Add(select_inner(o));
|
||||
aggregator.add_collection(select_inner(o));
|
||||
break;
|
||||
}
|
||||
};
|
||||
void add_collections(int new_item_index, IList new_items) {
|
||||
foreach (TOutter o in new_items)
|
||||
aggregator.Insert(new_item_index++, select_inner(o));
|
||||
aggregator.insert_collection(new_item_index++, select_inner(o));
|
||||
}
|
||||
void remove_collections(int old_item_index, IList old_items) {
|
||||
foreach (TOutter o in old_items)
|
||||
aggregator.RemoveAt(old_item_index);
|
||||
aggregator.remove_collection_at(old_item_index);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Aggregator<T> : IDisposable, IList<ObservableCollection<T>>, IReadOnlyList<ObservableCollection<T>> {
|
||||
internal class Aggregator<T> : IDisposable {
|
||||
readonly ObservableCollection<T> _target;
|
||||
readonly List<ObservableCollection<T>> _collections = new List<ObservableCollection<T>>();
|
||||
bool _is_disposed;
|
||||
@@ -61,39 +61,28 @@ namespace mbts.ui.controls {
|
||||
public Aggregator(ObservableCollection<T> target, params ObservableCollection<T>[] collections) {
|
||||
_target = target;
|
||||
foreach (var c in collections)
|
||||
Add(c);
|
||||
add_collection(c);
|
||||
}
|
||||
|
||||
public ObservableCollection<T> this[int index] {
|
||||
get => _collections[index];
|
||||
set => Insert(index, value);
|
||||
}
|
||||
|
||||
public int Count => _collections.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void Add(ObservableCollection<T> collection) {
|
||||
public void add_collection(ObservableCollection<T> collection) {
|
||||
_check_disposed();
|
||||
_target.AddRange(collection);
|
||||
_collections.Add(collection);
|
||||
collection.CollectionChanged += _on_collection_changed;
|
||||
}
|
||||
|
||||
public bool Remove(ObservableCollection<T> collection) {
|
||||
int num_before = _collections.Count;
|
||||
public void remove_collection(ObservableCollection<T> collection) {
|
||||
_check_disposed();
|
||||
_remove_collection_items(collection, out int _, out int collection_index);
|
||||
_collections.RemoveAt(collection_index);
|
||||
collection.CollectionChanged -= _on_collection_changed;
|
||||
return _collections.Count < num_before;
|
||||
}
|
||||
|
||||
public void Insert(int index, ObservableCollection<T> collection) {
|
||||
public void insert_collection(int index, ObservableCollection<T> collection) {
|
||||
_check_disposed();
|
||||
if (index < 0 || index > _collections.Count) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (index == _collections.Count)
|
||||
Add(collection);
|
||||
add_collection(collection);
|
||||
else {
|
||||
_collections.Insert(index, collection);
|
||||
_on_collection_changed(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection, 0));
|
||||
@@ -101,12 +90,12 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAt(int index) {
|
||||
public void remove_collection_at(int index) {
|
||||
_check_disposed();
|
||||
Remove(_collections[index]);
|
||||
remove_collection(_collections[index]);
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
public void clear_collections() {
|
||||
_check_disposed();
|
||||
foreach (var c in _collections)
|
||||
c.CollectionChanged -= _on_collection_changed;
|
||||
@@ -114,15 +103,6 @@ namespace mbts.ui.controls {
|
||||
_target.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(ObservableCollection<T> item) =>
|
||||
_collections.Contains(item);
|
||||
|
||||
public void CopyTo(ObservableCollection<T>[] array, int arrayIndex) =>
|
||||
_collections.CopyTo(array, arrayIndex);
|
||||
|
||||
public int IndexOf(ObservableCollection<T> item) =>
|
||||
_collections.IndexOf(item);
|
||||
|
||||
public void Dispose() {
|
||||
if (!_is_disposed) {
|
||||
foreach (var c in _collections)
|
||||
@@ -131,9 +111,6 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<ObservableCollection<T>> GetEnumerator() => _collections.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _collections.GetEnumerator();
|
||||
|
||||
void _on_collection_changed(object sender, NotifyCollectionChangedEventArgs e) {
|
||||
_check_disposed();
|
||||
int offset = _get_collection_start(sender);
|
||||
@@ -189,12 +166,10 @@ namespace mbts.ui.controls {
|
||||
public ObservableAggregateCollection(params ObservableCollection<T>[] collections) =>
|
||||
_aggregator = new ObservableAggregateCollection.Aggregator<T>(this, collections);
|
||||
|
||||
public IReadOnlyList<ObservableCollection<T>> source_collections => _aggregator;
|
||||
|
||||
public void add_collection(ObservableCollection<T> collection) => _aggregator.Add(collection);
|
||||
public void remove_collection(ObservableCollection<T> collection) => _aggregator.Remove(collection);
|
||||
public void insert_collection(int index, ObservableCollection<T> collection) => _aggregator.Insert(index, collection);
|
||||
public void remove_collection_at(int index) => _aggregator.RemoveAt(index);
|
||||
public void clear_collections() => _aggregator.Clear();
|
||||
public void add_collection(ObservableCollection<T> collection) => _aggregator.add_collection(collection);
|
||||
public void remove_collection(ObservableCollection<T> collection) => _aggregator.remove_collection(collection);
|
||||
public void insert_collection(int index, ObservableCollection<T> collection) => _aggregator.insert_collection(index, collection);
|
||||
public void remove_collection_at(int index) => _aggregator.remove_collection_at(index);
|
||||
public void clear_collections() => _aggregator.clear_collections();
|
||||
}
|
||||
}
|
||||
@@ -192,16 +192,16 @@ namespace mbts.ui.utility {
|
||||
}
|
||||
}
|
||||
|
||||
public static IDisposable connect_item_actions<T>(IReadOnlyObservableCollection<T> source, Action<T> action) =>
|
||||
connect_item_actions(source, source, action);
|
||||
|
||||
public static IDisposable connect_item_actions<T>(ObservableCollection<T> source, Action<T> action) =>
|
||||
connect_item_actions(source, source, action);
|
||||
|
||||
public static IDisposable connect_item_actions<T>(INotifyCollectionChanged notifier, IEnumerable<T> collection, Action<T> action) {
|
||||
foreach (T v in collection)
|
||||
public static IDisposable connect_item_actions<T>(IReadOnlyObservableCollection<T> source, Action<T> action) {
|
||||
foreach (T v in source)
|
||||
action(v);
|
||||
return notifier.Subscribe((s, e) => CollectionHelper.process_new_items(collection, action, e));
|
||||
return source.Subscribe((s, e) => CollectionHelper.process_new_items(source, action, e));
|
||||
}
|
||||
|
||||
public static IDisposable connect_item_actions<T>(ObservableCollection<T> source, Action<T> action) {
|
||||
foreach (T v in source)
|
||||
action(v);
|
||||
return source.Subscribe((s, e) => CollectionHelper.process_new_items(source, action, e));
|
||||
}
|
||||
|
||||
public static IDisposable connect_item_hooks<T>(IReadOnlyObservableCollection<T> source, Func<T, IDisposable> get_item_hook) {
|
||||
@@ -218,18 +218,6 @@ namespace mbts.ui.utility {
|
||||
source.Subscribe((s, e) => CollectionHelper.reflect_change(item_hooks, source, get_item_hook, d => d.Dispose(), e)));
|
||||
}
|
||||
|
||||
public static IDisposable dispose_on_removed<T>(IReadOnlyObservableCollection<T> source) where T : IDisposable =>
|
||||
connect_item_hooks(source, v => v);
|
||||
|
||||
public static IDisposable dispose_on_removed<T>(ObservableCollection<T> source, bool clear_source_on_dispose) where T : IDisposable =>
|
||||
connect_item_hooks(source, v => v, clear_source_on_dispose);
|
||||
|
||||
public static IDisposable try_dispose_on_removed<T>(IReadOnlyObservableCollection<T> source) =>
|
||||
connect_item_hooks(source, v => v as IDisposable ?? Disposable.empty);
|
||||
|
||||
public static IDisposable try_dispose_on_removed<T>(ObservableCollection<T> source, bool clear_source_on_dispose = false) =>
|
||||
connect_item_hooks(source, v => v as IDisposable ?? Disposable.empty, clear_source_on_dispose);
|
||||
|
||||
class SortedWrapper<T> : IReadOnlyObservableCollection<T> {
|
||||
const int _min_capacity = 4;
|
||||
readonly IComparer<T> _comparer;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace mbts.ui.utilities
|
||||
readonly IEqualityComparer<T> _comparer;
|
||||
readonly TSourceCollection _source;
|
||||
readonly IList<T> _target;
|
||||
IDictionary<T, int> _cache;
|
||||
IDictionary<T, int> _cache = new Dictionary<T, int>();
|
||||
IDisposable _sub;
|
||||
|
||||
public DistinctCollectionResolver(TSourceCollection source, IList<T> target, Func<TSource, T> get_value)
|
||||
@@ -38,7 +38,6 @@ namespace mbts.ui.utilities
|
||||
_target = target;
|
||||
_get_value = get_value;
|
||||
_comparer = comparer;
|
||||
_cache = new Dictionary<T, int>(comparer);
|
||||
_sync();
|
||||
_sub = _source.Subscribe(_source_CollectionChanged);
|
||||
}
|
||||
|
||||
@@ -87,11 +87,7 @@ namespace mbts.ui.utility {
|
||||
foreach (var value in source_collection.OfType<INotifyPropertyChanged>())
|
||||
value.PropertyChanged += on_item_PropertyChanged;
|
||||
}
|
||||
if (source_collection.Count == 0) {
|
||||
filtered_collection.Clear();
|
||||
} else {
|
||||
CollectionHelper.sync_with_never_duplicate(filtered_collection, source_collection.Where(filter));
|
||||
}
|
||||
CollectionHelper.sync_with_never_duplicate(filtered_collection, source_collection.Where(filter));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,4 +213,4 @@ namespace mbts.ui.utility {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,5 @@ namespace mbts {
|
||||
return (uint)value;
|
||||
}
|
||||
}
|
||||
|
||||
public static double? nan_nullable(double value) =>
|
||||
double.IsNaN(value) ? (double?)null : value;
|
||||
}
|
||||
}
|
||||
@@ -54,17 +54,6 @@ namespace System.Collections.Generic {
|
||||
// yield return item;
|
||||
//}
|
||||
|
||||
public static T SafeSingleOrDefault<T>(this IEnumerable<T> source) {
|
||||
using (var en = source.GetEnumerator()) {
|
||||
if (en.MoveNext()) {
|
||||
var rv_candidate = en.Current;
|
||||
if (!en.MoveNext())
|
||||
return rv_candidate;
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> TakeWhile<T, TAccumulate>(this IEnumerable<T> source, TAccumulate seed, Func<TAccumulate, T, TAccumulate> accumulate, Func<TAccumulate, T, bool> predicate) {
|
||||
TAccumulate a = seed;
|
||||
foreach (T el in source) {
|
||||
@@ -97,28 +86,6 @@ namespace System.Collections.Generic {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetSingle<T>(this IEnumerable<T> source, out T result) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
using (var en = source.GetEnumerator()) {
|
||||
if (en.MoveNext()) {
|
||||
result = en.Current;
|
||||
if (!en.MoveNext())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetSingle<T, TResult>(this IEnumerable<T> source, Func<T, bool> predicate, Func<T, TResult> convert, out TResult result) {
|
||||
if (TryGetSingle(source.Where(predicate), out T v)) {
|
||||
result = convert(v);
|
||||
return true;
|
||||
}
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static T FirstOrDefault<T>(this IEnumerable<T> source, Func<T, bool> predicate, T default_rv) {
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
foreach (T element in source)
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
using bts;
|
||||
using mbts;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading;
|
||||
using System.Reactive.Linq;
|
||||
using System.Collections;
|
||||
using mbts.utils;
|
||||
using System.Threading.Tasks;
|
||||
using mbts;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows.Threading;
|
||||
using System.Collections.Specialized;
|
||||
using mbts.spark;
|
||||
using bts.api;
|
||||
using bts;
|
||||
using bts.utils;
|
||||
|
||||
namespace mbts {
|
||||
namespace mbts
|
||||
{
|
||||
public static class GlobalExtensions
|
||||
{
|
||||
#region AddRange
|
||||
@@ -98,8 +105,8 @@ namespace mbts {
|
||||
return items.GroupBy(key_selector).ToDictionary(g => g.Key, g => g.AsEnumerable());
|
||||
}
|
||||
|
||||
public static IDictionary<K, V> MapAggregate<K, V, T>(this IEnumerable<T> items, Func<T, K> key_selector, Func<T, V> value_selector, V seed, Func<V, V, V> aggregate, IEqualityComparer<K> key_comparer) {
|
||||
return items.GroupBy(key_selector, key_comparer).ToDictionary(g => g.Key, g => g.Select(value_selector).Aggregate(seed, aggregate), key_comparer);
|
||||
public static IDictionary<K, V> MapAggregate<K, V, T>(this IEnumerable<T> items, Func<T, K> key_selector, Func<T, V> value_selector, V seed, Func<V, V, V> aggregate, IEqualityComparer<K> key_equality_comparer) {
|
||||
return items.GroupBy(key_selector, key_equality_comparer).ToDictionary(g => g.Key, g => g.Select(value_selector).Aggregate(seed, aggregate));
|
||||
}
|
||||
|
||||
public static int aggregate_hash<T>(this IEnumerable<T> series) where T : class {
|
||||
@@ -231,7 +238,8 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
|
||||
namespace System.Collections.Specialized {
|
||||
namespace System.Collections.Specialized
|
||||
{
|
||||
public static class NotifyCollectionChangedExtensions
|
||||
{
|
||||
public static IDisposable SuspendHandler(this INotifyCollectionChanged source, NotifyCollectionChangedEventHandler handler) {
|
||||
@@ -250,7 +258,8 @@ namespace System.Collections.Specialized {
|
||||
}
|
||||
}
|
||||
|
||||
namespace System.Reactive.Linq {
|
||||
namespace System.Reactive.Linq
|
||||
{
|
||||
public static class ObservableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -388,4 +397,4 @@ namespace System.Reactive.Linq {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@ namespace mbts.ui.native
|
||||
[DllImport("libspark")]
|
||||
public static extern void enable_limits();
|
||||
|
||||
[DllImport("libspark")]
|
||||
public static extern uint setup_process_job_object();
|
||||
|
||||
[DllImport("libspark", CharSet = CharSet.Unicode)]
|
||||
public static extern void set_window_app_id(IntPtr hwnd, string stack_id);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.utils;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -99,12 +98,8 @@ namespace mbts.ui {
|
||||
}
|
||||
}
|
||||
|
||||
public static string diagnostic_name(this IShellWindow window) {
|
||||
var sb = new StringBuilderEx { "[", window.window_type, "]", window.Title };
|
||||
if (window is ISharedWindow shared_win)
|
||||
sb.Add("(thread: ", shared_win.thread_id, ")");
|
||||
return sb.ToString();
|
||||
}
|
||||
public static string diagnostic_name(this IShellWindow window) =>
|
||||
$"[{window.window_type}]{window.Title}";
|
||||
|
||||
public static IObservable<string> observe_title(this IShellWindow window) {
|
||||
var src = Observable.FromEvent<Action, string>(cb => () => cb(window.Title), cb => window.TitleChanged += cb, cb => window.TitleChanged -= cb);
|
||||
|
||||
@@ -90,25 +90,20 @@ namespace mbts.utils {
|
||||
}
|
||||
|
||||
// Note: this is not optimized, be wary of use with large collections
|
||||
public static void sync_with_never_duplicate<T>(IList<T> dest, IEnumerable<T> src) =>
|
||||
public static void sync_with_never_duplicate<T>(ObservableCollection<T> dest, IEnumerable<T> src) =>
|
||||
sync_with_never_duplicate(dest, src, EqualityComparer<T>.Default);
|
||||
|
||||
// Note: this is not optimized, be wary of use with large collections
|
||||
public static void sync_with_never_duplicate<T>(IList<T> dest, IEnumerable<T> src, IEqualityComparer<T> comp) {
|
||||
var src_list = src as IList<T> ?? new List<T>(src);
|
||||
var src_set = src as ISet<T> ?? new HashSet<T>(src_list);
|
||||
for (int i = 0; i < dest.Count; i++) {
|
||||
if (!src_set.Contains(dest[i]))
|
||||
dest.RemoveAt(i);
|
||||
}
|
||||
for (int i = 0; i < src_list.Count; i++) {
|
||||
int curr_index = dest.IndexOf(src_list[i]);
|
||||
public static void sync_with_never_duplicate<T>(ObservableCollection<T> dest, IEnumerable<T> src, IEqualityComparer<T> comp) {
|
||||
int i = 0;
|
||||
foreach (var val in src.empty_if_null()) {
|
||||
int curr_index = dest.IndexOf(val);
|
||||
if (curr_index == i) {
|
||||
continue;
|
||||
} else if (curr_index >= 0 && curr_index < dest.Count) {
|
||||
} else if (curr_index > 0 && curr_index < dest.Count) {
|
||||
dest.RemoveAt(curr_index);
|
||||
}
|
||||
dest.Insert(i, src_list[i]);
|
||||
dest.Insert(i++, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,16 +112,11 @@ namespace mbts.utils {
|
||||
|
||||
// Note: this is not optimized, be wary of use with large collections
|
||||
public static void sync_with_never_duplicate<T, TSource, TKey>(ObservableCollection<T> dest, IEnumerable<TSource> src, Func<TSource, T> create, Func<T, TKey> key_selector, Func<TSource, TKey> src_key_selector, IEqualityComparer<TKey> key_comparer) {
|
||||
var src_list = src as IList<TSource> ?? new List<TSource>(src);
|
||||
var src_map = src_list.ToDictionary(src_key_selector);
|
||||
for (int i = 0; i < dest.Count; i++) {
|
||||
var k = key_selector(dest[i]);
|
||||
if (!src_map.ContainsKey(k))
|
||||
dest.RemoveAt(i);
|
||||
}
|
||||
int i = 0;
|
||||
var src_map = src.ToDictionary(src_key_selector);
|
||||
var dest_map = dest.ToDictionary(key_selector);
|
||||
for (int i = 0; i < src_list.Count; i++) {
|
||||
TKey key = src_key_selector(src_list[i]);
|
||||
foreach (var val in src.empty_if_null()) {
|
||||
TKey key = src_key_selector(val);
|
||||
if (dest_map.TryGetValue(key, out T dval)) {
|
||||
int curr_index = dest.IndexOf(dval);
|
||||
if (curr_index != i) {
|
||||
@@ -134,8 +124,9 @@ namespace mbts.utils {
|
||||
dest.Insert(i, dval);
|
||||
}
|
||||
} else {
|
||||
dest.Insert(i, create(src_list[i]));
|
||||
dest.Insert(i, create(val));
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,5 +136,13 @@ namespace mbts.utils {
|
||||
rv[i] = selector(source[i]);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static IDisposable dispose_on_removed<T>(ObservableCollection<T> collection) =>
|
||||
collection.Subscribe((s, e) => {
|
||||
if (e.OldItems != null) {
|
||||
foreach (T item in e.OldItems)
|
||||
(item as IDisposable)?.Dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
using mbts.collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.utils {
|
||||
class CommonValueMonitor<T> {
|
||||
readonly RefCountMap<T> _values;
|
||||
T _default_value;
|
||||
|
||||
public CommonValueMonitor()
|
||||
: this(default, EqualityComparer<T>.Default) { }
|
||||
|
||||
public CommonValueMonitor(T default_value)
|
||||
: this(default_value, EqualityComparer<T>.Default) { }
|
||||
|
||||
public CommonValueMonitor(T default_value, IEqualityComparer<T> comparer) {
|
||||
_default_value = default_value;
|
||||
_values = new RefCountMap<T>(comparer);
|
||||
}
|
||||
|
||||
public event Action<T> common_value_changed;
|
||||
|
||||
public ICollection<T> distinct_values => _values.keys;
|
||||
public T common_value => _values.keys.SafeSingleOrDefault();
|
||||
|
||||
public void add(T value) {
|
||||
if (_values.increment_adds_key(value)) {
|
||||
switch (_values.Count) {
|
||||
case 1: common_value_changed?.Invoke(value); break;
|
||||
case 2: common_value_changed?.Invoke(_default_value); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(T value) {
|
||||
if (_values.decrement_removes_key(value)) {
|
||||
switch (_values.Count) {
|
||||
case 0: common_value_changed?.Invoke(_default_value); break;
|
||||
case 1: common_value_changed?.Invoke(_values.keys.First()); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CommonValueMonitorMultiplexor<TSource> {
|
||||
int _member_id_counter;
|
||||
readonly Dictionary<int, (Action<TSource> add, Action<TSource> remove)> _members = new Dictionary<int, (Action<TSource> add, Action<TSource> remove)>();
|
||||
|
||||
public void add(TSource value) {
|
||||
foreach ((var add, var remove) in _members.Values)
|
||||
add(value);
|
||||
}
|
||||
|
||||
public void remove(TSource value) {
|
||||
foreach ((var add, var remove) in _members.Values)
|
||||
remove(value);
|
||||
}
|
||||
|
||||
public IDisposable add_member_mon<T>(CommonValueMonitor<T> member_mon, Func<TSource, T> get_value) {
|
||||
int id = _member_id_counter++;
|
||||
_members.Add(id, (
|
||||
v => member_mon.add(get_value(v)),
|
||||
v => member_mon.remove(get_value(v))));
|
||||
return Disposable.Create(() => _members.Remove(id));
|
||||
}
|
||||
|
||||
public IDisposable add_member_mon<T>(CommonValueMonitor<T> member_mon, Func<TSource, T> get_value, Func<T, bool> where) {
|
||||
int id = _member_id_counter++;
|
||||
_members.Add(id, (
|
||||
v => {
|
||||
var res = get_value(v);
|
||||
if (where(res))
|
||||
member_mon.add(res);
|
||||
},
|
||||
v => {
|
||||
var res = get_value(v);
|
||||
if (where(res))
|
||||
member_mon.remove(res);
|
||||
}));
|
||||
return Disposable.Create(() => _members.Remove(id));
|
||||
}
|
||||
}
|
||||
|
||||
static class CommonValueMonitorExtensions {
|
||||
public static bool try_get_common_value<T>(this CommonValueMonitor<T> monitor, out T result) =>
|
||||
monitor.distinct_values.TryGetSingle(out result);
|
||||
|
||||
public static T? common_value_nullable<T>(this CommonValueMonitor<T> monitor) where T : struct =>
|
||||
monitor.try_get_common_value(out T result) ? result : (T?)null;
|
||||
|
||||
public static IObservable<T> SubscribeThrottled<T>(this CommonValueMonitor<T> monitor, TimeSpan throttle) =>
|
||||
Observable.Create<T>(o => {
|
||||
bool busy = false, dirty = false;
|
||||
o.OnNext(monitor.common_value);
|
||||
monitor.common_value_changed += on_changed;
|
||||
return Disposable.Create(() => monitor.common_value_changed -= on_changed);
|
||||
async void on_changed(T _) {
|
||||
if (busy) {
|
||||
dirty = true;
|
||||
return;
|
||||
}
|
||||
dirty = false;
|
||||
do {
|
||||
o.OnNext(monitor.common_value);
|
||||
await Task.Delay(throttle);
|
||||
} while (dirty);
|
||||
busy = false;
|
||||
}
|
||||
});
|
||||
|
||||
public static IObservable<T> SubscribeThrottled<T>(this CommonValueMonitor<T> monitor, TimeSpan throttle, T default_value) =>
|
||||
Observable.Create<T>(o => {
|
||||
bool busy = false, dirty = false;
|
||||
notify();
|
||||
monitor.common_value_changed += on_changed;
|
||||
return Disposable.Create(() => monitor.common_value_changed -= on_changed);
|
||||
async void on_changed(T _) {
|
||||
if (busy) {
|
||||
dirty = true;
|
||||
return;
|
||||
}
|
||||
dirty = false;
|
||||
do {
|
||||
notify();
|
||||
await Task.Delay(throttle);
|
||||
} while (dirty);
|
||||
busy = false;
|
||||
}
|
||||
void notify() =>
|
||||
o.OnNext(monitor.try_get_common_value(out T result) ? result : default_value);
|
||||
});
|
||||
|
||||
public static IObservable<TResult> SubscribeThrottled<T, TResult>(this CommonValueMonitor<T> monitor, TimeSpan throttle, Func<T, TResult> selector, TResult default_value) =>
|
||||
Observable.Create<TResult>(o => {
|
||||
bool busy = false, dirty = false;
|
||||
notify();
|
||||
monitor.common_value_changed += on_changed;
|
||||
return Disposable.Create(() => monitor.common_value_changed -= on_changed);
|
||||
async void on_changed(T _) {
|
||||
if (busy) {
|
||||
dirty = true;
|
||||
return;
|
||||
}
|
||||
dirty = false;
|
||||
do {
|
||||
notify();
|
||||
await Task.Delay(throttle);
|
||||
} while (dirty);
|
||||
busy = false;
|
||||
}
|
||||
void notify() =>
|
||||
o.OnNext(monitor.try_get_common_value(out T result) ? selector(result) : default_value);
|
||||
});
|
||||
|
||||
public static IObservable<T?> SubscribeThrottledNullable<T>(this CommonValueMonitor<T> monitor, TimeSpan throttle) where T : struct =>
|
||||
monitor.SubscribeThrottled<T, T?>(throttle, val => val, null);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.utils {
|
||||
class PropertyUpdateThrottle<T> {
|
||||
readonly TimeSpan _throttle;
|
||||
readonly IEqualityComparer<T> _comparer;
|
||||
bool _busy, _dirty;
|
||||
T _value, _last_fired;
|
||||
|
||||
public PropertyUpdateThrottle(TimeSpan throttle)
|
||||
: this(throttle, EqualityComparer<T>.Default) { }
|
||||
|
||||
public PropertyUpdateThrottle(TimeSpan throttle, IEqualityComparer<T> comparer) {
|
||||
_throttle = throttle;
|
||||
_comparer = comparer;
|
||||
}
|
||||
|
||||
public T value {
|
||||
get => _value;
|
||||
set {
|
||||
if (!_comparer.Equals(_value, value)) {
|
||||
_value = value;
|
||||
_fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChange<T> changed;
|
||||
|
||||
async void _fire() {
|
||||
if (!_busy) {
|
||||
_busy = true;
|
||||
do {
|
||||
var old = _last_fired;
|
||||
_last_fired = _value;
|
||||
changed?.Invoke(old, _value);
|
||||
_dirty = false;
|
||||
await Task.Delay(_throttle);
|
||||
} while (_dirty);
|
||||
_busy = false;
|
||||
} else {
|
||||
_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +1,91 @@
|
||||
using mbts;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Reflection;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using mbts.utils;
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using System.Reflection;
|
||||
|
||||
namespace bts.utils {
|
||||
static class ProtoHelper {
|
||||
const string
|
||||
_case_suffix = "Case",
|
||||
_oneof_case_suffix = "OneofCase";
|
||||
|
||||
public static MessageDescriptor Descriptor<T>() where T : IMessage, new() =>
|
||||
_DescriptorImpl<T>.value;
|
||||
|
||||
public static OneofDescriptor get_oneof_descriptor<T>(string prop_name) where T : IMessage, new() {
|
||||
prop_name = prop_name.iremove_suffix(_case_suffix);
|
||||
return Descriptor<T>().Oneofs.FirstOrDefault(d => d.Name.iequals(prop_name));
|
||||
}
|
||||
|
||||
public static IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)> get_oneof_descriptor_index<T, TOneofCase>(string prop_name)
|
||||
where T : IMessage, new()
|
||||
where TOneofCase : struct =>
|
||||
_DescriptorImpl<T>.OneofImpl<TOneofCase>.get_descriptor_case_index(prop_name);
|
||||
|
||||
public static FieldDescriptor get_oneof_field_descriptor<T, TOneofCase>(TOneofCase oneof_case) where T : IMessage, new() where TOneofCase : struct {
|
||||
string oneof_name = oneof_case.ToString();
|
||||
return Descriptor<T>().Fields.InFieldNumberOrder().FirstOrDefault(fd => fd.Name.Replace("_", "").iequals(oneof_name));
|
||||
}
|
||||
|
||||
public static TImmutable immutable_factory<TMutable, TImmutable>(TMutable m) where TMutable : IMessage, new() where TImmutable : IImmutableMessage =>
|
||||
(TImmutable)Activator.CreateInstance(typeof(TImmutable), m);
|
||||
|
||||
public static IMessage mutable_factory(Type type) =>
|
||||
MutableFactoryStore.get(type)();
|
||||
|
||||
static class MutableFactoryStore {
|
||||
[ThreadStatic]
|
||||
static readonly Dictionary<Type, Func<IMessage>> _factories = new Dictionary<Type, Func<IMessage>>();
|
||||
|
||||
public static Func<IMessage> get(Type type) =>
|
||||
_factories.GetOrAdd(type,
|
||||
t => typeof(MutableFactory<>).MakeGenericType(t)
|
||||
.GetField(nameof(MutableFactory<LimitOrderDefinition>.anonymous_factory), BindingFlags.Static | BindingFlags.Public)
|
||||
.GetValue(null) as Func<IMessage>);
|
||||
}
|
||||
|
||||
static class MutableFactory<T> where T : IMessage<T>, new() {
|
||||
public static readonly Func<IMessage> anonymous_factory = () => new T();
|
||||
}
|
||||
|
||||
public static Type get_field_type(FieldDescriptor field_descriptor) {
|
||||
switch (field_descriptor.FieldType) {
|
||||
case FieldType.Bool: return typeof(bool);
|
||||
case FieldType.Bytes: return typeof(ByteString);
|
||||
case FieldType.Double: return typeof(double);
|
||||
case FieldType.Float: return typeof(float);
|
||||
case FieldType.UInt32: return typeof(uint);
|
||||
case FieldType.UInt64: return typeof(ulong);
|
||||
// int
|
||||
case FieldType.SFixed32:
|
||||
case FieldType.Fixed32:
|
||||
case FieldType.SInt32:
|
||||
case FieldType.Int32: return typeof(int);
|
||||
// long
|
||||
case FieldType.SFixed64:
|
||||
case FieldType.Fixed64:
|
||||
case FieldType.SInt64:
|
||||
case FieldType.Int64: return typeof(long);
|
||||
//
|
||||
case FieldType.String: return typeof(string);
|
||||
case FieldType.Enum: return field_descriptor.EnumType.ClrType;
|
||||
case FieldType.Message: return field_descriptor.MessageType.ClrType;
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(field_descriptor.FieldType, nameof(field_descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
public static IReadOnlyDictionary<TOneofCase, FieldDescriptor> get_oneof_descriptor_index<TOneofCase>(OneofDescriptor descriptor) where TOneofCase : struct =>
|
||||
descriptor.Fields.ToDictionary(fd => EnumHelper.parse<TOneofCase>(fd.Name.Replace("_", "")));
|
||||
|
||||
static class _DescriptorImpl<T> where T : IMessage, new() {
|
||||
public static readonly MessageDescriptor value = new T().Descriptor;
|
||||
|
||||
public static class OneofImpl<TOneofCase> where TOneofCase : struct {
|
||||
static Dictionary<string, IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)>> _descriptor_case_indices =
|
||||
new Dictionary<string, IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)>>();
|
||||
|
||||
public static IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)> get_descriptor_case_index(string prop_name) {
|
||||
/// Use local copy because <see cref="_descriptor_case_indices"/> is updated atomically.
|
||||
var dci = _descriptor_case_indices;
|
||||
if (!dci.TryGetValue(prop_name, out IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)> rv)) {
|
||||
/// Update <see cref="_descriptor_case_indices"/> atomically (lock-free).
|
||||
/// 1) clone the local copy.
|
||||
/// 2) add the new KeyValue to the clone.
|
||||
/// 3) assign the clone back to <see cref="_descriptor_case_indices"/>.
|
||||
rv = get_oneof_descriptor_index<TOneofCase>(get_oneof_descriptor<T>(prop_name))
|
||||
.ToDictionary(kv => kv.Key, kv => (kv.Value, get_field_type(kv.Value)));
|
||||
dci = dci.ToDictionary();
|
||||
dci.Add(prop_name, rv);
|
||||
/// It is possible for add ops to be overwritten.
|
||||
/// This is perfectly safe, just means that the 'clone-add-assign' will be repeated the next time a caller requests this key.
|
||||
_descriptor_case_indices = dci;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using mbts;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.Reflection;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using mbts.utils;
|
||||
|
||||
namespace bts.utils {
|
||||
static class ProtoHelper {
|
||||
const string
|
||||
_case_suffix = "Case",
|
||||
_oneof_case_suffix = "OneofCase";
|
||||
|
||||
public static MessageDescriptor Descriptor<T>() where T : IMessage, new() =>
|
||||
_DescriptorImpl<T>.value;
|
||||
|
||||
public static OneofDescriptor get_oneof_descriptor<T>(string prop_name) where T : IMessage, new() {
|
||||
prop_name = prop_name.iremove_suffix(_case_suffix);
|
||||
return Descriptor<T>().Oneofs.FirstOrDefault(d => d.Name.iequals(prop_name));
|
||||
}
|
||||
|
||||
public static IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)> get_oneof_descriptor_index<T, TOneofCase>(string prop_name)
|
||||
where T : IMessage, new()
|
||||
where TOneofCase : struct =>
|
||||
_DescriptorImpl<T>.OneofImpl<TOneofCase>.get_descriptor_case_index(prop_name);
|
||||
|
||||
public static FieldDescriptor get_oneof_field_descriptor<T, TOneofCase>(TOneofCase oneof_case) where T : IMessage, new() where TOneofCase : struct {
|
||||
string oneof_name = oneof_case.ToString();
|
||||
return Descriptor<T>().Fields.InFieldNumberOrder().FirstOrDefault(fd => fd.Name.Replace("_", "").iequals(oneof_name));
|
||||
}
|
||||
|
||||
public static Type get_field_type(FieldDescriptor field_descriptor) {
|
||||
switch (field_descriptor.FieldType) {
|
||||
case FieldType.Bool: return typeof(bool);
|
||||
case FieldType.Bytes: return typeof(ByteString);
|
||||
case FieldType.Double: return typeof(double);
|
||||
case FieldType.Float: return typeof(float);
|
||||
case FieldType.UInt32: return typeof(uint);
|
||||
case FieldType.UInt64: return typeof(ulong);
|
||||
// int
|
||||
case FieldType.SFixed32:
|
||||
case FieldType.Fixed32:
|
||||
case FieldType.SInt32:
|
||||
case FieldType.Int32: return typeof(int);
|
||||
// long
|
||||
case FieldType.SFixed64:
|
||||
case FieldType.Fixed64:
|
||||
case FieldType.SInt64:
|
||||
case FieldType.Int64: return typeof(long);
|
||||
//
|
||||
case FieldType.String: return typeof(string);
|
||||
case FieldType.Enum: return field_descriptor.EnumType.ClrType;
|
||||
case FieldType.Message: return field_descriptor.MessageType.ClrType;
|
||||
default: throw ExceptionBuilder.Argument.unexpected_case(field_descriptor.FieldType, nameof(field_descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
public static IReadOnlyDictionary<TOneofCase, FieldDescriptor> get_oneof_descriptor_index<TOneofCase>(OneofDescriptor descriptor) where TOneofCase : struct =>
|
||||
descriptor.Fields.ToDictionary(fd => EnumHelper.parse<TOneofCase>(fd.Name.Replace("_", "")));
|
||||
|
||||
static class _DescriptorImpl<T> where T : IMessage, new() {
|
||||
public static readonly MessageDescriptor value = new T().Descriptor;
|
||||
|
||||
public static class OneofImpl<TOneofCase> where TOneofCase : struct {
|
||||
static Dictionary<string, IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)>> _descriptor_case_indices =
|
||||
new Dictionary<string, IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)>>();
|
||||
|
||||
public static IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)> get_descriptor_case_index(string prop_name) {
|
||||
/// Use local copy because <see cref="_descriptor_case_indices"/> is updated atomically.
|
||||
var dci = _descriptor_case_indices;
|
||||
if (!dci.TryGetValue(prop_name, out IReadOnlyDictionary<TOneofCase, (FieldDescriptor field_descriptor, Type field_type)> rv)) {
|
||||
/// Update <see cref="_descriptor_case_indices"/> atomically (lock-free).
|
||||
/// 1) clone the local copy.
|
||||
/// 2) add the new KeyValue to the clone.
|
||||
/// 3) assign the clone back to <see cref="_descriptor_case_indices"/>.
|
||||
rv = get_oneof_descriptor_index<TOneofCase>(get_oneof_descriptor<T>(prop_name))
|
||||
.ToDictionary(kv => kv.Key, kv => (kv.Value, get_field_type(kv.Value)));
|
||||
dci = dci.ToDictionary();
|
||||
dci.Add(prop_name, rv);
|
||||
/// It is possible for add ops to be overwritten.
|
||||
/// This is perfectly safe, just means that the 'clone-add-assign' will be repeated the next time a caller requests this key.
|
||||
_descriptor_case_indices = dci;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,8 @@
|
||||
xmlns:mbts="clr-namespace:mbts.ui"
|
||||
xmlns:ui="clr-namespace:mbts.ui"
|
||||
>
|
||||
<!--Control Base Colors-->
|
||||
<Color x:Key="Control.MediumGrayColor">#B3B3B3</Color>
|
||||
<Color x:Key="Text.Prompt.Foreground.Color">#FF999999</Color>
|
||||
|
||||
<SolidColorBrush x:Key="Control.MediumGrayBrush" Color="{StaticResource Control.MediumGrayColor}"/>
|
||||
<SolidColorBrush x:Key="CommonBackground" Color="{ui:BTSColor common_background}" />
|
||||
<SolidColorBrush x:Key="ControlBackground" Color="{x:Static mbts:BTSColors.common_background}" />
|
||||
<SolidColorBrush x:Key="AccentColor1" Color="{x:Static mbts:BTSColors.accent1}" />
|
||||
@@ -52,48 +49,48 @@
|
||||
<SolidColorBrush x:Key="CheckBox.Pressed.BorderBrush" Color="{StaticResource CheckBox.Pressed.BorderColor}"/>
|
||||
|
||||
<!--ComboBox-->
|
||||
<LinearGradientBrush x:Key="ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFF0F0F0" Offset="0.0"/>
|
||||
<GradientStop Color="#FFE5E5E5" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FFACACAC"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="#FFABADB3"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#FF000000"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFECF4FC" Offset="0.0"/>
|
||||
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="#FF7EB4EA"/>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="#FF7EB4EA"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFEBF4FC" Offset="0.0"/>
|
||||
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="#FF7EB4EA"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#FF000000"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.Pressed.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFDAECFC" Offset="0.0"/>
|
||||
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="#FF569DE5"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="#FF569DE5"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFDAEBFC" Offset="0.0"/>
|
||||
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="#FF569DE5"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="#FFF0F0F0"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="#FFD9D9D9"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="#FFBFBFBF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFF0F0F0" Offset="0.0"/>
|
||||
<GradientStop Color="#FFE5E5E5" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FFACACAC"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="#FFABADB3"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#FF000000"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFECF4FC" Offset="0.0"/>
|
||||
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="#FF7EB4EA"/>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="#FF7EB4EA"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFEBF4FC" Offset="0.0"/>
|
||||
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="#FF7EB4EA"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#FF000000"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.Pressed.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFDAECFC" Offset="0.0"/>
|
||||
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="#FF569DE5"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="#FF569DE5"/>
|
||||
<LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
|
||||
<GradientStop Color="#FFDAEBFC" Offset="0.0"/>
|
||||
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="#FF569DE5"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="#FFF0F0F0"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="#FFD9D9D9"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="#FFFFFFFF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="#FFBFBFBF"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
|
||||
|
||||
<!--ListBox-->
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace mbts.ui.controls.adorners {
|
||||
class Adorner<T> : Adorner where T : UIElement {
|
||||
public Adorner(T adorned_element) : base(adorned_element) =>
|
||||
AdornedElement = adorned_element;
|
||||
|
||||
public new T AdornedElement { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
using bts.utils;
|
||||
using mbts.ui.extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui.controls.adorners {
|
||||
/// <summary>
|
||||
/// This Adorner draws a rectangle over rows when the mouse hovers over them.
|
||||
/// </summary>
|
||||
class DIYGridRowHighlighterAdorner : Adorner<DIYGrid> {
|
||||
DIYGridRowsPresenter _rows_presenter_backing;
|
||||
ScrollContentPresenter _content_presenter_backing;
|
||||
object _target_row;
|
||||
Brush _fill;
|
||||
Pen _stroke;
|
||||
|
||||
#region Fill
|
||||
public static readonly DependencyProperty FillProperty =
|
||||
WPFHelper.create_dp<SolidColorBrush, DIYGridRowHighlighterAdorner>(
|
||||
nameof(Fill),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._fill = v,
|
||||
DIYGrid.DefaultSelectionHighlight);
|
||||
|
||||
public SolidColorBrush Fill {
|
||||
get => GetValue(FillProperty) as SolidColorBrush;
|
||||
set => SetValue(FillProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Stroke
|
||||
public static readonly DependencyProperty StrokeProperty =
|
||||
WPFHelper.create_dp<Pen, DIYGridRowHighlighterAdorner>(
|
||||
nameof(Stroke),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._stroke = v,
|
||||
DIYGrid.DefaultSelectionStroke);
|
||||
|
||||
public Pen Stroke {
|
||||
get => GetValue(StrokeProperty) as Pen;
|
||||
set => SetValue(StrokeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public DIYGridRowHighlighterAdorner(DIYGrid grid) : base(grid) {
|
||||
_target_row = TargetRow;
|
||||
_fill = Fill;
|
||||
_stroke = Stroke;
|
||||
IsHitTestVisible = false;
|
||||
grid.SubscribeMouseEnter(on_mouse_enter);
|
||||
grid.SubscribeMouseLeave(on_mouse_leave);
|
||||
grid.SubscribePreviewMouseMove(on_mouse_move);
|
||||
void on_mouse_enter(object sender, MouseEventArgs e) => InvalidateVisual();
|
||||
void on_mouse_move(object sender, MouseEventArgs e) => InvalidateVisual();
|
||||
void on_mouse_leave(object sender, MouseEventArgs e) => InvalidateVisual();
|
||||
}
|
||||
|
||||
DIYGridRowsPresenter _rows_presenter => _rows_presenter_backing ?? (_rows_presenter_backing = AdornedElement.descendants_of_type<DIYGridRowsPresenter>().FirstOrDefault());
|
||||
ScrollContentPresenter _content_presenter => _content_presenter_backing ?? (_content_presenter_backing = _rows_presenter?.descendants_of_type<ScrollContentPresenter>().empty_if_null().FirstOrDefault());
|
||||
|
||||
public static readonly DependencyProperty TargetRowProperty =
|
||||
WPFHelper.create_dp<object, DIYGridRowHighlighterAdorner>(
|
||||
nameof(TargetRow),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._target_row = v);
|
||||
|
||||
public object TargetRow { get => GetValue(TargetRowProperty); set => SetValue(TargetRowProperty, value); }
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext) {
|
||||
if (_content_presenter == null || _target_row == null) {
|
||||
base.OnRender(drawingContext);
|
||||
}
|
||||
var pt = Mouse.GetPosition(AdornedElement);
|
||||
if (AdornedElement.RowContainerGenerator.ContainerFromItem(_target_row) is FrameworkElement rview) {
|
||||
var top_left = rview.TranslatePoint(new Point(0, 0), _content_presenter);
|
||||
double x_adj = MathUtils.within_range(0.0, _content_presenter.ActualWidth, top_left.X);
|
||||
double y_adj = MathUtils.within_range(0.0, _content_presenter.ActualHeight, top_left.Y);
|
||||
top_left = _content_presenter.TranslatePoint(new Point(x_adj, y_adj), AdornedElement);
|
||||
drawingContext.DrawRectangle(_fill, _stroke, new Rect(top_left, new Point(top_left.X + _content_presenter.ActualWidth, top_left.Y + rview.ActualHeight)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,14 +48,12 @@ namespace mbts.ui.controls {
|
||||
PnL_Generic = 80,
|
||||
Working = 72,
|
||||
LastMessage = 100,
|
||||
ParentKey = 100,
|
||||
AttachedTargetOrderKey = 100;
|
||||
ParentKey = 100;
|
||||
}
|
||||
|
||||
public static class DefaultColumnHeaders {
|
||||
public const string
|
||||
Account = "Account",
|
||||
AttachedTargetOrderKey = "Attached Target Key",
|
||||
Exchange = "Exchange",
|
||||
FillKey = "Fill Key",
|
||||
FillTime = "Fill Time",
|
||||
@@ -99,13 +97,13 @@ namespace mbts.ui.controls {
|
||||
TotalPnL = "Total PnL",
|
||||
PnLPrice = "PnL Price",
|
||||
Cancel = "Cancel",
|
||||
|
||||
MarketDescription = "Market Description",
|
||||
ClearedAlias = "Cleared Alias",
|
||||
ProductDescription = "Product Description",
|
||||
HubAlias = "Hub Alias",
|
||||
StripName = "Strip Name",
|
||||
MicCode = "Mic Code"
|
||||
MicCode = "Mic Code",
|
||||
PositionInQueue = "PIQ"
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using mbts.utils;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
interface IHandleGridSorting {
|
||||
@@ -324,9 +323,6 @@ namespace mbts.ui.controls {
|
||||
|
||||
public event DIYGridSortingEventHandler Sorting;
|
||||
|
||||
public ItemContainerGenerator RowContainerGenerator =>
|
||||
_row_presenter.ItemContainerGenerator;
|
||||
|
||||
private void OnCopy(object sender, ExecutedRoutedEventArgs e) {
|
||||
if (_row_presenter != null) {
|
||||
IEnumerable<RowBase> selected_rows = _row_presenter.SelectedItems.Cast<object>()
|
||||
@@ -708,8 +704,8 @@ namespace mbts.ui.controls {
|
||||
(o, v) => {
|
||||
if (DoubleHelper.IsNaNOrInfinity(v))
|
||||
v = 0;
|
||||
if (!o._visible_columns.is_null_or_empty()) {
|
||||
double max_font_size = o._visible_columns.Max(c => c.font_size);
|
||||
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)
|
||||
return min_row_height;
|
||||
@@ -1029,25 +1025,5 @@ namespace mbts.ui.controls {
|
||||
callback(map);
|
||||
}
|
||||
}
|
||||
|
||||
public static IObservable<T> SubscribeMouseHoverRow<T>(this DIYGrid grid) where T : class =>
|
||||
SubscribeMouseHoverRow(grid, EqualityComparer<T>.Default);
|
||||
|
||||
public static IObservable<T> SubscribeMouseHoverRow<T>(this DIYGrid grid, IEqualityComparer<T> comparer) where T : class =>
|
||||
Observable.Create<T>(o => {
|
||||
T curr_row = null;
|
||||
return Disposable.Create(
|
||||
grid.SubscribeMouseEnter(on_mouse_enter),
|
||||
grid.SubscribeMouseLeave(on_mouse_leave),
|
||||
grid.SubscribePreviewMouseMove(on_mouse_move));
|
||||
void on_mouse_enter(object sender, MouseEventArgs e) => refresh();
|
||||
void on_mouse_move(object sender, MouseEventArgs e) => refresh();
|
||||
void on_mouse_leave(object sender, MouseEventArgs e) => refresh();
|
||||
void refresh() {
|
||||
T row = grid.ChildAtPoint<ListBoxItem>(Mouse.GetPosition(grid)) is var rview && rview != null ? grid.RowContainerGenerator.ItemFromContainer(rview) as T : null;
|
||||
if (!comparer.Equals(curr_row, row))
|
||||
o.OnNext(curr_row = row);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,15 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Resources/Buttons.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<sys:Double x:Key="DialogSkirtPadding.Left">12</sys:Double>
|
||||
<sys:Double x:Key="DialogSkirtPadding.Top">12</sys:Double>
|
||||
<sys:Double x:Key="DialogSkirtPadding.Right">12</sys:Double>
|
||||
<Thickness x:Key="DialogSkirtPadding" Left="{StaticResource DialogSkirtPadding.Left}" Top="{StaticResource DialogSkirtPadding.Top}" Right="{StaticResource DialogSkirtPadding.Right}" Bottom="10"/>
|
||||
<Thickness x:Key="DialogSkirtButtonMargin" Left="4" Top="0" Right="4" Bottom="0"/>
|
||||
<Style x:Key="{x:Static c:DialogSkirt.DialogSkirtButtonStyleKey}" TargetType="Button" BasedOn="{StaticResource CommonButton}">
|
||||
<Setter Property="Content" Value="{Binding name}"/>
|
||||
<Setter Property="IsCancel" Value="{Binding is_cancel}"/>
|
||||
<Setter Property="IsDefault" Value="{Binding is_default}"/>
|
||||
<Setter Property="Margin" Value="{StaticResource DialogSkirtButtonMargin}"/>
|
||||
<Setter Property="Margin" Value="4,0"/>
|
||||
<Setter Property="Command" Value="{Binding command}"/>
|
||||
<Setter Property="CommandParameter" Value="{Binding command_parameter}"/>
|
||||
</Style>
|
||||
@@ -25,7 +19,7 @@
|
||||
<Style TargetType="c:DialogSkirt">
|
||||
<Setter Property="Background" Value="{x:Static u:BTSColors.dialog_skirt_brush}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="{StaticResource DialogSkirtPadding}"/>
|
||||
<Setter Property="Padding" Value="12,12,12,10"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="c:DialogSkirt">
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:ord="clr-namespace:bts.api.orderman;assembly=spark_api"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:ut="clr-namespace:mbts.ui.utilities"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
@@ -26,31 +25,10 @@
|
||||
<sys:Double x:Key="VerticalPush3">12</sys:Double>
|
||||
<Thickness x:Key="Margin.LP1" Left="{StaticResource HorizontalPush1}"/>
|
||||
<Thickness x:Key="Margin.LP3" Left="{StaticResource HorizontalPush3}"/>
|
||||
<Thickness x:Key="Margin.TP1" Top="{StaticResource VerticalPush1}"/>
|
||||
<sys:String x:Key="CanEditValuesState">CanEditValues</sys:String>
|
||||
<sys:String x:Key="CannotEditValuesState">CannotEditValues</sys:String>
|
||||
<sys:String x:Key="CanEditStructureState">CanEditStructure</sys:String>
|
||||
<sys:String x:Key="CannotEditStructureState">CannotEditStructure</sys:String>
|
||||
<cv:EnumHasFlagsConverter x:Key="CanEditValuesConverter">
|
||||
<sp:AlgoEditingPermissions>AllowEditValues</sp:AlgoEditingPermissions>
|
||||
</cv:EnumHasFlagsConverter>
|
||||
<cv:EnumHasFlagsConverter x:Key="CanEditStructureConverter">
|
||||
<sp:AlgoEditingPermissions>AllowEditStructure</sp:AlgoEditingPermissions>
|
||||
</cv:EnumHasFlagsConverter>
|
||||
<cv:ValueConverterPipe x:Key="ValuesEditingStateConverter">
|
||||
<cv:ValueConverterPipe.Pipe>
|
||||
<cv:EnumHasFlagsConverter>
|
||||
<sp:AlgoEditingPermissions>AllowEditValues</sp:AlgoEditingPermissions>
|
||||
</cv:EnumHasFlagsConverter>
|
||||
<cv:ValueMap Default="{StaticResource CannotEditValuesState}">
|
||||
<cv:KeyValue Key="{u:True}" Value="{StaticResource CanEditValuesState}"/>
|
||||
</cv:ValueMap>
|
||||
</cv:ValueConverterPipe.Pipe>
|
||||
</cv:ValueConverterPipe>
|
||||
<Style x:Key="{x:Static usp:AlgoContentTemplateSelector.DefinitionDropDownStyleKey}" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
|
||||
<Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding editing_permissions, Converter={StaticResource CanEditStructureConverter}}" Value="{u:False}">
|
||||
<DataTrigger Binding="{Binding can_edit_structure}" Value="{u:False}">
|
||||
<Setter Property="ToolTip" Value="Cannot change structure while templates are present."/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
@@ -58,7 +36,7 @@
|
||||
<Style x:Key="{x:Static usp:AlgoContentTemplateSelector.TemplateDropDownStyleKey}" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
|
||||
<Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding editing_permissions, Converter={StaticResource CanEditStructureConverter}}" Value="{u:False}">
|
||||
<DataTrigger Binding="{Binding can_edit_structure}" Value="{u:False}">
|
||||
<Setter Property="ToolTip" Value="Cannot change structure of template."/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
@@ -84,111 +62,33 @@
|
||||
</usp:AlgoContentTemplateSelector.DefaultOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.StopLimitOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot" VerticalAlignment="Top">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="stop_price_value" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid IsEnabled="{Binding can_edit_values}" VerticalAlignment="Top">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center">Stop Price</TextBlock>
|
||||
<usp:PriceEntryControl x:Name="stop_price_value" Grid.Column="2" Value="{Binding stop_price, FallbackValue={x:Static sys:Double.NaN}}" ContractSpec="{Binding spec}"/>
|
||||
<usp:PriceEntryControl Grid.Column="2" Value="{Binding stop_price, FallbackValue={x:Static sys:Double.NaN}}" ContractSpec="{Binding spec}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:AlgoContentTemplateSelector.StopLimitOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.StopMarketOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot" VerticalAlignment="Top">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="stop_price_value" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid IsEnabled="{Binding can_edit_values}" VerticalAlignment="Top">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center">Stop Price</TextBlock>
|
||||
<usp:PriceEntryControl x:Name="stop_price_value" Grid.Column="2" Value="{Binding stop_price}" ContractSpec="{Binding spec}"/>
|
||||
<usp:PriceEntryControl Grid.Column="2" Value="{Binding stop_price}" ContractSpec="{Binding spec}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:AlgoContentTemplateSelector.StopMarketOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.TimeBoxOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_StartMode_ComboBox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_AbsoluteStart_DateTimePicker" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_RelativeStart_TimeSpanControl" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_StopMode_ComboBox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_AbsoluteStop_DateTimePicker" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_RelativeStop_TimeSpanControl" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}" MaxWidth="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
@@ -205,51 +105,50 @@
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Start</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="PART_StartMode_ComboBox"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding can_edit_values}"
|
||||
ItemsSource="{Binding start_modes}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding start_mode}"
|
||||
/>
|
||||
<c:DateTimePicker
|
||||
x:Name="PART_AbsoluteStart_DateTimePicker"
|
||||
Grid.Column="4"
|
||||
IsEnabled="{Binding can_edit_values}"
|
||||
TimeTuple="{Binding absolute_start}"
|
||||
Visibility="{Binding show_absolute_start, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
/>
|
||||
<c:TimeSpanControl
|
||||
x:Name="PART_RelativeStart_TimeSpanControl"
|
||||
Grid.Column="4"
|
||||
IsEnabled="{Binding can_edit_values}"
|
||||
TimeSpan="{Binding relative_start, Mode=TwoWay}"
|
||||
Visibility="{Binding show_relative_start, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
/>
|
||||
<TextBlock Grid.Row="2" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Stop</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="PART_StopMode_ComboBox"
|
||||
Grid.Column="2"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding can_edit_values}"
|
||||
ItemsSource="{Binding stop_modes}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding stop_mode}"
|
||||
/>
|
||||
<c:DateTimePicker
|
||||
x:Name="PART_AbsoluteStop_DateTimePicker"
|
||||
Grid.Row="2"
|
||||
Grid.Column="4"
|
||||
IsEnabled="{Binding can_edit_values}"
|
||||
TimeTuple="{Binding absolute_stop}"
|
||||
Visibility="{Binding show_absolute_stop, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
/>
|
||||
<c:TimeSpanControl
|
||||
x:Name="PART_RelativeStop_TimeSpanControl"
|
||||
Grid.Row="2"
|
||||
Grid.Column="4"
|
||||
IsEnabled="{Binding can_edit_values}"
|
||||
TimeSpan="{Binding relative_stop, Mode=TwoWay}"
|
||||
Visibility="{Binding show_relative_stop, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="4"
|
||||
Grid.ColumnSpan="5"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -263,40 +162,7 @@
|
||||
</usp:AlgoContentTemplateSelector.TimeBoxOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.TickOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_DetachOnToward_CheckBox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_CrossThreshold_QuantityMeasureControl" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_CrossTicks_TickUpDownUInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_MaxChaseTicks_TickUpDownUInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TickOffsetOnDeploy_TickUpDownUInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}" />
|
||||
@@ -315,17 +181,16 @@
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox x:Name="PART_DetachOnToward_CheckBox" Grid.ColumnSpan="3" VerticalAlignment="Center" IsChecked="{Binding detach_on_toward}">Detach on Toward</CheckBox>
|
||||
<CheckBox Grid.ColumnSpan="3" VerticalAlignment="Center" IsEnabled="{Binding can_edit_values}" IsChecked="{Binding detach_on_toward}">Detach on Toward</CheckBox>
|
||||
<TextBlock Grid.Row="2" VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<uag:QuantityMeasureControl x:Name="PART_CrossThreshold_QuantityMeasureControl" Grid.Row="2" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
<uag:QuantityMeasureControl Grid.Row="2" Grid.Column="2" IsEnabled="{Binding can_edit_values}" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
<TextBlock Grid.Row="4" VerticalAlignment="Center">Cross Ticks</TextBlock>
|
||||
<c:TickUpDownUInt x:Name="PART_CrossTicks_TickUpDownUInt" Grid.Row="4" Grid.Column="2" Value="{Binding cross_ticks}"/>
|
||||
<c:TickUpDownUInt Grid.Row="4" Grid.Column="2" IsEnabled="{Binding can_edit_values}" Value="{Binding cross_ticks}"/>
|
||||
<TextBlock Grid.Row="6" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<c:TickUpDownUInt x:Name="PART_MaxChaseTicks_TickUpDownUInt" Grid.Row="6" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
|
||||
<c:TickUpDownUInt Grid.Row="6" Grid.Column="2" IsEnabled="{Binding can_edit_values}" Value="{Binding max_chase_ticks}"/>
|
||||
<TextBlock Grid.Row="8" VerticalAlignment="Center">Initial Tick Offset</TextBlock>
|
||||
<c:TickUpDownUInt x:Name="PART_TickOffsetOnDeploy_TickUpDownUInt" Grid.Row="8" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
|
||||
<c:TickUpDownUInt Grid.Row="8" Grid.Column="2" IsEnabled="{Binding can_edit_values}" Value="{Binding tick_offset_on_deploy}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="10"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -339,37 +204,7 @@
|
||||
</usp:AlgoContentTemplateSelector.TickOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.LeanOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_CrossThreshold_QuantityMeasureControl" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_MaxChaseTicks_TickUpDownUInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TradeTowardTypes_ComboBox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_Child_AlgoPickerNodeView" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
@@ -385,22 +220,21 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<TextBlock VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="When the size of the opposite-inside market falls below this size threshold the order will payup."/>
|
||||
</StackPanel>
|
||||
<uag:QuantityMeasureControl x:Name="PART_CrossThreshold_QuantityMeasureControl" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
<uag:QuantityMeasureControl Grid.Column="2" IsEnabled="{Binding can_edit_values}" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="The max number of ticks the order will continue to pay up after the initial payup."/>
|
||||
</StackPanel>
|
||||
<c:TickUpDownUInt x:Name="PART_MaxChaseTicks_TickUpDownUInt" Grid.Row="2" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
|
||||
<c:TickUpDownUInt Grid.Row="2" Grid.Column="2" IsEnabled="{Binding can_edit_values}" Value="{Binding max_chase_ticks}"/>
|
||||
<StackPanel Grid.Row="4" Orientation="Horizontal">
|
||||
<TextBlock>On Toward</TextBlock>
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="What should happen when the market moves toward the order."/>
|
||||
</StackPanel>
|
||||
<ComboBox x:Name="PART_TradeTowardTypes_ComboBox" Grid.Row="4" Grid.Column="2" ItemsSource="{Binding trade_toward_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding trade_toward_behavior}"/>
|
||||
<ComboBox Grid.Row="4" Grid.Column="2" IsEnabled="{Binding can_edit_values}" ItemsSource="{Binding trade_toward_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding trade_toward_behavior}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="6"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -414,28 +248,7 @@
|
||||
</usp:AlgoContentTemplateSelector.LeanOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.JoinOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_JoinThreshold_QuantityMeasureControl" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
|
||||
@@ -450,9 +263,8 @@
|
||||
<TextBlock VerticalAlignment="Center">Join Thresh</TextBlock>
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="The Threshold at which the order will join the market when the market flips (the event where the best offer becomes the best bid in the buy case)."/>
|
||||
</StackPanel>
|
||||
<uag:QuantityMeasureControl x:Name="PART_JoinThreshold_QuantityMeasureControl" Grid.Column="2" QuantityMeasure="{Binding join_threshold}"/>
|
||||
<uag:QuantityMeasureControl Grid.Column="2" IsEnabled="{Binding can_edit_values}" QuantityMeasure="{Binding join_threshold}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -466,28 +278,7 @@
|
||||
</usp:AlgoContentTemplateSelector.JoinOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.TopOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TopThreshold_QuantityMeasureControl" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
|
||||
@@ -502,9 +293,8 @@
|
||||
<TextBlock VerticalAlignment="Center">Top Thresh</TextBlock>
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="If the order is at the top of book, the quantity remaining below which the order will be canceled."/>
|
||||
</StackPanel>
|
||||
<uag:QuantityMeasureControl x:Name="PART_TopThreshold_QuantityMeasureControl" Grid.Column="2" QuantityMeasure="{Binding top_threshold}"/>
|
||||
<uag:QuantityMeasureControl Grid.Column="2" IsEnabled="{Binding can_edit_values}" QuantityMeasure="{Binding top_threshold}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -518,28 +308,7 @@
|
||||
</usp:AlgoContentTemplateSelector.TopOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.CoverOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_HedgeTicks_TickUpDownInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
|
||||
@@ -556,9 +325,8 @@
|
||||
<TextBlock VerticalAlignment="Center">Hedge Ticks</TextBlock>
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="This is the number of ticks to offset the hedge."/>
|
||||
</StackPanel>
|
||||
<c:TickUpDownInt x:Name="PART_HedgeTicks_TickUpDownInt" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding hedge_ticks}"/>
|
||||
<c:TickUpDownInt Grid.Column="2" HorizontalAlignment="Stretch" IsEnabled="{Binding can_edit_values}" Value="{Binding hedge_ticks}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Hedge_AlgoPickerNodeView"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.hedge.algos}"
|
||||
@@ -568,7 +336,6 @@
|
||||
OrderKindTemplate="{StaticResource OrderKindTemplate}"
|
||||
/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="4"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -582,31 +349,7 @@
|
||||
</usp:AlgoContentTemplateSelector.CoverOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.OCOOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_OtherTicks_TickUpDownInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_OtherSideTypes_ComboBox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
@@ -625,14 +368,13 @@
|
||||
<TextBlock VerticalAlignment="Center" >Stop Ticks</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="This is the number of ticks away from the main order to start the 'stop' order." />
|
||||
</StackPanel>
|
||||
<c:TickUpDownInt x:Name="PART_OtherTicks_TickUpDownInt" Grid.Column="2" Value="{Binding other_ticks}"/>
|
||||
<c:TickUpDownInt Grid.Column="2" Value="{Binding other_ticks}"/>
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="2" >
|
||||
<TextBlock VerticalAlignment="Center" >Stop Side</TextBlock>
|
||||
<c:HelpButton Margin="4,0,0,0" ToolTip="The Side of the 'stop' order" />
|
||||
</StackPanel>
|
||||
<ComboBox x:Name="PART_OtherSideTypes_ComboBox" Grid.Row="2" Grid.Column="2" ItemsSource="{Binding other_side_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding other_side}"/>
|
||||
<ComboBox Grid.Row="2" Grid.Column="2" ItemsSource="{Binding other_side_types}" ItemTemplate="{StaticResource EnumDescriptionTemplate}" SelectedItem="{Binding other_side}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Other_AlgoPickerNodeView"
|
||||
Grid.Row="4"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.other.algos}"
|
||||
@@ -642,7 +384,6 @@
|
||||
OrderKindTemplate="{StaticResource OrderKindTemplate}"
|
||||
/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="6"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -656,28 +397,7 @@
|
||||
</usp:AlgoContentTemplateSelector.OCOOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.OffsetOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TickOffsetOnDeploy_TickUpDownUInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}" />
|
||||
@@ -689,9 +409,8 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center">Tick Offset</TextBlock>
|
||||
<c:TickUpDownUInt x:Name="PART_TickOffsetOnDeploy_TickUpDownUInt" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
|
||||
<c:TickUpDownUInt Grid.Column="2" IsEnabled="{Binding can_edit_values}" Value="{Binding tick_offset_on_deploy}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -705,70 +424,7 @@
|
||||
</usp:AlgoContentTemplateSelector.OffsetOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.TriggerOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
<u:StateDefinition>
|
||||
<MultiBinding Converter="{x:Static usp:AlgoContentTemplateSelector.TriggerTimeoutStateConverter}">
|
||||
<Binding Path="DataContext.is_timeout_checked" Source="{u:DirectReference ContentRoot}"/>
|
||||
<Binding Path="DataContext.editing_permissions" Source="{u:DirectReference ContentRoot}"/>
|
||||
</MultiBinding>
|
||||
</u:StateDefinition>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TimeoutSecs_TickUpDownInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TriggerPriceTypes_ComboBox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_PayupTicks_TickUpDownInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TimeoutEnabled_CheckBox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TimeoutSecs_TickUpDownInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_PriceoutEnabled_CheckBox" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup Name="{x:Static GroupName, MemberType={u:Nested usp:AlgoContentTemplateSelector.VisualStateNames.TriggerTimeout}}">
|
||||
<VisualState Name="{x:Static Active, MemberType={u:Nested usp:AlgoContentTemplateSelector.VisualStateNames.TriggerTimeout}}">
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TimeoutSecs_TickUpDownInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState Name="{x:Static Inactive, MemberType={u:Nested usp:AlgoContentTemplateSelector.VisualStateNames.TriggerTimeout}}">
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_TimeoutSecs_TickUpDownInt" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
@@ -796,9 +452,9 @@
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="This is the side of the market that is observed for the trigger condition. When this market touches (or penetrates the trigger price), the child order is sent to the market." />
|
||||
</StackPanel>
|
||||
<ComboBox
|
||||
x:Name="PART_TriggerPriceTypes_ComboBox"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding can_edit_values}"
|
||||
ItemsSource="{Binding trigger_price_types}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding trigger_price_type}"
|
||||
@@ -819,9 +475,8 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock>Payup\less</TextBlock>
|
||||
<c:TickUpDownInt x:Name="PART_PayupTicks_TickUpDownInt" Grid.Column="2" Value="{Binding payup_ticks}"/>
|
||||
<c:TickUpDownInt Grid.Column="2" IsEnabled="{Binding can_edit_values}" Value="{Binding payup_ticks}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -849,7 +504,7 @@
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="The trigger order can be canceled after a specified amount of time (Timeout) or conditionally (Price Conditional) if the trigger condition ceases to hold." />
|
||||
</StackPanel>
|
||||
</GroupBox.Header>
|
||||
<Grid>
|
||||
<Grid IsEnabled="{Binding can_edit_values}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="{Binding Source={StaticResource HorizontalPush1}}"/>
|
||||
@@ -860,11 +515,11 @@
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<CheckBox x:Name="PART_TimeoutEnabled_CheckBox" IsChecked="{Binding is_timeout_checked}" VerticalContentAlignment="Center">
|
||||
<CheckBox IsChecked="{Binding is_timeout_checked}" VerticalContentAlignment="Center">
|
||||
Timeout (secs)
|
||||
</CheckBox>
|
||||
<c:TickUpDownInt x:Name="PART_TimeoutSecs_TickUpDownInt" Grid.Column="2" Minimum="0" Value="{Binding timeout_secs}"/>
|
||||
<CheckBox x:Name="PART_PriceoutEnabled_CheckBox" Grid.Row="2" Grid.ColumnSpan="3" IsChecked="{Binding is_priceout_checked}" VerticalAlignment="Center">
|
||||
<c:TickUpDownInt Grid.Column="2" IsEnabled="{Binding is_timeout_checked, Mode=OneWay}" Minimum="0" Value="{Binding timeout_secs}"/>
|
||||
<CheckBox Grid.Row="2" Grid.ColumnSpan="3" IsChecked="{Binding is_priceout_checked}" VerticalAlignment="Center">
|
||||
Price Conditional
|
||||
</CheckBox>
|
||||
</Grid>
|
||||
@@ -874,28 +529,7 @@
|
||||
</usp:AlgoContentTemplateSelector.TriggerOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.IcebergOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_ShowQuantity_TickUpDownDouble" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
|
||||
@@ -907,9 +541,8 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{StaticResource Margin.LP3}">Show Quantity</TextBlock>
|
||||
<c:TickUpDownDouble x:Name="PART_ShowQuantity_TickUpDownDouble" Grid.Column="2" HorizontalAlignment="Stretch" Value="{Binding show_quantity}"/>
|
||||
<c:TickUpDownDouble Grid.Column="2" HorizontalAlignment="Stretch" IsEnabled="{Binding can_edit_values}" Value="{Binding show_quantity}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
@@ -923,34 +556,12 @@
|
||||
</usp:AlgoContentTemplateSelector.IcebergOrderTemplate>
|
||||
<usp:AlgoContentTemplateSelector.SpreaderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid x:Name="ContentRoot">
|
||||
<u:StateManager.Groups>
|
||||
<u:StateGroupsManager Target="{Binding Source={u:DirectReference ContentRoot}}">
|
||||
<u:StateDefinitionCollection>
|
||||
<u:StateDefinition State="{Binding DataContext.editing_permissions, Source={u:DirectReference ContentRoot}, Converter={StaticResource ValuesEditingStateConverter}}"/>
|
||||
</u:StateDefinitionCollection>
|
||||
</u:StateGroupsManager>
|
||||
</u:StateManager.Groups>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup Name="ValuesEditingStates">
|
||||
<VisualState Name="{StaticResource CanEditValuesState}"/>
|
||||
<VisualState Name="{StaticResource CannotEditValuesState}">
|
||||
<VisualState.Storyboard>
|
||||
<Storyboard>
|
||||
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="PART_Strategy_ExecutionStrategyEditor" Storyboard.TargetProperty="(UIElement.IsEnabled)">
|
||||
<DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
|
||||
</BooleanAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState.Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid IsEnabled="{Binding can_edit_values}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<usp:ExecutionStrategyEditor
|
||||
x:Name="PART_Strategy_ExecutionStrategyEditor"
|
||||
Margin="6"
|
||||
HorizontalAlignment="Stretch"
|
||||
DataContext="{Binding strategy_editor}"
|
||||
@@ -964,7 +575,7 @@
|
||||
RemoveLegCommand="{Binding remove_leg}"
|
||||
ContractPickerModel="{Binding contract_picker_model}"
|
||||
/>
|
||||
<c:DialogSkirt Grid.Row="1" Margin="-4" VerticalAlignment="Top" RightItems="{Binding skirt_items}"/>
|
||||
<c:DialogSkirt Grid.Row="1" Margin="-4" RightItems="{Binding skirt_items}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:AlgoContentTemplateSelector.SpreaderTemplate>
|
||||
@@ -982,11 +593,6 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ItemsControl ItemsSource="{Binding parameters}" Grid.ColumnSpan="3">
|
||||
<ItemsControl.ItemContainerStyle>
|
||||
<Style>
|
||||
<Setter Property="Control.Margin" Value="{StaticResource Margin.TP1}"/>
|
||||
</Style>
|
||||
</ItemsControl.ItemContainerStyle>
|
||||
<ItemsControl.ItemTemplateSelector>
|
||||
<usp:DynamicOrderParamTemplateSelector>
|
||||
<usp:DynamicOrderParamTemplateSelector.BoolTemplate>
|
||||
@@ -998,12 +604,7 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.ParamName, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<CheckBox
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
IsChecked="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
<CheckBox HorizontalAlignment="Stretch" Grid.Column="2" IsChecked="{Binding value, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.BoolTemplate>
|
||||
@@ -1016,11 +617,7 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.ParamName, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<c:TickUpDownDouble
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
<c:TickUpDownDouble Grid.Column="2" Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.DoubleTemplate>
|
||||
@@ -1033,11 +630,7 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.ParamName, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<c:TickUpDownInt
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
<c:TickUpDownInt Grid.Column="2" Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.IntTemplate>
|
||||
@@ -1050,11 +643,7 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.ParamName, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<c:TickUpDownUInt
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
<c:TickUpDownUInt Grid.Column="2" Value="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.UIntTemplate>
|
||||
@@ -1067,12 +656,7 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.ParamName, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<TextBox
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
Text="{Binding value, UpdateSourceTrigger=PropertyChanged}"
|
||||
/>
|
||||
<TextBox HorizontalAlignment="Stretch" Grid.Column="2" Text="{Binding value, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.StringTemplate>
|
||||
@@ -1085,12 +669,7 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding descriptor.ParamName, Converter={x:Static ut:DisplayNameHelper.SnakeToTitleCaseConverter}}"/>
|
||||
<ComboBox
|
||||
Grid.Column="2"
|
||||
IsEnabled="{Binding DataContext.editing_permissions, RelativeSource={RelativeSource AncestorType=ItemsControl}, Converter={StaticResource CanEditValuesConverter}}"
|
||||
ItemsSource="{Binding elements}"
|
||||
SelectedItem="{Binding selected_element}"
|
||||
/>
|
||||
<ComboBox Grid.Column="2" ItemsSource="{Binding descriptor.ParamType.Enumerated.Element}" SelectedItem="{Binding value}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:DynamicOrderParamTemplateSelector.EnumTemplate>
|
||||
@@ -1103,7 +682,6 @@
|
||||
<uag:AlgoPickerNodeView
|
||||
ItemsSource="{Binding algos_view}"
|
||||
SelectedItem="{Binding selected_algo}"
|
||||
CanEditStructure="{Binding DataContext.editing_permissions, Converter={StaticResource CanEditStructureConverter}, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
|
||||
ContentTemplateSelector="{DynamicResource AlgoTemplateSelector}"
|
||||
OrderKind="{x:Static usp:AlgoContentTemplateSelector.ChildKind}"
|
||||
OrderKindTemplate="{StaticResource OrderKindTemplate}"
|
||||
|
||||
@@ -9,20 +9,13 @@ namespace mbts.ui.spark {
|
||||
class AlgoContentTemplateSelector : DataTemplateSelector {
|
||||
public const string
|
||||
DefinitionDropDownStyleKey = nameof(DefinitionDropDownStyleKey),
|
||||
TemplateDropDownStyleKey = nameof(TemplateDropDownStyleKey),
|
||||
TriggerTimeoutEnabledStateName = nameof(TriggerTimeoutEnabledStateName);
|
||||
TemplateDropDownStyleKey = nameof(TemplateDropDownStyleKey);
|
||||
|
||||
public static readonly IValueConverter
|
||||
AlgoDropDownStyleKeyConverter = ValueConverter.create(obj => {
|
||||
public static readonly IValueConverter AlgoDropDownStyleKeyConverter = ValueConverter.create(obj => {
|
||||
if (obj is AlgoDefinitionTemplateVM t) return TemplateDropDownStyleKey;
|
||||
if (obj is AlgoDefinitionVM d) return DefinitionDropDownStyleKey;
|
||||
return null;
|
||||
}),
|
||||
DataContextConverter = ValueConverter.create(obj => obj is AlgoVMWrapper w ? w.value.algo_root : obj);
|
||||
|
||||
public static IMultiValueConverter TriggerTimeoutStateConverter =
|
||||
ValueConverter.create<bool, AlgoEditingPermissions, string>((is_timeout_enabled, editing_permissions) =>
|
||||
is_timeout_enabled && editing_permissions.HasFlag(AlgoEditingPermissions.AllowEditValues) ? VisualStateNames.TriggerTimeout.Active : VisualStateNames.TriggerTimeout.Inactive);
|
||||
});
|
||||
|
||||
public static readonly OrderKind ChildKind = new OrderKind("Child", "This is the type of order to be placed when the algo is triggered.");
|
||||
public static readonly OrderKind HedgeKind = new OrderKind("Hedge", "This is the type of the hedge order to send for each fill of Child Order.");
|
||||
@@ -70,15 +63,6 @@ namespace mbts.ui.spark {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class VisualStateNames {
|
||||
public static class TriggerTimeout {
|
||||
public const string GroupName = nameof(TriggerTimeout);
|
||||
public static readonly string
|
||||
Active = $"{nameof(TriggerTimeout)}{VisualStates.Active}",
|
||||
Inactive = $"{nameof(TriggerTimeout)}{VisualStates.Inactive}";
|
||||
}
|
||||
}
|
||||
|
||||
public class OrderKind {
|
||||
public OrderKind(string name, string description) {
|
||||
this.name = name;
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:uag="clr-namespace:mbts.ui.spark.algo"
|
||||
xmlns:uspcv="clr-namespace:mbts.ui.spark.converters"
|
||||
>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Resources/Converters.xaml"/>
|
||||
<ResourceDictionary Source="/view/Resources/ComboBoxes.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Order/OrderTypeItem.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<cv:EnumHasFlagsConverter x:Key="CanEditStructureConverter">
|
||||
<sp:AlgoEditingPermissions>AllowEditStructure</sp:AlgoEditingPermissions>
|
||||
</cv:EnumHasFlagsConverter>
|
||||
<!--<uspcv:OrderTypeBrushConverter x:Key="OrderTypeBrushConverter"/>-->
|
||||
<ControlTemplate x:Key="AlgoPickerNodeViewTemplate" TargetType="uag:AlgoPickerNodeView">
|
||||
<GroupBox
|
||||
x:Name="Root"
|
||||
@@ -108,11 +105,11 @@
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderBrush" Value="{Binding SelectedItem, RelativeSource={RelativeSource Self}, Converter={StaticResource OrderTypeBrushConverter}}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CanEditStructure" Value="{Binding editing_permissions, Converter={StaticResource CanEditStructureConverter}}"/>
|
||||
<Setter Property="CanEditStructure" Value="{Binding can_edit_structure, FallbackValue={u:True}}"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Top"/>
|
||||
<Setter Property="Template" Value="{StaticResource AlgoPickerNodeViewTemplate}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary>
|
||||
@@ -1,10 +1,7 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:txt="clr-namespace:mbts.ui.utility.text"
|
||||
>
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Colors.xaml" />
|
||||
<ResourceDictionary Source="/view/Resources/Converters.xaml" />
|
||||
@@ -54,12 +51,7 @@
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="c:TickUpDownDouble">
|
||||
<c:TickUpDown x:Name="PART_tick_up_down">
|
||||
<TextBox
|
||||
Name="PART_TextBox"
|
||||
u:DoubleDisplay.Multiplier="{TemplateBinding Multiplier}"
|
||||
u:DoubleDisplay.Format="{TemplateBinding Format}"
|
||||
u:TextBoxBehavior.InputFilter="{u:RegexInputFilter Regex={x:Static Decimal, MemberType={u:Nested txt:TextUtils.RegexFilters}}}"
|
||||
/>
|
||||
<TextBox Name="PART_TextBox" u:DoubleDisplay.Multiplier="{TemplateBinding Multiplier}" u:DoubleDisplay.Format="{TemplateBinding Format}"/>
|
||||
</c:TickUpDown>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
using mbts.ui.extensions;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using mbts.ui.converters;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
class TickUpDownDouble : Control {
|
||||
IDisposable _text_hooks = null;
|
||||
TextBox _text_box;
|
||||
|
||||
static TickUpDownDouble() =>
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(TickUpDownDouble), new FrameworkPropertyMetadata(typeof(TickUpDownDouble)));
|
||||
|
||||
#region FlushTextImmediately
|
||||
public static readonly DependencyProperty FlushTextImmediatelyProperty =
|
||||
WPFHelper.create_dp<bool, TickUpDownDouble>(nameof(FlushTextImmediately));
|
||||
WPFHelper.create_dp<bool, TickUpDownDouble>(
|
||||
nameof(FlushTextImmediately),
|
||||
(o, v) => o._init_text_binding(v));
|
||||
|
||||
public bool FlushTextImmediately {
|
||||
get => (bool)GetValue(FlushTextImmediatelyProperty);
|
||||
@@ -22,19 +19,10 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static readonly DependencyProperty ValueProperty =
|
||||
WPFHelper.create_dp<double, TickUpDownDouble>(
|
||||
nameof(Value),
|
||||
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, v) => {
|
||||
if (o._text_box != null) {
|
||||
if (!double.TryParse(o._text_box.Text, out double from_text))
|
||||
from_text = double.NaN;
|
||||
if (!from_text.Equals(v) && !double.IsNaN(v)) {
|
||||
o._text_box.Text = o.Format is string f ? v.ToString(f, CultureInfo.CurrentCulture) : v.ToString();
|
||||
}
|
||||
}
|
||||
});
|
||||
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(TickUpDownDouble), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged));
|
||||
static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
|
||||
}
|
||||
public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register("Increment", typeof(double), typeof(TickUpDownDouble), new FrameworkPropertyMetadata(1.0));
|
||||
public static readonly DependencyProperty MultiplierProperty = DependencyProperty.Register("Multiplier", typeof(double), typeof(TickUpDownDouble), new FrameworkPropertyMetadata(1.0));
|
||||
public static readonly DependencyProperty FormatProperty = DependencyProperty.Register("Format", typeof(string), typeof(TickUpDownDouble));
|
||||
@@ -47,23 +35,18 @@ namespace mbts.ui.controls {
|
||||
public double TickDefaultValue { get => (double)GetValue(TickDefaultValueProperty); set => SetValue(TickDefaultValueProperty, value); }
|
||||
|
||||
public override void OnApplyTemplate() {
|
||||
Disposable.dispose(ref _text_hooks);
|
||||
var tick_up_down = this.assert_template_child<TickUpDown>("PART_tick_up_down");
|
||||
_text_box = this.assert_template_child<TextBox>("PART_TextBox");
|
||||
_text_box.Text = _format_value(Value, Format);
|
||||
tick_up_down.TickUpCommand = new DelegateCommand(_tick_up);
|
||||
tick_up_down.TickDownCommand = new DelegateCommand(_tick_down);
|
||||
push_text_to_value();
|
||||
_text_hooks = _text_box.SubscribeTextChanged((s, e) => push_text_to_value());
|
||||
void push_text_to_value() {
|
||||
if (double.TryParse(_text_box.Text, out double d)) {
|
||||
SetCurrentValue(ValueProperty, d);
|
||||
} else {
|
||||
SetCurrentValue(ValueProperty, double.NaN);
|
||||
}
|
||||
}
|
||||
_init_text_binding();
|
||||
}
|
||||
|
||||
void _init_text_binding() =>
|
||||
_init_text_binding(FlushTextImmediately);
|
||||
|
||||
void _init_text_binding(bool flush_text_immediately) =>
|
||||
TickUpDownHelper.init_text_binding(this, ValueConverters.DoubleTextConverter, flush_text_immediately);
|
||||
|
||||
void _tick_up() {
|
||||
double v = Value;
|
||||
Value = !DoubleHelper.IsNaNOrInfinity(v) ? v + Increment : TickDefaultValue;
|
||||
@@ -75,8 +58,5 @@ namespace mbts.ui.controls {
|
||||
Value = v - Increment;
|
||||
} else Value = TickDefaultValue;
|
||||
}
|
||||
|
||||
static string _format_value(double value, string format) =>
|
||||
format == null ? value.ToString() : value.ToString(format, CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace mbts.ui.converters {
|
||||
[ContentProperty(nameof(Flags))]
|
||||
class EnumHasFlagsConverter : IValueConverter {
|
||||
Func<object, object, bool> _convert_del;
|
||||
|
||||
public IList Flags { get; } = new List<object>();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
if (value == null)
|
||||
return false;
|
||||
if (Flags.Count == 0)
|
||||
return true;
|
||||
if (_convert_del == null)
|
||||
_convert_del = EnumHelper.get_has_flags_delegate(value.GetType());
|
||||
foreach (var f in Flags) {
|
||||
if (!_convert_del(value, f))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.utility.text;
|
||||
using mbts.ui.utility.text;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
@@ -10,22 +8,9 @@ namespace mbts.ui {
|
||||
enum RelativePosition { Left, Right, Above, Below }
|
||||
|
||||
static class AdornerHelper {
|
||||
public static void attach(this Adorner adorner) => try_attach(adorner);
|
||||
|
||||
public static async Task attach_async(this Adorner adorner) {
|
||||
if (!adorner.try_attach()) {
|
||||
await adorner.LoadedAsync();
|
||||
adorner.attach();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool try_attach(this Adorner adorner) {
|
||||
public static void attach(this Adorner adorner) {
|
||||
var al = AdornerLayer.GetAdornerLayer(adorner.AdornedElement);
|
||||
if (al != null) {
|
||||
al.Add(adorner);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (al != null) al.Add(adorner);
|
||||
}
|
||||
|
||||
public static void detach(this Adorner adorner) {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using CompositeDisposable = System.Reactive.Disposables.CompositeDisposable;
|
||||
|
||||
namespace mbts.ui.extensions {
|
||||
static class FrameworkElementHelper {
|
||||
@@ -32,42 +29,5 @@ namespace mbts.ui.extensions {
|
||||
element.SizeChanged += callback;
|
||||
return Disposable.Create(() => element.SizeChanged -= callback);
|
||||
}
|
||||
|
||||
public static IDisposable SubscribeDataContextDisposed(this FrameworkElement element, Func<IDisposable> set_hooks) {
|
||||
IDisposable data_context_sub = null;
|
||||
subscribe_data_context(element.DataContext as IWillLetYouKnowWhenIDispose);
|
||||
element.DataContextChanged += on_data_context_changed;
|
||||
return Disposable.Create(() => {
|
||||
element.DataContextChanged -= on_data_context_changed;
|
||||
Disposable.dispose(ref data_context_sub);
|
||||
});
|
||||
void on_data_context_changed(object sender, DependencyPropertyChangedEventArgs e) =>
|
||||
subscribe_data_context(e.NewValue as IWillLetYouKnowWhenIDispose);
|
||||
|
||||
void on_data_context_disposed(bool disposing) =>
|
||||
Disposable.dispose(ref data_context_sub);
|
||||
|
||||
void subscribe_data_context(IWillLetYouKnowWhenIDispose dc) {
|
||||
Disposable.dispose(ref data_context_sub);
|
||||
if (dc != null) {
|
||||
dc.OnDisposed += on_data_context_disposed;
|
||||
data_context_sub = new CompositeDisposable(set_hooks()) {
|
||||
() => dc.OnDisposed -= on_data_context_disposed
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<RoutedEventArgs> LoadedAsync(this FrameworkElement element) {
|
||||
if (element.IsLoaded)
|
||||
return Task.FromResult(new RoutedEventArgs(FrameworkElement.LoadedEvent, element));
|
||||
var tcs = new TaskCompletionSource<RoutedEventArgs>();
|
||||
element.Loaded += on_loaded;
|
||||
return tcs.Task;
|
||||
void on_loaded(object sender, RoutedEventArgs e) {
|
||||
element.Loaded -= on_loaded;
|
||||
tcs.TrySetResult(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,10 +103,8 @@ namespace mbts.ui {
|
||||
}
|
||||
|
||||
static void tb_keydown(object sender, KeyEventArgs e) {
|
||||
if (e.Key == Key.Enter) {
|
||||
var binding = BindingOperations.GetBindingExpression(sender as DependencyObject, TextBox.TextProperty);
|
||||
binding.UpdateSource();
|
||||
}
|
||||
if (e.Key == Key.Enter)
|
||||
BindingOperations.GetBindingExpression(sender as DependencyObject, TextBox.TextProperty)?.UpdateSource();
|
||||
}
|
||||
|
||||
public static bool GetCommitOnEnter(DependencyObject obj) => (bool)obj.GetValue(CommitOnEnterProperty);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using mbts.ui.utility.text;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
@@ -108,11 +108,6 @@ namespace mbts.ui.extensions {
|
||||
return Disposable.Create(() => target.PreviewMouseDown -= callback);
|
||||
}
|
||||
|
||||
public static IDisposable SubscribePreviewMouseMove(this UIElement target, MouseEventHandler callback) {
|
||||
target.PreviewMouseMove += callback;
|
||||
return Disposable.Create(() => target.PreviewMouseMove -= callback);
|
||||
}
|
||||
|
||||
public static IDisposable SubscribePreviewDragOver(this UIElement target, DragEventHandler callback) {
|
||||
target.PreviewDragOver += callback;
|
||||
return Disposable.Create(() => target.PreviewDragOver -= callback);
|
||||
@@ -138,13 +133,6 @@ namespace mbts.ui.extensions {
|
||||
return Disposable.Create(() => target.PreviewKeyDown -= callback);
|
||||
}
|
||||
|
||||
public static IObservable<KeyEventArgs> SubscribePreviewKeyDown(this UIElement target) =>
|
||||
Observable.Create<KeyEventArgs>(o => {
|
||||
target.PreviewKeyDown += on_preview_key_down;
|
||||
return Disposable.Create(() => target.PreviewKeyDown -= on_preview_key_down);
|
||||
void on_preview_key_down(object sender, KeyEventArgs e) => o.OnNext(e);
|
||||
});
|
||||
|
||||
public static IDisposable SubscribeIsKeyboardFocusWithinChanged(this UIElement target, DependencyPropertyChangedEventHandler callback) {
|
||||
target.IsKeyboardFocusWithinChanged += callback;
|
||||
return Disposable.Create(() => target.IsKeyboardFocusWithinChanged -= callback);
|
||||
@@ -225,4 +213,4 @@ namespace mbts.ui.extensions {
|
||||
!DoubleHelper.IsNaNOrInfinityOr(curr, 0) && !DoubleHelper.IsNaNOrInfinityOr(max, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,32 +211,6 @@ namespace mbts.ui {
|
||||
foreach (var dp in d_props)
|
||||
BindingOperations.ClearBinding(d_obj, dp);
|
||||
}
|
||||
|
||||
public static IDisposable attach_binding(this DependencyObject target, DependencyProperty target_property, string path, FrameworkElement source_view, IValueConverter converter = null) {
|
||||
object source_vm = source_view.DataContext;
|
||||
try_reset_binding();
|
||||
source_view.DataContextChanged += on_data_context_changed;
|
||||
return Disposable.Create(() => {
|
||||
try {
|
||||
source_view.DataContextChanged -= on_data_context_changed;
|
||||
} catch (Exception ex) {
|
||||
Log.Ui.warn($"Error detaching from {nameof(source_view)}.{nameof(FrameworkElement.DataContextChanged)} event: {ex}.");
|
||||
}
|
||||
});
|
||||
void on_data_context_changed(object sender, DependencyPropertyChangedEventArgs e) {
|
||||
source_vm = e.NewValue;
|
||||
try_reset_binding();
|
||||
}
|
||||
void try_reset_binding() {
|
||||
if (source_vm != null)
|
||||
target.reset_binding(target_property, new Binding(path) { Source = source_vm, Converter = converter });
|
||||
}
|
||||
}
|
||||
|
||||
public static void reset_binding(this DependencyObject target, DependencyProperty target_property, BindingBase binding) {
|
||||
BindingOperations.ClearBinding(target, target_property);
|
||||
BindingOperations.SetBinding(target, target_property, binding);
|
||||
}
|
||||
|
||||
public static void TryRaiseCanExecuteChanged(this ICommand command) {
|
||||
if (command == null) return;
|
||||
|
||||
@@ -593,7 +593,6 @@
|
||||
<Setter Property="Content" Value="Cancel" />
|
||||
<Setter Property="IsCancel" Value="True" />
|
||||
<Setter Property="u:Behavior.CloseWindow" Value="True" />
|
||||
<Setter Property="ToolTip" Value="Cancel (Esc)"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ComputeButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" x:Shared="False">
|
||||
|
||||
@@ -20,18 +20,6 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="DisabledPlaceholderComboBox" TargetType="ComboBox">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
<Setter Property="Foreground" Value="Gray"/>
|
||||
<Setter Property="SelectedIndex" Value="0"/>
|
||||
<Setter Property="ItemsSource">
|
||||
<Setter.Value>
|
||||
<x:ArrayExtension Type="{x:Type sys:String}">
|
||||
<sys:String>(N/A)</sys:String>
|
||||
</x:ArrayExtension>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<!-- Removed ExpirationLabel
|
||||
<Style x:Key="ExpirationFilter" TargetType="control:MultiSelectDropDown" BasedOn="{StaticResource {x:Type control:MultiSelectDropDown}}">
|
||||
<Setter Property="ItemTemplate">
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace mbts.ui.shell {
|
||||
Configuration.initialize();
|
||||
}
|
||||
catch (ApplicationException e) {
|
||||
ErrorMessage = e.Message;
|
||||
ErrorMessage = (e.Message ?? string.Empty) + " " + (App.stunnel_job_error ?? string.Empty);
|
||||
HasFatalError = true;
|
||||
return;
|
||||
}
|
||||
@@ -106,7 +106,13 @@ namespace mbts.ui.shell {
|
||||
var auth = _ctx.locate<AuthService>();
|
||||
var response = await auth.get_users_async(new GetUsersRequest());
|
||||
if (response.success) {
|
||||
Users = response.value.Users.Select(x => new User(x.Login,x.Id)).OrderBy(u => u.name).ToArray();
|
||||
if (!string.IsNullOrEmpty(response.value.Error)) {
|
||||
ErrorMessage = response.value.Error;
|
||||
HasFatalError = true;
|
||||
}
|
||||
else {
|
||||
Users = response.value.Users.Select(x => new User(x.Login,x.Id)).OrderBy(u => u.name).ToArray();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ErrorMessage = "Logon: " + response.error_message;
|
||||
|
||||
@@ -89,10 +89,6 @@ namespace mbts.ui.shell
|
||||
}
|
||||
|
||||
public Dispatcher get_dispatcher(ISharedWindow window) {
|
||||
if (window.thread_id == null) {
|
||||
window.thread_init();
|
||||
return Dispatcher.CurrentDispatcher;
|
||||
}
|
||||
var uid = get(window.thread_id);
|
||||
return attach_window(uid, window);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utilities;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
@@ -86,9 +84,6 @@ namespace mbts.spark {
|
||||
public static TimeInForce time_in_force(this OrderRecordImmutable order) =>
|
||||
order.HasDef ? order.Def.TimeInForce : TimeInForce.Undefined;
|
||||
|
||||
public static uint attached_target_order_key(this OrderRecordImmutable order) =>
|
||||
order.HasDef ? order.Def.AttachTargetOrderKey : 0;
|
||||
|
||||
public static bool is_exchange_order(this OrderRecordImmutable order) {
|
||||
if (order.HasDef && order.Def.HasCustomDef) {
|
||||
switch (order.Def.CustomDef.CustomCase) {
|
||||
@@ -101,24 +96,6 @@ namespace mbts.spark {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool can_attach(this OrderRecordImmutable order) =>
|
||||
order.working_quantity() > 0;
|
||||
|
||||
public static bool can_be_attached_to(this OrderRecordImmutable order) {
|
||||
if (order.HasDef && order.Def.HasCustomDef) {
|
||||
// TODO: implement with real logic (this is impl is a placeholder for development purposes)
|
||||
switch (order.Def.CustomDef.CustomCase) {
|
||||
case CustomOrderDefinition.CustomOneofCase.Iceberg:
|
||||
case CustomOrderDefinition.CustomOneofCase.Dynamic:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool can_detach(this OrderRecordImmutable order) =>
|
||||
order.attached_target_order_key() != 0;
|
||||
|
||||
public static string display_name(this OrderSecurityAlgoMeta order_tuple) =>
|
||||
display_name(order_tuple.order, order_tuple.algo);
|
||||
|
||||
@@ -164,20 +141,6 @@ namespace mbts.spark {
|
||||
public static string short_description(this OrderRecordImmutable order, ContractSpecificationImmutable? spec) =>
|
||||
$"{(order.HasDef ? order.Def.Side.ToString() : "(unknown-side)")} {(order.HasState ? ($@"{spec.format(order.State.Price)} @ {order.State.Quantity}") : "(unknown-price) @ (unknown-quantity)")}";
|
||||
|
||||
public static string short_description(this OrderRecordImmutable order, SecurityImmutable? security) {
|
||||
var sb = new StringBuilderEx("[", order.Key.ToString(), "] ", "[", camel_to_title_case(order.status().ToString()), "] ", security.display_name(), " ", order.side().ToString(), " ");
|
||||
if (order.HasState) {
|
||||
sb.Add(order.State.Quantity.ToString(), " @ ", security.spec_or_default().format(order.State.Price), " (filled ", order.State.FilledQuantity.ToString());
|
||||
if (order.State.FilledQuantity > 0) {
|
||||
sb.Add(" @ ", security.spec_or_default().format(order.State.AvgPrice));
|
||||
}
|
||||
sb.Add(")");
|
||||
} else {
|
||||
sb.Add("(unknown-quantity) @ (unknown-price)");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static bool has_parent(this OrderRecordImmutable order) =>
|
||||
order.ParentKey != 0;
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ namespace mbts.spark {
|
||||
_ask_vis_tup = new VisualGlyphTuple(),
|
||||
_price_vis_tup = new VisualGlyphTuple(),
|
||||
_last_vis_tup = new VisualGlyphTuple(),
|
||||
_volume_vis_tup = new VisualGlyphTuple();
|
||||
_volume_vis_tup = new VisualGlyphTuple(),
|
||||
_piq_vis_tup = new VisualGlyphTuple();
|
||||
|
||||
readonly RefValueContainer<IEnumerable<VolumeAtPriceLevelImmutable>> _volume_container =
|
||||
new RefValueContainer<IEnumerable<VolumeAtPriceLevelImmutable>>();
|
||||
@@ -92,7 +93,7 @@ namespace mbts.spark {
|
||||
};
|
||||
}
|
||||
|
||||
public enum ColumnType { Order, Bid, Price, Ask, Last, Volume, None }
|
||||
public enum ColumnType { Order, Bid, Price, Ask, Last, Volume, None, PIQ }
|
||||
|
||||
public event Action visible_columns_changed;
|
||||
|
||||
@@ -192,6 +193,18 @@ namespace mbts.spark {
|
||||
set => SetValue(LastColumnWidthProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PIQColumnWidthProperty =
|
||||
WPFHelper.create_dp<uint, LadderGridHost>(
|
||||
nameof(PIQColumnWidth),
|
||||
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
|
||||
(o, oldval, newval) => o._adjust_column_width(o.columns.FirstOrDefault(c => c.column_type == ColumnType.PIQ), (double)newval - (double)oldval),
|
||||
50u);
|
||||
|
||||
public uint PIQColumnWidth {
|
||||
get => (uint)GetValue(PIQColumnWidthProperty);
|
||||
set => SetValue(PIQColumnWidthProperty, value);
|
||||
}
|
||||
|
||||
#region Command
|
||||
public ICommand Command {
|
||||
get => (ICommand)GetValue(CommandProperty);
|
||||
@@ -404,7 +417,8 @@ namespace mbts.spark {
|
||||
double x = 0;
|
||||
var col_list = new List<Column> {
|
||||
new Column(_settings.orders_column, ColumnType.Order, null, x),
|
||||
new Column(_settings.bid_column, ColumnType.Bid, _bid_vis_tup, (x += _settings.orders_column.show ? _settings.orders_column.width : 0)),
|
||||
new Column(_settings.piq_column, ColumnType.PIQ, _piq_vis_tup, (x += _settings.orders_column.show ? _settings.orders_column.width : 0)),
|
||||
new Column(_settings.bid_column, ColumnType.Bid, _bid_vis_tup, (x += _settings.piq_column.show ? _settings.piq_column.width : 0)),
|
||||
(_price_column = new Column(_settings.price_column, ColumnType.Price, _price_vis_tup, (x += _settings.bid_column.show ? _settings.bid_column.width : 0))),
|
||||
new Column(_settings.ask_column, ColumnType.Ask, _ask_vis_tup, (x += _settings.price_column.show ? _settings.price_column.width : 0))
|
||||
};
|
||||
@@ -509,6 +523,8 @@ namespace mbts.spark {
|
||||
if (depth_changed)
|
||||
_refresh_sizes();
|
||||
}
|
||||
// TODO - detect changes in PIQ to selectively redraw
|
||||
_refresh_piq();
|
||||
_refresh_volume_at_price();
|
||||
_last_origin_price = _mkt_break_lines.origin;
|
||||
_price_dirty = false;
|
||||
@@ -545,6 +561,14 @@ namespace mbts.spark {
|
||||
LadderRender.render_last(_last_vis_tup.visual, _last_vis_tup.glyph_context, _last_trade, last_index, _settings.row_height, _settings.last_column);
|
||||
}
|
||||
|
||||
void _refresh_piq() {
|
||||
if (!_settings.piq_column.show)
|
||||
return;
|
||||
int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer);
|
||||
var piq_list = _vm.price_vs_piq;
|
||||
LadderRender.render_piqs(_piq_vis_tup.visual, _last_vis_tup.glyph_context, origin_index, _price_list, piq_list.ToArray(), _settings.row_height, security.ContractSpec, _settings.piq_column);
|
||||
}
|
||||
|
||||
void _refresh_sizes() {
|
||||
Log.assert_d(_price_list != null);
|
||||
int origin_index = LadderRender.index_of(_price_list, _mkt_break_lines.origin, _price_comparer);
|
||||
@@ -693,6 +717,7 @@ namespace mbts.spark {
|
||||
_ask_vis_tup.glyph_context = glyph_context(s.ask_column);
|
||||
_price_vis_tup.glyph_context = glyph_context(s.price_column);
|
||||
_last_vis_tup.glyph_context = glyph_context(s.last_column);
|
||||
_piq_vis_tup.glyph_context = glyph_context(s.piq_column);
|
||||
_volume_vis_tup.glyph_context = glyph_context(s.volume_column.general);
|
||||
|
||||
GlyphContext glyph_context(LadderColumnSettings cs) =>
|
||||
@@ -720,6 +745,7 @@ namespace mbts.spark {
|
||||
case ColumnType.Price: SetCurrentValue(price_column_widthProperty, col.settings.width); break;
|
||||
case ColumnType.Ask: SetCurrentValue(ask_column_widthProperty, col.settings.width); break;
|
||||
case ColumnType.Last: SetCurrentValue(LastColumnWidthProperty, col.settings.width); break;
|
||||
case ColumnType.PIQ: SetCurrentValue(PIQColumnWidthProperty, col.settings.width); break;
|
||||
case ColumnType.Volume:
|
||||
SetCurrentValue(VolumeColumnWidthProperty, col.settings.width);
|
||||
_volume_container.mark_dirty();
|
||||
|
||||
@@ -46,9 +46,9 @@ namespace mbts.spark {
|
||||
int working_quantity;
|
||||
|
||||
LadderModel model => _parent.model;
|
||||
Dictionary<uint, OrderRecordImmutable> _orders = new Dictionary<uint, OrderRecordImmutable>();
|
||||
Dictionary<uint, (OrderRecordImmutable order, uint? p)> _orders = new Dictionary<uint, (OrderRecordImmutable order, uint? p)>();
|
||||
|
||||
public LadderOrderVM(OrderRecordImmutable o, double real_price, LadderVM parent, IBus bus, IObservable<LadderSettings.BadgeStyle> bs) {
|
||||
public LadderOrderVM((OrderRecordImmutable order, uint? p) op, double real_price, LadderVM parent, IBus bus, IObservable<LadderSettings.BadgeStyle> bs) {
|
||||
_parent = parent;
|
||||
_bus = bus;
|
||||
_bs = bs;
|
||||
@@ -73,7 +73,7 @@ namespace mbts.spark {
|
||||
double get_font_size(LadderSettings s) => s.orders_column.font_size;
|
||||
Brush get_foreground(LadderSettings s) => s.orders_column.foreground.rgb_brush();
|
||||
//
|
||||
add_order(o);
|
||||
add_order(op);
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
_bs.Subscribe(badge => {
|
||||
@@ -109,19 +109,19 @@ namespace mbts.spark {
|
||||
// if this badge only represents a single order, then is_exchange_order and the border (= algo color)
|
||||
// should be set appropriately
|
||||
if (_orders.Count == 1) {
|
||||
var single_order = _orders.Values.First();
|
||||
var single_order = _orders.Values.First().order;
|
||||
var single_order_metadata = _parent.model.order_gateway.factory.get_metadata(single_order);
|
||||
border = single_order_metadata.Color.rgb_brush();
|
||||
is_exchange_order = OrderUtils.is_exchange_order(_orders.Values.First());
|
||||
is_exchange_order = OrderUtils.is_exchange_order(single_order);
|
||||
} else {
|
||||
border = new SolidColorBrush(color);
|
||||
is_exchange_order = false;
|
||||
}
|
||||
has_multiple_orders_at_same_level = _orders.Count > 1;
|
||||
is_hedge_order = _orders.Any(o => o.Value.sub_type() == OrderSubType.HedgingLeg);
|
||||
is_hedge_order = _orders.Any(op => op.Value.order.sub_type() == OrderSubType.HedgingLeg);
|
||||
|
||||
if (_orders.Any()) {
|
||||
summary = _orders.Values.Select(order => order.short_description(_parent.security.ContractSpec)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
summary = _orders.Values.Select(op => op.order.short_description(_parent.security.ContractSpec)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
}
|
||||
_set_badge_text();
|
||||
|
||||
@@ -132,8 +132,8 @@ namespace mbts.spark {
|
||||
RaisePropertyChanged(nameof(text_lines));
|
||||
}
|
||||
|
||||
public void add_order(OrderRecordImmutable new_order) {
|
||||
_orders[new_order.Key] = new_order;
|
||||
public void add_order((OrderRecordImmutable order, uint? p) new_op) {
|
||||
_orders[new_op.order.Key] = new_op;
|
||||
}
|
||||
|
||||
public void remove_order(uint order_key) {
|
||||
@@ -149,10 +149,11 @@ namespace mbts.spark {
|
||||
|
||||
public uint security_key => _parent.security.Id;
|
||||
public double price { get => double.IsNaN(_phantom_price) ? _real_price : _phantom_price; }
|
||||
public uint? min_piq => _orders.Select(op => op.Value.p).Where(piq => piq.HasValue).Min();
|
||||
public double real_price => _real_price;
|
||||
public Side side {
|
||||
get {
|
||||
var distinct_sides = _orders.Values.Select(o => o.side()).Distinct();
|
||||
var distinct_sides = _orders.Values.Select(op => op.order.side()).Distinct();
|
||||
if (distinct_sides.Count() > 1) {
|
||||
return Side.Undefined;
|
||||
}
|
||||
@@ -160,14 +161,14 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<OrderRecordImmutable> _parent_and_child_not_sharing_slot() => _orders.Values.Where(o => !_orders.ContainsKey(o.ParentKey));
|
||||
IEnumerable<OrderRecordImmutable> _parent_and_child_not_sharing_slot() => _orders.Values.Select(op => op.order).Where(o => !_orders.ContainsKey(o.ParentKey));
|
||||
|
||||
public IReadOnlyList<string> text_lines { get; private set; }
|
||||
|
||||
public bool has_parent => _orders.Values.Any( order => order.has_parent());
|
||||
public bool has_parent => _orders.Values.Any( op => op.order.has_parent());
|
||||
public bool has_phantom_price => !double.IsNaN(_phantom_price);
|
||||
|
||||
public ICommand detach_command => new DelegateCommand(() => _execute_command(_detach_order), () => !has_parent);
|
||||
public ICommand detach_command => new DelegateCommand(() => _execute_command(_crack_order), () => !has_parent);
|
||||
public ICommand cancel_command => new DelegateCommand(() => _execute_command(_cancel_order));
|
||||
//public ICommand modify_command => new DelegateCommand(() => _execute_command(() => _bus.post(SparkTags.ShowModifyOrderTag, _order.Key)), () => !has_parent);
|
||||
public event Action command_executed;
|
||||
@@ -191,17 +192,17 @@ namespace mbts.spark {
|
||||
// the setter is public so that the LadderVM can set this field
|
||||
public CustomOrderDefinition.CustomOneofCase parent_algo_type { get => _parent_algo_type.Value; set => _parent_algo_type.Value = value; }
|
||||
|
||||
public IEnumerable<OrderRecordImmutable> orders => _orders.Values;
|
||||
public IEnumerable<OrderRecordImmutable> orders => _orders.Values.Select(o => o.order);
|
||||
|
||||
void _cancel_order() {
|
||||
foreach (var order in _orders.Values) {
|
||||
model.order_gateway.cancel_order(order).ignore();
|
||||
foreach (var op in _orders.Values) {
|
||||
model.order_gateway.cancel_order(op.order).ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void _detach_order() {
|
||||
var distinct_parent_keys = _orders.Values.Where(o => o.has_parent()).Select(x => x.ParentKey).Distinct();
|
||||
model.order_gateway.detach_orders(distinct_parent_keys).ignore();
|
||||
void _crack_order() {
|
||||
var distinct_parent_keys = _orders.Values.Where(op => op.order.has_parent()).Select(x => x.order.ParentKey).Distinct();
|
||||
model.order_gateway.crack_orders(distinct_parent_keys).ignore();
|
||||
}
|
||||
|
||||
public async void try_set_price(OrderRecordImmutable order, double new_price) {
|
||||
@@ -252,20 +253,20 @@ namespace mbts.spark {
|
||||
// | Cancel | Qty | Price | ...
|
||||
// ------------------------
|
||||
//
|
||||
var price_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookGridView.ColumnKeys.Price.ToString());
|
||||
var price_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Price.ToString());
|
||||
var price_index = obvm.columns.indexOf(price_col);
|
||||
if (price_index > -1) {
|
||||
var existing_editprice_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookGridView.ColumnKeys.EditablePrice.ToString());
|
||||
var existing_editprice_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditablePrice.ToString());
|
||||
var existing_editprice_index = obvm.columns.indexOf(existing_editprice_col);
|
||||
if (existing_editprice_index == -1) {
|
||||
// insert the edit price column at the beginning
|
||||
obvm.columns.Insert(0, new OrderBookVM.EditablePriceColumn());
|
||||
}
|
||||
}
|
||||
var qty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookGridView.ColumnKeys.Size.ToString());
|
||||
var qty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.Size.ToString());
|
||||
var qty_index = obvm.columns.indexOf(qty_col);
|
||||
if (qty_index > -1) {
|
||||
var existing_editqty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookGridView.ColumnKeys.EditableQty.ToString());
|
||||
var existing_editqty_col = obvm.columns.FirstOrDefault(x => x.id == OrderBookView.Columns.EditableQty.ToString());
|
||||
var existing_editqty_index = obvm.columns.indexOf(existing_editqty_col);
|
||||
if (existing_editqty_index == -1) {
|
||||
// insert the edit quantity column at the beginning
|
||||
@@ -279,7 +280,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"Price: {this.price} Order Count: {this.orders.Count()}";
|
||||
return $"Price: {this.price} Order Count: {this.orders.Count()} PIQ: {(this.min_piq.HasValue ? this.min_piq.Value.ToString() : "")}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 367 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user