Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cc738c1ba | ||
|
|
e0a2aad2f7 | ||
|
|
2b5c2b9f82 | ||
|
|
2a9bbbd182 | ||
|
|
e4d649b245 | ||
|
|
f5b114b9ce | ||
|
|
0a7ef4a4cd | ||
|
|
6cc1b38358 | ||
|
|
0f3169ef05 | ||
|
|
f72753cd77 | ||
|
|
2240b3a85d | ||
|
|
e792f3a697 | ||
|
|
85581ebf41 | ||
|
|
ea180d8ebc | ||
|
|
1f7f091a51 | ||
|
|
78be34cdd3 | ||
|
|
b59158ea43 | ||
|
|
9937fc7cd2 | ||
|
|
f3f53d05c8 | ||
|
|
b49b7f01d5 | ||
|
|
a699865342 | ||
|
|
1b5f58c572 | ||
|
|
bae0b797e9 | ||
|
|
79a69e29a0 | ||
|
|
38bf38c62d | ||
|
|
4e08a43d0c | ||
|
|
4f912f99bd | ||
|
|
ae7e1761a7 | ||
|
|
4891eea5ba | ||
|
|
bc36db0480 | ||
|
|
cdaba74c06 | ||
|
|
b045307f30 | ||
|
|
60a7b504db | ||
|
|
0f62f14a1d | ||
|
|
87030dbb88 | ||
|
|
e209ec59e4 | ||
|
|
92dbedf28f | ||
|
|
7993f75fb1 | ||
|
|
1cddd2bcf3 | ||
|
|
9f7e5846b8 | ||
|
|
f7b3aac11a | ||
|
|
ec32101cef | ||
|
|
4e0cf68fa3 | ||
|
|
4e9a7d576e | ||
|
|
5e056dfff4 | ||
|
|
4029ccf8e6 | ||
|
|
22911d582b | ||
|
|
6ef7a2ccb5 | ||
|
|
737a07cecb | ||
|
|
1cbea3d1ee | ||
|
|
4dbedfa1f7 | ||
|
|
074f87dd37 | ||
|
|
6ee0ca9589 | ||
|
|
5745c13355 | ||
|
|
5f99edde0e | ||
|
|
d39bc5c73c | ||
|
|
9cb6d5ab88 | ||
|
|
278432a35b | ||
|
|
13c414f301 | ||
|
|
34f5265a42 | ||
|
|
83103e6dc4 | ||
|
|
add631b04d | ||
|
|
e08dc5dfba | ||
|
|
6a8e995c9c | ||
|
|
d7c460ac98 | ||
|
|
7871dc5e4f | ||
|
|
1aab1db4ca | ||
|
|
373187c091 | ||
|
|
423d127ee9 | ||
|
|
14f306d50d | ||
|
|
71fe863f1b | ||
|
|
ac47f0246f | ||
|
|
8e43f96331 | ||
|
|
3187e4134f | ||
|
|
0331501ce4 | ||
|
|
bd0f1a6f84 | ||
|
|
8776b80b07 | ||
|
|
f5a72841b4 | ||
|
|
f9ada7fca2 | ||
|
|
1cb4f24610 | ||
|
|
021c88f26b | ||
|
|
014fa8e291 | ||
|
|
71d54224bb | ||
|
|
bebbf2a0f1 | ||
|
|
7413814e4a | ||
|
|
36474ef3ea | ||
|
|
974be037fe | ||
|
|
a9ee246436 | ||
|
|
b692742e30 | ||
|
|
c61cbba316 | ||
|
|
4d99ffc86c | ||
|
|
52001a55c6 | ||
|
|
570dd387d2 | ||
|
|
28a122aa7a | ||
|
|
b2facaecd9 | ||
|
|
e346858516 | ||
|
|
be8ca67be5 | ||
|
|
197e68e357 | ||
|
|
dfa3e4a38d | ||
|
|
0f7625ff4a | ||
|
|
88ab4fbb79 | ||
|
|
5cc9b8c875 | ||
|
|
187793d47c | ||
|
|
656c7104f6 | ||
|
|
359e01e8bd | ||
|
|
2a452d3201 | ||
|
|
78cefa187a | ||
|
|
8531b330dc | ||
|
|
54d9bde6b7 | ||
|
|
f0b089d2e5 | ||
|
|
8979c595a5 | ||
|
|
ba4771aeee | ||
|
|
719c1e474b | ||
|
|
72123a61a9 | ||
|
|
3b1c2dc52b | ||
|
|
05f1be77ef | ||
|
|
79e876e1dc | ||
|
|
b6cc413733 | ||
|
|
a5e9f40f01 | ||
|
|
e40dc48fa8 | ||
|
|
5aab4312fc | ||
|
|
9ede4c7736 | ||
|
|
b63b0b6303 | ||
|
|
e004580d99 | ||
|
|
7403057c58 | ||
|
|
47730bd3d4 | ||
|
|
9964d2579a | ||
|
|
6960ee1a12 | ||
|
|
9edde6b8e3 | ||
|
|
8ef02ea4cb | ||
|
|
dc99ea6ea4 | ||
|
|
9c6993d70c | ||
|
|
c6be4dad17 | ||
|
|
bd2e2674bf | ||
|
|
71ddcd135b | ||
|
|
bffb7686a8 | ||
|
|
a73bf0ce7c | ||
|
|
94c5a1bcd1 | ||
|
|
ca2a346054 | ||
|
|
b990923cf5 | ||
|
|
8490151844 | ||
|
|
78c8234eef | ||
|
|
1f1f92d337 | ||
|
|
a6426e7a98 | ||
|
|
dc1a7b3f5d | ||
|
|
2a549e551f | ||
|
|
c158188557 | ||
|
|
17614b663e | ||
|
|
5e36a22aba | ||
|
|
442fbd2888 | ||
|
|
d1975cdd00 | ||
|
|
a2c75f08d8 | ||
|
|
02a867f8a4 | ||
|
|
adc48d7871 | ||
|
|
33bd089e74 | ||
|
|
edcb5a5a24 | ||
|
|
6f39705d4f | ||
|
|
07377b8cc9 | ||
|
|
77ad9ca011 | ||
|
|
10f24aa2e9 | ||
|
|
37fc5880ea | ||
|
|
910d991d92 | ||
|
|
ced8bd9fea | ||
|
|
c5004f46ee | ||
|
|
1deb5d25b0 | ||
|
|
b5950ab8c0 | ||
|
|
a233451333 | ||
|
|
deef8136df | ||
|
|
9dda440a48 | ||
|
|
f22442e665 | ||
|
|
1ba7ceaf6a | ||
|
|
54676d5aa9 | ||
|
|
edf5c78405 | ||
|
|
f621d08f72 | ||
|
|
724243c762 | ||
|
|
c97e4be79e | ||
|
|
95be0015d6 | ||
|
|
cbe8917f28 | ||
|
|
653b1c7814 | ||
|
|
9a1098669e | ||
|
|
dcf6c69ef9 | ||
|
|
fdcd245ef5 | ||
|
|
cf9133da8d | ||
|
|
802501a337 | ||
|
|
b9c6f8d309 | ||
|
|
ee94f43628 | ||
|
|
e0add4cc47 | ||
|
|
cd670c4bd1 | ||
|
|
acd637000c | ||
|
|
c09f03a922 | ||
|
|
f9fbf9c45a | ||
|
|
846f6d80d8 | ||
|
|
9819a01e38 | ||
|
|
d0ad69e259 | ||
|
|
f0abd13c48 | ||
|
|
ea403b7138 | ||
|
|
4a94beaa83 | ||
|
|
3f03ceb2b5 | ||
|
|
0dd988fd1e | ||
|
|
93d0a21c9d | ||
|
|
92b3cd1da0 | ||
|
|
8fc42f6193 | ||
|
|
73d5b44a7f | ||
|
|
e47c4ead2a | ||
|
|
21dfe5ce43 | ||
|
|
e032dc822f | ||
|
|
87148b41f0 | ||
|
|
68cb21a0a9 | ||
|
|
093ce9a0c3 | ||
|
|
5003b23f18 | ||
|
|
0de0ddbf16 | ||
|
|
5d79c0c79a | ||
|
|
8e4d4782ce | ||
|
|
b8bd3944ce | ||
|
|
1fc38234e5 | ||
|
|
625d47d98e | ||
|
|
8c8f2d3179 | ||
|
|
669ccd5ffc | ||
|
|
12db326f1d | ||
|
|
55fc880b2e | ||
|
|
30ca30442a | ||
|
|
802160abf1 | ||
|
|
3da1bfaeb1 | ||
|
|
03eff1f388 | ||
|
|
b53825baa9 | ||
|
|
0065dba0f0 | ||
|
|
078b9d41b1 | ||
|
|
564cd3e273 | ||
|
|
709229ac14 | ||
|
|
375786b283 | ||
|
|
9e5970c93e | ||
|
|
ab5fca73f6 | ||
|
|
840115db03 | ||
|
|
db7f74f85d | ||
|
|
3f381f248b | ||
|
|
b5a93e4466 | ||
|
|
1b56ff3327 | ||
|
|
1f823003d7 | ||
|
|
a64749cd07 | ||
|
|
3ac6e882d4 | ||
|
|
4c239f31f8 | ||
|
|
f9759b9533 | ||
|
|
56fc1019de | ||
|
|
abe7c25bf4 | ||
|
|
4dcaf224ef | ||
|
|
8c492fdad3 | ||
|
|
b811fb6a32 | ||
|
|
0f78cbc80e | ||
|
|
eb3067448d | ||
|
|
f4a3256cb8 | ||
|
|
b7a3d5a23c | ||
|
|
4f4bfea70f | ||
|
|
76d6e37f59 | ||
|
|
8e535b0303 | ||
|
|
44887d80e6 | ||
|
|
bd63c0798e | ||
|
|
6e06b3566a | ||
|
|
ebc7432995 | ||
|
|
16e9d100d2 | ||
|
|
5009c4c29a | ||
|
|
82d3e59024 | ||
|
|
6fe59386df | ||
|
|
767cb36856 | ||
|
|
8cef60b5ab | ||
|
|
bbf961ed78 | ||
|
|
f3b8ce8088 | ||
|
|
026bda165e | ||
|
|
9727f17edf | ||
|
|
61b2913a31 |
No files matched your search
Vendored
+1
-1
@@ -37,7 +37,7 @@
|
||||
"type": "clr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/Debug/bin/net471/spark_sdk.exe",
|
||||
"args": ["md","GLBX","Future","ES","ESM8"],
|
||||
"args": ["md","GLBX","Future","ZN", "ZNU8"],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"externalConsole": true,
|
||||
|
||||
+26
-6
@@ -32,10 +32,30 @@ if (WIN32)
|
||||
|
||||
add_subdirectory(src/lib)
|
||||
else ()
|
||||
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \
|
||||
-Wall -pedantic -Wextra -Werror -Wno-missing-braces -Wno-ignored-qualifiers --std=c++14 -fdiagnostics-color")
|
||||
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG -O3")
|
||||
add_subdirectory(src/lib)
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/web)
|
||||
# so that sparkd looks for libspark.so in its directory
|
||||
set (CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
set (CMAKE_INSTALL_RPATH ".")
|
||||
|
||||
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \
|
||||
-fPIC -Wall -pedantic -Wextra -Werror \
|
||||
-Wno-missing-braces -Wno-ignored-qualifiers \
|
||||
--std=c++14 -fdiagnostics-color")
|
||||
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG -O3")
|
||||
add_subdirectory(src/server/sparkd)
|
||||
add_subdirectory(src/lib)
|
||||
add_subdirectory(src/web)
|
||||
add_subdirectory(src/sdk)
|
||||
add_subdirectory(src/data_files)
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/sparkd ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/defaults ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/web ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
|
||||
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
|
||||
COMMENT "Creating server runtime layout"
|
||||
DEPENDS sparkd data_files_dir)
|
||||
|
||||
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
|
||||
endif()
|
||||
+1
-1
Submodule docker/linux updated: 6037d094b7...aedfd79e02.
+1
-1
Submodule externals updated: 281bd00a2d...83046b27ef.
@@ -0,0 +1,6 @@
|
||||
1.1.0
|
||||
- With this release we dropped wild cards from limits, so any user
|
||||
that has limits expressed in terms of wild cards will have to have
|
||||
those re-entered explicitly
|
||||
- With this release we add per user location and smp id tags. At least
|
||||
the location will have to be set through the admin UI for every user
|
||||
@@ -0,0 +1,35 @@
|
||||
1.1.22
|
||||
- Orderbook: Orders in Error status can now be recreated
|
||||
- Orderbook: Orders containing child orders will now be collapsed by default.
|
||||
- Orderbook: Expand / Collapse All button added to the right-click menu and window action pane, denoted by ▲ icon
|
||||
- Orderbook: Symbol column added to allow for filtering based on group symbol
|
||||
- Time filters now have a quick Set Last Hour hyperlink which will set the Period Begin filter to (Now - 1 hour)
|
||||
|
||||
1.1.20
|
||||
- Update CME market data handler for price precision extension.
|
||||
https://www.cmegroup.com/confluence/display/EPICSANDBOX/Price+Precision+Extension
|
||||
|
||||
1.1.8
|
||||
- Enabled wild cards in risk management UI
|
||||
- Fixed spreader order updates
|
||||
|
||||
1.1.7
|
||||
- Fixed spread leg price fill
|
||||
- Fixed HO cash per point value
|
||||
|
||||
1.1.6
|
||||
- Fixed occasional crash when filtering fills
|
||||
- Fixed several cash per point multipliers
|
||||
- Colored time of sales row with aggressor side
|
||||
- Iceberg orders now cancel (rathern than error) when a child is canceled
|
||||
|
||||
1.1.0
|
||||
- Added per user self match protection, location and self match protection FIX tag settings
|
||||
- Implemented a new "Trigger Order" algo
|
||||
- Corrected tick formatting for a number of products
|
||||
- Added the hability to recreate canceled orders
|
||||
- Added leg fills to facilitate futured based PnL
|
||||
- PnL report now has PnL columns calculated based on your start of day, current day, and total position
|
||||
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
|
||||
- Corrected cash per point values, used in pnl calculations, for a number of products
|
||||
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
|
||||
+1
-1
@@ -4,7 +4,7 @@ VisualStudioVersion = 15.0.27130.2024
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spark_lib", "build\src\lib\spark_lib.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
|
||||
EndProject
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ VisualStudioVersion = 15.0.26730.10
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sparkd", "src\server\sparkd\sparkd.vcxproj", "{BBBAFD38-312D-4A8D-9AF0-F2BDEE72CE6F}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spark_lib", "build\src\lib\spark_lib.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{502FD050-679A-3B50-B438-098500700919}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace bts {
|
||||
_deleted_synthetics = new HashSet<uint>(t.Where(kv => kv.Value.Deleted).Select(kv => kv.Key));
|
||||
_bus.post(SparkTags.SyntheticstrategiesTag, t);
|
||||
}));
|
||||
new DataPersistor(_bus);
|
||||
Task.Run(run);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using bts.api;
|
||||
using mbts.spark;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace mbts.ui {
|
||||
[Export(typeof(IShellDaemon))]
|
||||
class RecenterDaemon : IShellDaemon, IDisposable {
|
||||
bool _run = true;
|
||||
readonly IBus _bus;
|
||||
readonly TimeSpan _time_to_center;
|
||||
DateTime _last_recenter;
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
public RecenterDaemon(IBus bus) {
|
||||
_bus = bus;
|
||||
_time_to_center = TimeSpan.FromSeconds(config.Configuration.get<bts.config.LadderKnobs>().seconds_to_recenter);
|
||||
_last_recenter = DateTime.UtcNow;
|
||||
|
||||
_dispose = _bus.subscribe(SparkTags.RequestRecenterLadder).Subscribe(_ => trigger_recenter(SparkTags.RecenterLadderNow));
|
||||
run();
|
||||
}
|
||||
|
||||
void trigger_recenter(ITag<DateTime> tag) {
|
||||
_last_recenter = DateTime.UtcNow;
|
||||
_bus.post(tag, _last_recenter);
|
||||
}
|
||||
|
||||
async void run() {
|
||||
_run = true;
|
||||
while (_run) {
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
if (_last_recenter + _time_to_center < DateTime.UtcNow) {
|
||||
trigger_recenter(SparkTags.RecenterLadderBegin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using bts.api.core;
|
||||
using bts.metrics;
|
||||
using mbts.config;
|
||||
using mbts.ui;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace bts.perf {
|
||||
|
||||
[Export, Export(typeof(IShellDaemon))]
|
||||
class ShellLatencyDaemon : IShellDaemon, IDisposable {
|
||||
readonly ShellLatencyService _service;
|
||||
readonly string _hostname;
|
||||
readonly string _version;
|
||||
IDisposable _subs;
|
||||
|
||||
[ImportingConstructor]
|
||||
public ShellLatencyDaemon(ShellLatencyService service) {
|
||||
_service = service;
|
||||
_version = Configuration.get<bts.config.System>().version;
|
||||
_hostname = Environment.MachineName;
|
||||
_subs = _service.subscribei_clock().Subscribe(on_tick);
|
||||
}
|
||||
|
||||
void on_tick(Update<TickImmutable, int> update) {
|
||||
if (update.added.Count > 0) {
|
||||
var tick = update.added.Last();
|
||||
_service.report_async(new Report() {
|
||||
HostName = _hostname,
|
||||
Version = _version,
|
||||
Timestamp = tick.Timestamp
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using bts.api.orderman;
|
||||
using bts.api.synthetic;
|
||||
using bts.api.diagnostic;
|
||||
using bts.api.admin;
|
||||
using bts.metrics;
|
||||
|
||||
namespace Spark
|
||||
{
|
||||
@@ -41,5 +42,9 @@ namespace Spark
|
||||
[Export]
|
||||
public SyntheticSecurityService synthetic_security_service => context.locate<SyntheticSecurityService>();
|
||||
|
||||
}
|
||||
[Export]
|
||||
public ShellLatencyService shell_latency_service => context.locate<ShellLatencyService>();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,7 @@ namespace mbts.ui
|
||||
public static ImageSource Tetris { get { return (DrawingImage)_resources["Tetris"]; } }
|
||||
public static ImageSource ViewSettings => (DrawingImage)_resources["ViewSettings"];
|
||||
public static ImageSource Wrench { get { return (DrawingImage)_resources["Wrench"]; } }
|
||||
public static ImageSource UpArrow { get { return (DrawingImage)_resources["UpArrow"]; } }
|
||||
|
||||
public static ImageSource Support => (DrawingImage)_resources["SupportIcon"];
|
||||
}
|
||||
|
||||
@@ -753,6 +753,19 @@ M0,0L63.999001,0 63.999001,48.792999 0,48.792999z">
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="UpArrow" x:Shared="False">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<GeometryDrawing
|
||||
Brush="Black"
|
||||
Geometry="M16,0L32,19.745 0,19.745z"
|
||||
/>
|
||||
</DrawingGroup.Children>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<!-- Pin
|
||||
<Path Data="F1M3,11L3,7 0,7 0,6 1,6 1,1 1,0 6,0 6,1 6,6 7,6 7,7 4,7 4,11 3,11z M2,1L2,6 4,6 4,1 2,1z" />
|
||||
Unpin
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace mbts.spark {
|
||||
})
|
||||
),
|
||||
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
|
||||
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count < 3, secs => new SecurityCriteria(secs))
|
||||
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs))
|
||||
};
|
||||
_subscriptions = _bus.subscribe(update_securities_tag).OnDispatcher().Subscribe(c =>
|
||||
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states())));
|
||||
@@ -67,4 +67,4 @@ namespace mbts.spark {
|
||||
Commands.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,14 @@ using mbts.ui.controls;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using static bts.shell.TimeAndSalesWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
|
||||
namespace mbts.spark {
|
||||
class TimeAndSalesVM : SecurityGrid<TimeAndSalesVM.Row, TradeInfo> {
|
||||
@@ -26,23 +29,32 @@ namespace mbts.spark {
|
||||
security_filter.load_state(filters.security);
|
||||
price_filter.load_state(filters.price);
|
||||
size_filter.load_state(filters.size);
|
||||
aggressor_filter.load_state(filters.aggressor_sides);
|
||||
trade_key_filter.load_state(filters.trade_key);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
// Columns
|
||||
add_column(TimeAndSalesView.Columns.Security, "Security", DIYGrid.DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.Time, "Time", DIYGrid.DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter);
|
||||
add_column(TimeAndSalesView.Columns.Price, "Price", DIYGrid.DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
|
||||
add_column(TimeAndSalesView.Columns.Size, "Size", DIYGrid.DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax);
|
||||
add_column(TimeAndSalesView.Columns.TradeKey, "Trade Key", DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.SecurityKey, "Security Key", DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template) =>
|
||||
columns.Add(new DIYGrid.RenderTextColumn<Row>(r => r[col_key], filter, filter_template) { Header = header, Width = width });
|
||||
//
|
||||
add_column(TimeAndSalesView.Columns.Security, DefaultColumnHeaders.Security, DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.Time, DefaultColumnHeaders.Time, DefaultColumnWidths.Time, time_filter, DataGridFilterKnownTemplates.TimeFilter);
|
||||
add_column(TimeAndSalesView.Columns.Price, DefaultColumnHeaders.Price, DefaultColumnWidths.Price, price_filter, DataGridFilterKnownTemplates.DoubleMinMax, System.Windows.HorizontalAlignment.Right);
|
||||
add_column(TimeAndSalesView.Columns.Size, DefaultColumnHeaders.Size, DefaultColumnWidths.Size, size_filter, DataGridFilterKnownTemplates.UIntMinMax, System.Windows.HorizontalAlignment.Right);
|
||||
add_column(TimeAndSalesView.Columns.Aggressor, DefaultColumnHeaders.Aggressor, DIYGrid.DefaultColumnWidths.Aggressor, aggressor_filter, DataGridFilterKnownTemplates.EnumValues);
|
||||
add_column(TimeAndSalesView.Columns.TradeKey, DefaultColumnHeaders.TradeKey, DIYGrid.DefaultColumnWidths.TradeKey, trade_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
add_column(TimeAndSalesView.Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, DIYGrid.DefaultColumnWidths.SecurityKey, security_key_filter, DataGridFilterKnownTemplates.Regex);
|
||||
void add_column(TimeAndSalesView.Columns col_key, string header, double width, IDataGridColumnFilter filter, string filter_template, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
columns.Add(new TimeAndSalesColumn(
|
||||
r => r[col_key]
|
||||
, (r) => _get_aggressor_brush(r)
|
||||
, filter
|
||||
, filter_template
|
||||
)
|
||||
{ Header = header, Width = width, horizontal_alignment = h_alignment }
|
||||
);
|
||||
|
||||
_resources.AddRange(
|
||||
show_ladder,
|
||||
DataGridFilterHelper.hook_filters(
|
||||
rows_view, time_filter, security_filter, price_filter, size_filter, trade_key_filter, security_key_filter
|
||||
rows_view, time_filter, security_filter, price_filter, size_filter, aggressor_filter, trade_key_filter, security_key_filter
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -50,11 +62,22 @@ namespace mbts.spark {
|
||||
public ObservableCollection<DIYGrid.ColumnBase> columns { get; } = new ObservableCollection<DIYGrid.ColumnBase>();
|
||||
public DelegateCommandParamObserver<Row> show_ladder { get; }
|
||||
|
||||
private Brush _get_aggressor_brush(Row r) {
|
||||
BTSColor c;
|
||||
Brush b;
|
||||
if (r.aggressor_side == AggressorSide.Buy) c = BTSColors.MarketData.bid;
|
||||
else if (r.aggressor_side == AggressorSide.Sell) c = BTSColors.MarketData.ask;
|
||||
else c = Brushes.Transparent;
|
||||
(b = new SolidColorBrush(c) { Opacity = 0.33 }).Freeze();
|
||||
return b;
|
||||
}
|
||||
|
||||
#region Filters
|
||||
public IDataGridColumnPersistentFilter<Row, TimeFilterTuple> time_filter { get; } = new TimeFilterVM<Row>(r => r.time, nameof(Row.time));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<Row>(r => r.price, nameof(Row.price));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> size_filter { get; } = new UIntMinMax<Row>(r => r.size, nameof(Row.size));
|
||||
public IDataGridColumnPersistentFilter<Row, AggressorSide[]> aggressor_filter { get; } = DiscreteValuesFilterVM.Create<Row, AggressorSide>(EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side, nameof(Row.aggressor_side));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
public IDataGridColumnPersistentFilter<Row, string> trade_key_filter { get; } = new RegexFilterVM<Row>(r => r.trade_key.ToString(), nameof(Row.trade_key));
|
||||
|
||||
@@ -65,7 +88,8 @@ namespace mbts.spark {
|
||||
price = price_filter.get_state(),
|
||||
size = size_filter.get_state(),
|
||||
trade_key = trade_key_filter.get_state(),
|
||||
security_key = security_key_filter.get_state()
|
||||
security_key = security_key_filter.get_state(),
|
||||
aggressor_sides = aggressor_filter.get_state()
|
||||
};
|
||||
#endregion
|
||||
|
||||
@@ -90,6 +114,7 @@ namespace mbts.spark {
|
||||
_time_text,
|
||||
_price_text,
|
||||
_size_text,
|
||||
_aggressor_text,
|
||||
_trade_key_text,
|
||||
_security_key_text;
|
||||
|
||||
@@ -98,11 +123,13 @@ namespace mbts.spark {
|
||||
time = trade.Time.lldatetime();
|
||||
price = trade.Mantissa * Math.Pow(10, trade.Exponent);
|
||||
size = trade.Size;
|
||||
aggressor_side = trade.AggressorSide;
|
||||
show_ladder = new DelegateCommand(() => parent._show_ladder_async(this).ignore());
|
||||
_security_text = security.ToString();
|
||||
_time_text = time.ToString("M/d h:mm:ss tt");
|
||||
_price_text = security.ContractSpec.format(price);
|
||||
_size_text = size.ToString();
|
||||
_aggressor_text = aggressor_side.ToString();
|
||||
_trade_key_text = _trade_key.ToString();
|
||||
_security_key_text = security_key.ToString();
|
||||
}
|
||||
@@ -113,6 +140,7 @@ namespace mbts.spark {
|
||||
public DateTime time { get; }
|
||||
public double price { get; }
|
||||
public uint size { get; }
|
||||
public AggressorSide aggressor_side { get; }
|
||||
public ICommand show_ladder { get; }
|
||||
|
||||
public string this[TimeAndSalesView.Columns col] {
|
||||
@@ -122,6 +150,7 @@ namespace mbts.spark {
|
||||
case TimeAndSalesView.Columns.Time: return _time_text;
|
||||
case TimeAndSalesView.Columns.Price: return _price_text;
|
||||
case TimeAndSalesView.Columns.Size: return _size_text;
|
||||
case TimeAndSalesView.Columns.Aggressor: return _aggressor_text;
|
||||
case TimeAndSalesView.Columns.TradeKey: return _trade_key_text;
|
||||
case TimeAndSalesView.Columns.SecurityKey: return _security_key_text;
|
||||
default: return null;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for TimeAndSalesView.xaml
|
||||
/// </summary>
|
||||
partial class TimeAndSalesView : UserControl {
|
||||
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey }
|
||||
public enum Columns { Time, Security, Price, Size, OrderType, TradeKey, SecurityKey, Aggressor }
|
||||
|
||||
public TimeAndSalesView() =>
|
||||
InitializeComponent();
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using static bts.shell.TimeAndSalesWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
|
||||
namespace mbts.spark {
|
||||
class TimeAndSalesWindow : SparkWindow<StartupData>, ISlowWindow {
|
||||
@@ -36,7 +37,16 @@ namespace mbts.spark {
|
||||
public override string window_type => _window_type;
|
||||
public override IEnumerable<SparkCommand> Commands => _commands;
|
||||
public override UIElement Content => _view;
|
||||
public override double MinWidth => 444 + SPARK_MENU_COLLAPSED_WIDTH + LAUNCHER_BUTTON_BORDER_WIDTH;
|
||||
public override double MinWidth => DefaultColumnWidths.Security
|
||||
+ DefaultColumnWidths.Time
|
||||
+ DefaultColumnWidths.Price
|
||||
+ DefaultColumnWidths.Size
|
||||
+ DefaultColumnWidths.Aggressor
|
||||
+ DefaultColumnWidths.TradeKey
|
||||
+ DefaultColumnWidths.SecurityKey
|
||||
+ SPARK_MENU_COLLAPSED_WIDTH
|
||||
+ LAUNCHER_BUTTON_BORDER_WIDTH;
|
||||
|
||||
public override StartupData SaveState => _state = new StartupData(_state.security_criteria, _vm.value.rows_view.GetSortingCriteria(), _vm.value.get_filter_states());
|
||||
|
||||
public override void Dispose() =>
|
||||
|
||||
@@ -70,6 +70,8 @@ namespace bts.shell {
|
||||
public string trade_key;
|
||||
[ProtoMember(6)]
|
||||
public string security_key;
|
||||
[ProtoMember(7)]
|
||||
public co2.AggressorSide[] aggressor_sides;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,9 +72,9 @@
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.security_key_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_key_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_key_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
|
||||
/>
|
||||
</DataGrid.Columns>
|
||||
</c:DataGridEx>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -53,6 +53,8 @@ namespace bts.shell {
|
||||
public IntMinMaxState n_legs;
|
||||
[ProtoMember(5)]
|
||||
public UIntMinMaxState n_working_orders;
|
||||
[ProtoMember(6)]
|
||||
public string security_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,4 +335,4 @@ namespace mbts.spark {
|
||||
else clear_error(nameof(leg_count));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,9 @@ namespace mbts.spark {
|
||||
description_filter.load_state(filters.description);
|
||||
n_legs_filter.load_state(filters.n_legs);
|
||||
n_working_orders_filter.load_state(filters.n_working_orders);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
_dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter);
|
||||
_dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter);
|
||||
}
|
||||
|
||||
protected override Row row_factory((ExecutionStrategyImmutable? strategy, SecurityMD md) row_model) =>
|
||||
@@ -41,6 +42,7 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<Row, string> description_filter { get; } = new RegexFilterVM<Row>(r => r.description, nameof(Row.description));
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> n_legs_filter { get; } = new IntMinMax<Row>(r => r.n_legs, nameof(Row.n_legs));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> n_working_orders_filter { get; } = new UIntMinMax<Row>(r => r.n_working_orders, nameof(Row.n_legs));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
|
||||
public FilterStates get_filter_states() =>
|
||||
new FilterStates {
|
||||
@@ -48,7 +50,8 @@ namespace mbts.spark {
|
||||
create_time = create_time_filter.get_state(),
|
||||
name = name_filter.get_state(),
|
||||
n_legs = n_legs_filter.get_state(),
|
||||
n_working_orders = n_working_orders_filter.get_state()
|
||||
n_working_orders = n_working_orders_filter.get_state(),
|
||||
security_key = security_key_filter.get_state()
|
||||
};
|
||||
#endregion
|
||||
|
||||
@@ -104,4 +107,4 @@ namespace mbts.spark {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Data;
|
||||
using static bts.api.orderman.Fill.Types;
|
||||
using static bts.shell.FillsWindowFactory;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
using static mbts.ui.spark.FillsView;
|
||||
@@ -52,6 +53,7 @@ namespace mbts.spark {
|
||||
side_filter.load_state(filters.side);
|
||||
price_filter.load_state(filters.price);
|
||||
size_filter.load_state(filters.size);
|
||||
fill_kind_filter.load_state(filters.fill_kind);
|
||||
fill_key_filter.load_state(filters.fill_key);
|
||||
order_key_filter.load_state(filters.order_key);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
@@ -74,6 +76,7 @@ namespace mbts.spark {
|
||||
columns.Add(new SideColumn<Row, Columns>(Columns.Side, r => r.side, side_filter) { sort_member_path = nameof(Row.side) });
|
||||
add_col(Columns.Size, DefaultColumnHeaders.Size, r => r.size.ToString(), nameof(Row.size), size_filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.Size);
|
||||
add_col(Columns.Price, DefaultColumnHeaders.Price, r => r.format_price(r.price), nameof(Row.price), price_filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.Price);
|
||||
add_col(Columns.FillKind, "Fill Kind", r => r.fill_kind.ToString(), nameof(Row.fill_kind), fill_kind_filter, DataGridFilterKnownTemplates.DiscreteValues, 70);
|
||||
add_col(Columns.FillKey, DefaultColumnHeaders.FillKey, r => r.fill_key.ToString(), nameof(Row.fill_key), fill_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.FillKey);
|
||||
add_col(Columns.OrderKey, DefaultColumnHeaders.OrderKey, r => r.order_key.ToString(), nameof(Row.order_key), order_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.OrderKey);
|
||||
add_col(Columns.SecurityKey, DefaultColumnHeaders.SecurityKey, r => r.security_key.ToString(), nameof(Row.security_key), security_key_filter, DataGridFilterKnownTemplates.Regex, DefaultColumnWidths.SecurityKey);
|
||||
@@ -87,7 +90,7 @@ namespace mbts.spark {
|
||||
//
|
||||
_resources.Add(DataGridFilterHelper.hook_filters(rows_view,
|
||||
fill_time_filter, time_in_force_filter, order_type_filter, account_filter, user_filter, exchange_filter,
|
||||
security_filter, side_filter, price_filter, size_filter, fill_key_filter, order_key_filter, security_key_filter
|
||||
security_filter, side_filter, price_filter, size_filter, fill_kind_filter, fill_key_filter, order_key_filter, security_key_filter
|
||||
));
|
||||
}
|
||||
|
||||
@@ -104,6 +107,7 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<Row, Side[]> side_filter { get; } = DiscreteValuesFilterVM.Create<Row, Side>(new[] { Side.Buy, Side.Sell }, r => r.side, nameof(Row.side));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<Row>(r => r.price, nameof(Row.price));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> size_filter { get; } = new DoubleMinMax<Row>(r => r.size, nameof(Row.size));
|
||||
public IDataGridColumnPersistentFilter<Row, FillKind[]> fill_kind_filter { get; } = DiscreteValuesFilterVM.Create<Row, FillKind>(EnumHelper.get_values<FillKind>().ToArray(), r => r.fill_kind, nameof(Row.fill_kind));
|
||||
public IDataGridColumnPersistentFilter<Row, string> fill_key_filter { get; } = new RegexFilterVM<Row>(r => r.fill_key, nameof(Row.fill_key));
|
||||
public IDataGridColumnPersistentFilter<Row, string> order_key_filter { get; } = new RegexFilterVM<Row>(r => r.order_key.ToString(), nameof(Row.order_key));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
@@ -120,6 +124,7 @@ namespace mbts.spark {
|
||||
side = side_filter.get_state(),
|
||||
price = price_filter.get_state(),
|
||||
size = size_filter.get_state(),
|
||||
fill_kind = fill_kind_filter.get_state(),
|
||||
fill_key = fill_key_filter.get_state(),
|
||||
order_key = order_key_filter.get_state(),
|
||||
security_key = security_key_filter.get_state()
|
||||
@@ -157,6 +162,7 @@ namespace mbts.spark {
|
||||
public Side side => _fill.Side;
|
||||
public double price => _fill.LastPrice;
|
||||
public double size => _fill.LastShares;
|
||||
public FillKind fill_kind => _fill.FillKind;
|
||||
public string fill_key => _fill.FillKey;
|
||||
public string order_key => _order.Key.ToString();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for FillsView.xaml
|
||||
/// </summary>
|
||||
partial class FillsView : UserControl {
|
||||
public enum Columns { FillTime, User, Account, Exchange, Security, TimeInForce, OrderType, Side, Price, Size, FillKey, OrderKey, SecurityKey, ExchangeOrderID }
|
||||
public enum Columns { FillTime, User, Account, Exchange, Security, TimeInForce, OrderType, Side, Price, Size, FillKind, FillKey, OrderKey, SecurityKey, ExchangeOrderID }
|
||||
|
||||
public FillsView() =>
|
||||
InitializeComponent();
|
||||
|
||||
@@ -74,6 +74,8 @@ namespace bts.shell {
|
||||
public string order_key;
|
||||
[ProtoMember(13)]
|
||||
public string security_key;
|
||||
[ProtoMember(14)]
|
||||
public Fill.Types.FillKind[] fill_kind;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,10 @@ namespace bts.shell {
|
||||
public TimeFilterTuple last_change;
|
||||
[ProtoMember(18)]
|
||||
public string security_regex;
|
||||
[ProtoMember(19)]
|
||||
public api.orderman.OrderSubType[] order_sub_types;
|
||||
[ProtoMember(20)]
|
||||
public string[] symbols;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.spark;
|
||||
using ProtoBuf;
|
||||
@@ -20,7 +22,6 @@ namespace bts.shell {
|
||||
_dispose = bus.subscribe(SparkTags.ShowPAndLTag).OnDispatcher().Subscribe(show);
|
||||
}
|
||||
|
||||
|
||||
public override Task<IShellWindow> load(PAndLStartupData state) {
|
||||
var w = new PAndLWindow(_model, this, _bus, state, SparkCommands.ShowPAndL.icon) as IShellWindow;
|
||||
return Task.FromResult(w);
|
||||
@@ -33,6 +34,61 @@ namespace bts.shell {
|
||||
|
||||
[ProtoContract]
|
||||
public class PAndLStartupData {
|
||||
|
||||
/// <summary>
|
||||
/// Parameterless constructor for protobuf
|
||||
/// </summary>
|
||||
public PAndLStartupData() { }
|
||||
|
||||
public PAndLStartupData(List<SortingCriteria> sorting_criteria, FilterStates filters, pnl_calc_type calc_type, TimeSpan start_of_day_time) {
|
||||
this.sort_criteria = sorting_criteria;
|
||||
this.filters = filters;
|
||||
this.calc_type = calc_type;
|
||||
this.start_of_day_time = start_of_day_time;
|
||||
}
|
||||
|
||||
[ProtoMember(5)]
|
||||
public readonly List<SortingCriteria> sort_criteria;
|
||||
[ProtoMember(10)]
|
||||
public FilterStates filters;
|
||||
[ProtoMember(15)]
|
||||
public pnl_calc_type calc_type;
|
||||
[ProtoMember(20)]
|
||||
public TimeSpan? start_of_day_time;
|
||||
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class FilterStates {
|
||||
[ProtoMember(1)]
|
||||
public string account_regex;
|
||||
[ProtoMember(2)]
|
||||
public string exchange_regex;
|
||||
[ProtoMember(3)]
|
||||
public string symbol_regex;
|
||||
[ProtoMember(4)]
|
||||
public string security_regex;
|
||||
[ProtoMember(5)]
|
||||
public IntMinMaxState start_net_pos;
|
||||
[ProtoMember(6)]
|
||||
public DoubleMinMaxState start_pnl;
|
||||
[ProtoMember(7)]
|
||||
public UIntMinMaxState day_long_pos;
|
||||
[ProtoMember(8)]
|
||||
public UIntMinMaxState day_short_pos;
|
||||
[ProtoMember(9)]
|
||||
public DoubleMinMaxState day_pnl;
|
||||
[ProtoMember(10)]
|
||||
public IntMinMaxState total_net_pos;
|
||||
[ProtoMember(11)]
|
||||
public DoubleMinMaxState total_pnl;
|
||||
[ProtoMember(12)]
|
||||
public DoubleMinMaxState pnl_price;
|
||||
|
||||
// [ProtoMember(13)]
|
||||
// ProtoMember 13 cannot be used further
|
||||
[ProtoMember(14)]
|
||||
public SecurityType[] security_types;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,10 +21,9 @@ namespace mbts.spark {
|
||||
public ExistingOrderTicketVM(OrdersModel model, IObservable<OrderRecordTuple> order_source, IObservable<int[]> calculator_buttons) {
|
||||
uint? key = null;
|
||||
var security_md_source = order_source.Where(ot => ot.security_md.HasValue).Select(ot => ot.security_md.Value);
|
||||
var security_source = security_md_source.Select(smd => smd.security);
|
||||
//
|
||||
_security = backing(nameof(security), security_source, SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_source.OnDispatcher(), order_source.Select(ot => new InlineOrderProperties(model, ot.order.Def)));
|
||||
_security = backing(nameof(security), security_md_source.Select(smd => smd.security), SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_md_source, order_source.Select(ot => new InlineOrderProperties(model, ot.order.Def)));
|
||||
_price = backing(nameof(price), double.NaN);
|
||||
size_calculator = new CalculatorLogic(null, calculator_buttons);
|
||||
_current_price = backing(nameof(current_price), order_source.Select(ort => ort.order.State.Price));
|
||||
|
||||
@@ -90,18 +90,16 @@ namespace mbts.spark {
|
||||
HashSet<uint> changed_keys = new HashSet<uint>();
|
||||
foreach (var o in orders.removed) {
|
||||
if (_order_fills.try_lookup(o.Key, out ISet<string> fill_keys)) {
|
||||
IEnumerable<FillImmutable> fills = fill_keys.Select(fk => _fills_cache[fk]);
|
||||
SecurityMD? sec_md = _get_security_md(o.security_key());
|
||||
foreach (FillImmutable fill in fills) {
|
||||
FillTuple old_ft = new FillTuple(fill, o, sec_md);
|
||||
removed.Add(old_ft.key, old_ft);
|
||||
}
|
||||
OrderRecordImmutable? curr_order = null;
|
||||
if (orders.try_get(o.Key, out OrderRecordImmutable newval)) {
|
||||
curr_order = newval;
|
||||
changed_keys.Add(o.Key);
|
||||
}
|
||||
IEnumerable<FillImmutable> fills = fill_keys.Select(fk => _fills_cache[fk]);
|
||||
foreach (FillImmutable fill in fills) {
|
||||
SecurityMD? sec_md = _get_security_md(fill.SecurityKey);
|
||||
FillTuple old_ft = new FillTuple(fill, o, sec_md);
|
||||
removed.Add(old_ft.key, old_ft);
|
||||
FillTuple new_ft = new FillTuple(fill, curr_order, sec_md);
|
||||
added.Add(new_ft.key, new_ft);
|
||||
}
|
||||
@@ -110,8 +108,8 @@ namespace mbts.spark {
|
||||
foreach (var o in orders.added) {
|
||||
if (!changed_keys.Contains(o.Key) && _order_fills.try_lookup(o.Key, out ISet<string> fill_keys)) {
|
||||
IEnumerable<FillImmutable> fills = fill_keys.Select(fk => _fills_cache[fk]);
|
||||
SecurityMD? sec_md = _get_security_md(o.security_key());
|
||||
foreach (FillImmutable fill in fills) {
|
||||
SecurityMD? sec_md = _get_security_md(fill.SecurityKey);
|
||||
FillTuple old_ft = new FillTuple(fill, null, sec_md);
|
||||
removed.Add(old_ft.key, old_ft);
|
||||
FillTuple new_ft = new FillTuple(fill, o, sec_md);
|
||||
|
||||
@@ -47,9 +47,8 @@ namespace mbts.spark {
|
||||
IObservable<InlineOrderTicketProperties> props_source,
|
||||
IObservable<int[]> calculator_buttons
|
||||
) {
|
||||
var security_source = security_md_source.Select(smd => smd.security);
|
||||
_security = backing(nameof(security), security_source, SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_source, props_source.Select(p => p.order_props));
|
||||
_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(security_md_source);
|
||||
send_order = new DelegateCommand<Side>(side => {
|
||||
bool price_valid = _validate_price(), size_valid = size_calculator.validate();
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace mbts.ui.spark {
|
||||
public DataTemplate LeanOrderTemplate { get; set; }
|
||||
public DataTemplate JoinOrderTemplate { get; set; }
|
||||
public DataTemplate TopOrderTemplate { get; set; }
|
||||
public DataTemplate TriggerOrderTemplate { get; set; }
|
||||
public DataTemplate IcebergOrderTemplate { get; set; }
|
||||
public DataTemplate SpreaderTemplate { get; set; }
|
||||
public DataTemplate BTSStopOrderTemplate { get; set; }
|
||||
@@ -41,6 +42,7 @@ namespace mbts.ui.spark {
|
||||
case CustomOrderDefinition.CustomOneofCase.Lean: return LeanOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: return JoinOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: return TopOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: return TriggerOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Iceberg: return IcebergOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: return SpreaderTemplate;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace mbts.spark {
|
||||
class InlineOrderProperties {
|
||||
@@ -44,8 +43,9 @@ namespace mbts.spark {
|
||||
readonly SelectorProperty<IOrderTypeVM> _order_type;
|
||||
IDisposable _order_type_param_errors_sub;
|
||||
|
||||
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityImmutable> security_source, IObservable<InlineOrderProperties> order_properties) {
|
||||
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineOrderProperties> order_properties) {
|
||||
_model = model;
|
||||
var security_source = security_md_source.Select(smd => smd.security);
|
||||
_security = backing(nameof(security), security_source.OnDispatcher(), SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
// guarentee that "this.security" will always be set before observers are notified
|
||||
// this is important bc some observers will reference "this.security" in their callbacks
|
||||
@@ -75,7 +75,7 @@ namespace mbts.spark {
|
||||
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
|
||||
// local methods
|
||||
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) {
|
||||
var vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_source);
|
||||
var vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source);
|
||||
vm.load_from(cd);
|
||||
return vm;
|
||||
}
|
||||
@@ -136,6 +136,7 @@ namespace mbts.spark {
|
||||
protected override void Dispose(bool disposing) {
|
||||
base.Dispose(disposing);
|
||||
Disposable.dispose(ref _order_type_param_errors_sub);
|
||||
_order_type.items.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
class IcebergOrderVM : OrderTypeVMBase<IcebergOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -34,18 +34,18 @@ namespace mbts.spark {
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public IcebergOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineIcebergOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineIcebergOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public IcebergOrderVM(OrdersModel model,
|
||||
IObservable<SecurityImmutable> security_source,
|
||||
IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineIcebergOrderProperties> inline_properties)
|
||||
: base(model) {
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override) {
|
||||
@@ -68,7 +68,7 @@ namespace mbts.spark {
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null) {
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
}
|
||||
return _child_order_choices;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace mbts.spark
|
||||
|
||||
class JoinOrderVM : OrderTypeVMBase<JoinOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -79,17 +79,17 @@ namespace mbts.spark
|
||||
}
|
||||
public int hedge_ticks { get; set; } = 1;
|
||||
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineJoinOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineJoinOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineJoinOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
@@ -157,7 +157,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace mbts.spark
|
||||
|
||||
class LeanOrderVM : OrderTypeVMBase<LeanOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -46,17 +46,17 @@ namespace mbts.spark
|
||||
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
|
||||
public uint max_chase_ticks { get; set; } = 10;
|
||||
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineLeanOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineLeanOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineLeanOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
@@ -104,7 +104,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using bts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
@@ -12,31 +13,30 @@ namespace mbts.spark {
|
||||
public static IOrderTypeVM market(OrdersModel model) => new MarketOrderVM(model);
|
||||
public static IOrderTypeVM limit(OrdersModel model) => new LimitOrderVM(model);
|
||||
public static IOrderTypeVM stop(OrdersModel model) => new StopOrderVM(model);
|
||||
public static IOrderTypeVM stop_limit(OrdersModel model) => new StopLimitOrderVM(model);
|
||||
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TimeBoxOrderVM(model, security_source);
|
||||
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TickOrderVM(model, security_source);
|
||||
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityImmutable> security_source) => new LeanOrderVM(model, security_source);
|
||||
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityImmutable> security_source) => new JoinOrderVM(model, security_source);
|
||||
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TopOrderVM(model, security_source);
|
||||
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityImmutable> security_source) => new IcebergOrderVM(model, security_source);
|
||||
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source) => new StopLimitOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TimeBoxOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TickOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source) => new LeanOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source) => new JoinOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TopOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TriggerOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source) => new IcebergOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source) => new SpreaderOrderVM(model, security_source);
|
||||
|
||||
static Func<CustomOneofCase, IOrderTypeVM> _get_factory(OrdersModel orders_model, IObservable<SecurityImmutable> security_source) =>
|
||||
new Func<CustomOneofCase, IOrderTypeVM>(ot => create(ot, orders_model, security_source));
|
||||
|
||||
public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model, IObservable<SecurityImmutable> security_source) {
|
||||
public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model, IObservable<SecurityMD> security_md_source) {
|
||||
switch (order_type) {
|
||||
case CustomOneofCase.Market: return market(model);
|
||||
case CustomOneofCase.Limit: return limit(model);
|
||||
case CustomOneofCase.Stop: return stop(model);
|
||||
case CustomOneofCase.StopLimit: return stop_limit(model);
|
||||
case CustomOneofCase.TimeBox: return time_box(model, security_source);
|
||||
case CustomOneofCase.Tick: return tick(model, security_source);
|
||||
case CustomOneofCase.Lean: return lean(model, security_source);
|
||||
case CustomOneofCase.Join: return join(model, security_source);
|
||||
case CustomOneofCase.Top: return top(model, security_source);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_source);
|
||||
case CustomOneofCase.Spreader: return spreader(model, security_source);
|
||||
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source);
|
||||
case CustomOneofCase.TimeBox: return time_box(model, security_md_source);
|
||||
case CustomOneofCase.Tick: return tick(model, security_md_source);
|
||||
case CustomOneofCase.Lean: return lean(model, security_md_source);
|
||||
case CustomOneofCase.Join: return join(model, security_md_source);
|
||||
case CustomOneofCase.Top: return top(model, security_md_source);
|
||||
case CustomOneofCase.Trigger: return trigger(model, security_md_source);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source);
|
||||
case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security));
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,61 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.utils;
|
||||
using mbts.co2;
|
||||
using mbts.ui;
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
private class StopLimitOrderVM : OrderTypeVMBase<StopLimitOrderDefinition> {
|
||||
double _stop_price = 0;
|
||||
class InlineStopLimitOrderProperties {
|
||||
readonly Func<InlineUserProperty<double>> _get_stop_price;
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model) : base(model) { }
|
||||
public static IObservable<InlineStopLimitOrderProperties> subscribe(IObservable<SecurityMD> security_md_source) {
|
||||
ulong time = 0;
|
||||
return security_md_source.Select(smd => new InlineStopLimitOrderProperties(() => {
|
||||
smd.md.get_bbo(ref time, out NativePriceSize bid, out NativePriceSize ask);
|
||||
return new InlineUserProperty<double>(bid.price);
|
||||
}));
|
||||
}
|
||||
|
||||
public InlineStopLimitOrderProperties(Func<InlineUserProperty<double>> get_stop_price) =>
|
||||
_get_stop_price = get_stop_price;
|
||||
|
||||
public InlineUserProperty<double> stop_price => _get_stop_price();
|
||||
}
|
||||
|
||||
private class StopLimitOrderVM : OrderTypeVMBase<StopLimitOrderDefinition>, IDisposable {
|
||||
UserProperty<double> _stop_price;
|
||||
InlineStopLimitOrderProperties _inline_props;
|
||||
IDisposable _dispose;
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source)) { }
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties) : base(model) {
|
||||
_stop_price = new UserProperty<double>(backing(nameof(stop_price), double.NaN));
|
||||
_dispose = Disposable.Create(
|
||||
spec_source.OnDispatcher().Subscribe(s => {
|
||||
spec = s;
|
||||
RaisePropertyChanged(nameof(spec));
|
||||
}), inline_properties.OnDispatcher().Subscribe(p => _inline_props = p));
|
||||
}
|
||||
|
||||
public override bool editable_properties_open {
|
||||
get => base.editable_properties_open;
|
||||
set {
|
||||
if (value) {
|
||||
if (_update_inline_props())
|
||||
_validate();
|
||||
}
|
||||
base.editable_properties_open = value;
|
||||
}
|
||||
}
|
||||
|
||||
public double stop_price {
|
||||
get => _stop_price;
|
||||
get => _stop_price.Value;
|
||||
set {
|
||||
_stop_price = value;
|
||||
_stop_price.Value = value;
|
||||
_validate();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +64,8 @@ namespace mbts.spark {
|
||||
SecurityDataHelper.Defaults.ContractSpecificationImmutable;
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value) {
|
||||
_update_inline_props();
|
||||
_validate();
|
||||
if (!HasErrors) {
|
||||
var sl = factory.stop_limit_order();
|
||||
sl.StopPrice = stop_price;
|
||||
@@ -28,9 +73,22 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => Disposable.dispose(ref _dispose);
|
||||
|
||||
// no need to constantly poll the md, just check the inline props when the popup opens, and right before sending an order
|
||||
// (if the user never looks at the popup, assume that the bid_price will be used)
|
||||
// TODO: add settings so the trigger-price can default to something relative to the md, e.g "bid-price" or "ask-price - nticks" or "best-bid-price with a minimum size of 10"
|
||||
bool _update_inline_props() {
|
||||
var inline_stop_price = _inline_props.stop_price;
|
||||
if (inline_stop_price.has_value && (inline_stop_price.force_override || !_stop_price.setter_called)) {
|
||||
_stop_price.bypass_setter(inline_stop_price.value);
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
void _validate() {
|
||||
using (validation())
|
||||
validation_assert(!DoubleHelper.IsNaNOrInfinity(_stop_price), string.Empty, nameof(stop_price));
|
||||
validation_assert(!DoubleHelper.IsNaNOrInfinity(stop_price), string.Empty, nameof(stop_price));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace mbts.spark
|
||||
|
||||
class TickOrderVM : OrderTypeVMBase<TickOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -55,17 +55,17 @@ namespace mbts.spark
|
||||
public uint max_chase_ticks { get; set; } = 10;
|
||||
public bool detach_on_toward { get; set; } = true;
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineTickOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTickOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTickOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
@@ -131,7 +131,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace mbts.spark {
|
||||
|
||||
class TimeBoxOrderVM : OrderTypeVMBase<TimeBoxOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
|
||||
const double _start_immediately_delay_ms = 500;
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -40,18 +40,18 @@ namespace mbts.spark {
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineTimeBoxOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTimeBoxOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TimeBoxOrderVM(OrdersModel model,
|
||||
IObservable<SecurityImmutable> security_source,
|
||||
IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTimeBoxOrderProperties> inline_properties)
|
||||
: base(model) {
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override) {
|
||||
@@ -94,7 +94,7 @@ namespace mbts.spark {
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null) {
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
}
|
||||
return _child_order_choices;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace mbts.spark
|
||||
|
||||
class TopOrderVM : OrderTypeVMBase<TopOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -77,18 +77,18 @@ namespace mbts.spark
|
||||
}
|
||||
public int hedge_ticks { get; set; } = 1;
|
||||
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineTopOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList())))
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTopOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
|
||||
{ }
|
||||
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTopOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p =>
|
||||
{
|
||||
@@ -157,7 +157,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineTriggerOrderProperties
|
||||
{
|
||||
public InlineTriggerOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
|
||||
trigger_price_type = new InlineUserProperty<TriggerPriceType>(TriggerPriceType.Bid, false);
|
||||
order_price = new InlineUserProperty<double>(double.NaN, true);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
|
||||
public readonly InlineUserProperty<TriggerPriceType> trigger_price_type;
|
||||
public readonly InlineUserProperty<double> order_price;
|
||||
}
|
||||
|
||||
class TriggerOrderVM : OrderTypeVMBase<TriggerOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineTriggerOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public TriggerPriceType trigger_price_type { get; set; } = TriggerPriceType.Bid;
|
||||
public double order_price { get; set; } = double.NaN;
|
||||
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTriggerOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTriggerOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
{
|
||||
_order_types.SetItems(p.child_order_types.value);
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (p.trigger_price_type.force_override)
|
||||
{
|
||||
trigger_price_type = p.trigger_price_type.value;
|
||||
RaisePropertyChanged(nameof(trigger_price_type));
|
||||
}
|
||||
if (p.order_price.force_override)
|
||||
{
|
||||
order_price = p.order_price.value;
|
||||
RaisePropertyChanged(nameof(order_price));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
);
|
||||
_validate();
|
||||
}
|
||||
|
||||
public IEnumerable<TriggerPriceType> trigger_price_types => EnumHelper.get_values_except<TriggerPriceType>(TriggerPriceType.LastTradePrice);
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value)
|
||||
{
|
||||
if (_child_order == null)
|
||||
_select_child_order(model.factory.default_order_type(_security.Value, true));
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.trigger_order();
|
||||
i.TriggerCondition = new TriggerCondition()
|
||||
{
|
||||
PriceType = trigger_price_type,
|
||||
AbsolutePriceTrigger = new AbsolutePriceTrigger() { TriggerQuantity = new TriggerQuantity() }
|
||||
};
|
||||
i.TriggerAction = new TriggerAction()
|
||||
{
|
||||
Price = order_price,
|
||||
SecurityId = _security.Value.Id
|
||||
};
|
||||
child_order.assign_to(i.Child);
|
||||
value.Trigger = i;
|
||||
}
|
||||
}
|
||||
|
||||
public double price_tick
|
||||
{
|
||||
get { return _security.Value.ContractSpec.LowTick; }
|
||||
}
|
||||
|
||||
public ContractSpecificationImmutable spec
|
||||
{
|
||||
get { return _security.Value.ContractSpec; }
|
||||
}
|
||||
|
||||
public ICollection<IOrderTypeVM> child_order_types
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _child_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _child_order_errors_sub);
|
||||
_child_order = value;
|
||||
_child_order?.init();
|
||||
if (_child_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposable.dispose(ref _dispose);
|
||||
if (_child_order_choices != null)
|
||||
_child_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
|
||||
public override void init()
|
||||
{
|
||||
if (_child_order == null && _security.HasValue)
|
||||
_select_child_order();
|
||||
_validate();
|
||||
}
|
||||
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
void _select_child_order(CustomOneofCase ot)
|
||||
{
|
||||
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_child_order == null)
|
||||
_child_order = child_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
|
||||
void _validate()
|
||||
{
|
||||
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
|
||||
set_error(nameof(child_order), string.Empty, true);
|
||||
else clear_error(nameof(child_order));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ namespace mbts.spark {
|
||||
_dispose = Disposable.Create(
|
||||
on_orders.Subscribe(_security_orders_demuxer),
|
||||
_bus.subscribe(SparkTags.CreateOrder).Subscribe(a => send_order(a.definition, a.price, a.size).ignore()),
|
||||
_bus.subscribe(SparkTags.RecreateOrder).Subscribe(a => a.ForEach( x => send_order(x.definition, x.price, x.size).ignore())),
|
||||
_bus.subscribe(SparkTags.CancelOrders).Subscribe(x => cancel_orders(x).ignore()),
|
||||
_bus.subscribe(SparkTags.CrackOrders).Subscribe(x => crack_orders(x).ignore()),
|
||||
_bus.subscribe(SparkTags.UpdateOrder).Subscribe(x => this.update_order(x.key, x.price, x.size).ignore())
|
||||
|
||||
@@ -177,9 +177,9 @@ namespace mbts.api.Test {
|
||||
|
||||
|
||||
class ApiTestsNativeMethods {
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr apitest_start_apitest_service(IntPtr context);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void apitest_stop_apitest_service(IntPtr server);
|
||||
|
||||
}
|
||||
|
||||
@@ -102,5 +102,7 @@ namespace bts.spark
|
||||
SparkCommand.create("Cancel Sells", Icons.CancelSells, SparkTags.CancelOrders);
|
||||
public static PartialCommand<uint> ShowModifyOrder =>
|
||||
SparkCommand.create("Modify", Icons.ModifyOrder, SparkTags.ShowModifyOrderTag);
|
||||
public static PartialCommand<IEnumerable<(OrderDefinitionImmutable, double, uint)>> RecreateSelectedOrders =>
|
||||
SparkCommand.create("Recreate", Icons.Beaker, SparkTags.RecreateOrder);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ namespace mbts.spark
|
||||
|
||||
#region Order
|
||||
public static ITag<(OrderDefinitionImmutable definition, double price, uint size)> CreateOrder { get; } = new SparkTag<(OrderDefinitionImmutable, double, uint)>();
|
||||
public static ITag<IEnumerable<(OrderDefinitionImmutable definition, double price, uint size)>> RecreateOrder { get; } = new SparkTag<IEnumerable<(OrderDefinitionImmutable, double, uint)>>();
|
||||
|
||||
public static ITag<IEnumerable<uint>> CancelOrders { get; } = new SparkTag<IEnumerable<uint>>();
|
||||
public static ITag<IEnumerable<uint>> CrackOrders { get; } = new SparkTag<IEnumerable<uint>>();
|
||||
public static ITag<(uint key, double price, uint size)> UpdateOrder { get; } = new SparkTag<(uint, double, uint)>();
|
||||
@@ -67,9 +69,11 @@ namespace mbts.spark
|
||||
public static ITag<SecurityCriteria> ShowTimeAndSalesTag { get; } = new SparkTag<SecurityCriteria>("Show TimeAndSales");
|
||||
#endregion
|
||||
|
||||
#region Persist
|
||||
public static ITag<PersistMessage> PersistCommandTag { get; } = new SparkTag<PersistMessage>("PersistCommandTag");
|
||||
public static ITag<PersistedItems> PersistedItemsTag { get; } = new SparkTag<PersistedItems>("PersistedItemsTag");
|
||||
#region Ladder
|
||||
public static ITag<Nothing> RequestRecenterLadder { get; } = new SparkTag<Nothing>(nameof(RequestRecenterLadder));
|
||||
public static ITag<DateTime> RecenterLadderBegin { get; } = new SparkTag<DateTime>(nameof(RecenterLadderBegin));
|
||||
public static ITag<DateTime> RecenterLadderNow { get; } = new SparkTag<DateTime>(nameof(RecenterLadderNow));
|
||||
|
||||
#endregion
|
||||
|
||||
public static ITag<uint> SyntheticDeleteTag { get; } = new SparkTag<uint>(nameof(SyntheticDeleteTag));
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using bts.api.core;
|
||||
using IronPython.Runtime.Types;
|
||||
using mbts;
|
||||
@@ -49,6 +50,9 @@ namespace bts.api.diagnostic {
|
||||
readonly StringBuilder _pending = new StringBuilder();
|
||||
readonly OutputPublisher _out;
|
||||
|
||||
readonly Thread _thread;
|
||||
Dispatcher _dispatcher;
|
||||
|
||||
public ShellScriptServer(ServiceContext context, ShellDiagnosticsServiceServer parent, string qualifier) : base(context, qualifier) {
|
||||
_parent = parent;
|
||||
_out = make_output_publisher();
|
||||
@@ -59,11 +63,19 @@ namespace bts.api.diagnostic {
|
||||
_scope.SetVariable("shell_objects", new Func<object,object>(o => get_exports(o,null)));
|
||||
_scope.SetVariable("shell_named_object", new Func<object,string,object>(get_export));
|
||||
_scope.SetVariable("shell_named_objects", new Func<object,string,object>(get_exports));
|
||||
|
||||
_thread = new Thread(_ => {
|
||||
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||
Thread.CurrentThread.Name = $"ShellScriptServer-{qualifier}";
|
||||
Dispatcher.Run();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public override Task<VoidMessage> dispose(VoidMessage request) {
|
||||
log("Finished", true);
|
||||
_parent.terminate(this);
|
||||
_dispatcher.BeginInvokeShutdown(DispatcherPriority.ApplicationIdle);
|
||||
return Task.FromResult(new VoidMessage());
|
||||
}
|
||||
|
||||
@@ -124,45 +136,58 @@ namespace bts.api.diagnostic {
|
||||
});
|
||||
}
|
||||
|
||||
Task<ScriptResultMessage> run_code(ScriptSource code) {
|
||||
ScriptResultMessage run_code(ScriptSource code) {
|
||||
try {
|
||||
object result = code.Execute(_scope);
|
||||
return Task.FromResult(new ScriptResultMessage { Res = ScriptResult.Complete, Message = (result?.ToString()) ?? "" });
|
||||
return new ScriptResultMessage { Res = ScriptResult.Complete, Message = (result?.ToString()) ?? "" };
|
||||
}
|
||||
catch (Exception e) {
|
||||
//log($"Exception: {e.Message}");
|
||||
return Task.FromResult(new ScriptResultMessage { Res = ScriptResult.RunError, Message = e.Message });
|
||||
return (new ScriptResultMessage { Res = ScriptResult.RunError, Message = e.Message });
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<ScriptResultMessage> run(ScriptLines request) {
|
||||
|
||||
// append lines to _pending
|
||||
foreach (var line in request.Lines) {
|
||||
_pending.AppendLine(line);
|
||||
}
|
||||
// CreateScriptSource from pending
|
||||
var code = _engine.CreateScriptSourceFromString(_pending.ToString().TrimStart(' ', '\n', '\r'), SourceCodeKind.InteractiveCode);
|
||||
var parse_result = code.GetCodeProperties();
|
||||
if (parse_result == ScriptCodeParseResult.IncompleteStatement && code.GetCode().EndsWith(Environment.NewLine+Environment.NewLine)) {
|
||||
parse_result = Microsoft.Scripting.ScriptCodeParseResult.Complete;
|
||||
}
|
||||
// todo: If code is 'incomplete' but we have 2 blank lines run it!
|
||||
switch (parse_result) {
|
||||
// If 'complete' run code
|
||||
case ScriptCodeParseResult.Complete:
|
||||
_pending.Clear();
|
||||
return run_code(code);
|
||||
// if 'invalid' return error
|
||||
case ScriptCodeParseResult.Invalid:
|
||||
Console.WriteLine("Invalid");
|
||||
_pending.Clear();
|
||||
return Task.FromResult(new ScriptResultMessage { Res = ScriptResult.Invalid, Message = code.GetCode() });
|
||||
// otherwise continue buffering
|
||||
default:
|
||||
Console.WriteLine("Incomplete");
|
||||
return Task.FromResult(new ScriptResultMessage { Res = ScriptResult.Incomplete });
|
||||
var tcs = new TaskCompletionSource<ScriptResultMessage>();
|
||||
void ret(ScriptResultMessage r) {
|
||||
tcs.TrySetResult(r);
|
||||
}
|
||||
|
||||
_dispatcher.BeginInvoke(() => {
|
||||
|
||||
// append lines to _pending
|
||||
foreach (var line in request.Lines) {
|
||||
_pending.AppendLine(line);
|
||||
}
|
||||
// CreateScriptSource from pending
|
||||
var code = _engine.CreateScriptSourceFromString(_pending.ToString().TrimStart(' ', '\n', '\r'), SourceCodeKind.InteractiveCode);
|
||||
var parse_result = code.GetCodeProperties();
|
||||
if (parse_result == ScriptCodeParseResult.IncompleteStatement && code.GetCode().EndsWith(Environment.NewLine+Environment.NewLine)) {
|
||||
parse_result = Microsoft.Scripting.ScriptCodeParseResult.Complete;
|
||||
}
|
||||
// todo: If code is 'incomplete' but we have 2 blank lines run it!
|
||||
switch (parse_result) {
|
||||
// If 'complete' run code
|
||||
case ScriptCodeParseResult.Complete:
|
||||
_pending.Clear();
|
||||
ret(run_code(code));
|
||||
break;
|
||||
// if 'invalid' return error
|
||||
case ScriptCodeParseResult.Invalid:
|
||||
Console.WriteLine("Invalid");
|
||||
_pending.Clear();
|
||||
ret(new ScriptResultMessage { Res = ScriptResult.Invalid, Message = code.GetCode() });
|
||||
break;
|
||||
// otherwise continue buffering
|
||||
default:
|
||||
Console.WriteLine("Incomplete");
|
||||
ret(new ScriptResultMessage { Res = ScriptResult.Incomplete });
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ namespace mbts.ui.native
|
||||
{
|
||||
public static class BTSUINativeMethods
|
||||
{
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
public static extern void enable_limits();
|
||||
|
||||
[DllImport("spark_lib", CharSet = CharSet.Unicode)]
|
||||
[DllImport("libspark", CharSet = CharSet.Unicode)]
|
||||
public static extern void set_window_app_id(IntPtr hwnd, string stack_id);
|
||||
}
|
||||
}
|
||||
@@ -39,4 +39,4 @@ namespace mbts.utils {
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,12 @@ namespace mbts.ui.controls {
|
||||
SecurityKey = 90,
|
||||
Side = 47,
|
||||
Size = 48,
|
||||
Symbol = 48,
|
||||
Time = 100,
|
||||
TimeInForce = 47,
|
||||
TradeKey = 76,
|
||||
User = 53;
|
||||
User = 53,
|
||||
Aggressor = 80;
|
||||
}
|
||||
|
||||
public static class DefaultColumnHeaders {
|
||||
@@ -38,9 +40,12 @@ namespace mbts.ui.controls {
|
||||
SecurityKey = "Security Key",
|
||||
Side = "Side",
|
||||
Size = "Qty",
|
||||
Symbol = "Symbol",
|
||||
TimeInForce = "TIF",
|
||||
TradeKey = "Trade Key",
|
||||
User = "User";
|
||||
User = "User",
|
||||
Time = "Time",
|
||||
Aggressor = "Aggressor";
|
||||
}
|
||||
|
||||
internal interface IKeyedColumn<TColKey> {
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui.utility.text;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
public partial class DIYGrid {
|
||||
internal class TimeAndSalesColumn : RenderTextColumn<TimeAndSalesVM.Row> {
|
||||
|
||||
readonly Func<TimeAndSalesVM.Row, string> _get_text;
|
||||
readonly Func<TimeAndSalesVM.Row, Brush> _get_brush;
|
||||
|
||||
public TimeAndSalesColumn(Func<TimeAndSalesVM.Row, string> get_text, Func<TimeAndSalesVM.Row, Brush> get_brush, IDataGridColumnFilter filter = null, string filter_template = null, GlyphContext glyph_context = null) : base(get_text, filter, filter_template, glyph_context) {
|
||||
_get_text = get_text;
|
||||
_get_brush = get_brush;
|
||||
}
|
||||
|
||||
public override void draw(TimeAndSalesVM.Row row, DrawingContext dc, ColumnGeometry position, double row_height) {
|
||||
CellRender.draw_text(dc
|
||||
, glyph_context
|
||||
, font_size
|
||||
, _get_text(row)
|
||||
, position
|
||||
, row_height
|
||||
, foreground
|
||||
, _get_brush(row)
|
||||
, horizontal_alignment
|
||||
, vertical_alignment
|
||||
, padding);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,9 +42,12 @@
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock VerticalAlignment="Center">From</TextBlock>
|
||||
<TextBlock Grid.Column="2" HorizontalAlignment="Left">
|
||||
<Hyperlink x:Name="ClearBeginLink" Focusable="False">(Set Anytime)</Hyperlink>
|
||||
</TextBlock>
|
||||
<Grid Grid.Column="2">
|
||||
<TextBlock HorizontalAlignment="Left">
|
||||
<Hyperlink x:Name="ClearBeginLink" Focusable="False">(Set Anytime)</Hyperlink>
|
||||
<Hyperlink x:Name="SetLastHourLink" Focusable="False">(Set Last Hour)</Hyperlink>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<c:DateTimePicker
|
||||
x:Name="PART_Begin_DateTimePicker"
|
||||
Grid.Row="2"
|
||||
|
||||
@@ -44,8 +44,10 @@ namespace mbts.ui.controls {
|
||||
public override void OnApplyTemplate() {
|
||||
var clear_begin_time = this.assert_template_child<Hyperlink>("ClearBeginLink");
|
||||
var clear_end_time = this.assert_template_child<Hyperlink>("ClearEndLink");
|
||||
var set_last_hour = this.assert_template_child<Hyperlink>("SetLastHourLink");
|
||||
clear_begin_time.Command = new DelegateCommand(() => Begin = default);
|
||||
clear_end_time.Command = new DelegateCommand(() => End = default);
|
||||
set_last_hour.Command = new DelegateCommand(() => Begin = TimeTextTuple.FromDateTime(DateTime.Now.AddHours(-1)));
|
||||
}
|
||||
|
||||
class TimeFilterErrorAdorner : NotifyDataErrorAdorner<TimeFilterControl, ITimeFilterVM>, IDisposable {
|
||||
|
||||
@@ -190,17 +190,21 @@ namespace mbts.ui.controls
|
||||
}
|
||||
}
|
||||
|
||||
void item_Unselected(object sender, RoutedEventArgs e) {
|
||||
if (_selection_changing) return;
|
||||
var item = ((ListBoxItem)sender).DataContext;
|
||||
var selected_items = Selection;
|
||||
if (selected_items.Contains(item)) {
|
||||
using (_selection_change()) {
|
||||
selected_items.Remove(item);
|
||||
_update_is_all_selected();
|
||||
}
|
||||
}
|
||||
}
|
||||
void item_Unselected(object sender, RoutedEventArgs e) {
|
||||
if (!_selection_changing && sender is ListBoxItem container) {
|
||||
// the [ListBoxItem] sender may be "Disconnected" https://stackoverflow.com/questions/14282894/wpf-listbox-virtualization-creates-disconnecteditems/14358688
|
||||
var item = ItemContainerGenerator.ItemFromContainer(container);
|
||||
if (item != DependencyProperty.UnsetValue) {
|
||||
var selected_items = Selection;
|
||||
if (selected_items.Contains(item)) {
|
||||
using (_selection_change()) {
|
||||
selected_items.Remove(item);
|
||||
_update_is_all_selected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void item_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) {
|
||||
using (_selection_change()) ((ListBoxItem)sender).IsSelected = Selection.Contains(e.NewValue);
|
||||
@@ -368,4 +372,4 @@ namespace mbts.ui.controls
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,7 +335,45 @@
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TopOrderTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TopOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.TriggerOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Trigger Price Type</TextBlock>
|
||||
<ComboBox
|
||||
Grid.Row="1" Grid.Column="2"
|
||||
ItemsSource="{Binding trigger_price_types}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding trigger_price_type}"
|
||||
/>
|
||||
<TextBlock Grid.Row="3" VerticalAlignment="Center">Order Price</TextBlock>
|
||||
<usp:PriceEntryControl Grid.Row="3" Grid.Column="2" Value="{Binding order_price}" ContractSpec="{Binding spec}" />
|
||||
<TextBlock Grid.Row="5">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="5" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TriggerOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.IcebergOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
|
||||
@@ -14,9 +14,20 @@ namespace mbts.ui.converters {
|
||||
static NumericSignBrushConverter() =>
|
||||
_error_brush.Freeze();
|
||||
|
||||
public Color PositiveColor { get => _positive_brush.Color; set => (_positive_brush = new SolidColorBrush(value)).Freeze(); }
|
||||
public Color NeutralColor { get => _neutral_brush.Color; set => (_neutral_brush = new SolidColorBrush(value)).Freeze(); }
|
||||
public Color NegativeColor { get => _negative_brush.Color; set => (_negative_brush = new SolidColorBrush(value)).Freeze(); }
|
||||
public Color PositiveColor { get => _positive_brush.Color; set => (_positive_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
|
||||
public Color NeutralColor { get => _neutral_brush.Color; set => (_neutral_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
|
||||
public Color NegativeColor { get => _negative_brush.Color; set => (_negative_brush = new SolidColorBrush(value) { Opacity = _opacity }).Freeze(); }
|
||||
|
||||
private double _opacity = 1.0;
|
||||
public Double Opacity {
|
||||
get { return _opacity; }
|
||||
set {
|
||||
_opacity = value;
|
||||
(_positive_brush = new SolidColorBrush(_positive_brush.Color) { Opacity = _opacity }).Freeze();
|
||||
(_negative_brush = new SolidColorBrush(_negative_brush.Color) { Opacity = _opacity }).Freeze();
|
||||
(_neutral_brush = new SolidColorBrush(_neutral_brush.Color) { Opacity = _opacity }).Freeze();
|
||||
}
|
||||
}
|
||||
public IComparable Zero { get; set; } = 0.0;
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
|
||||
@@ -26,23 +26,16 @@ namespace mbts.ui.spark.converters {
|
||||
}
|
||||
|
||||
class TickDisplayTwoWayConverter : IValueConverter {
|
||||
public ContractSpecificationImmutable? ContractSpec { get; set; } =
|
||||
new ContractSpecification {
|
||||
CashPerPoint = 1.0,
|
||||
LowTick = 0.01,
|
||||
HighTick = 0.01,
|
||||
PriceBreak = double.MaxValue,
|
||||
PriceMultiplier = 1.0
|
||||
};
|
||||
public ContractSpecificationImmutable? ContractSpec { get; set; } = SecurityDataHelper.Defaults.ContractSpecificationImmutablePennies;
|
||||
|
||||
public double DisplayMultiplier { get; set; } = double.NaN;
|
||||
public bool ShowCommas { get; set; } = false;
|
||||
public string NaNText { get; set; } = string.Empty;
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
|
||||
ContractSpec.Value.format((double)value, DisplayMultiplier, ShowCommas, NaNText);
|
||||
ContractSpec.format((double)value, DisplayMultiplier, ShowCommas, NaNText);
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
|
||||
ContractSpec.Value.parse(value as string, DisplayMultiplier, NaNText);
|
||||
ContractSpec.parse(value as string, DisplayMultiplier, NaNText);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace mbts.ui {
|
||||
|
||||
#region TraverseOnEnter
|
||||
|
||||
public static readonly DependencyProperty TraverseOnEnterProperty = DependencyProperty.RegisterAttached("TraverseOnEnter", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, OnTraverseOnEnterChanged));
|
||||
public static readonly DependencyProperty TraverseOnEnterProperty = DependencyProperty.RegisterAttached("TraverseOnEnter", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, OnTraverseOnEnterChanged));
|
||||
private static void OnTraverseOnEnterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var text_box = (TextBox)d;
|
||||
if ((bool)e.NewValue) text_box.PreviewKeyDown += commit_on_enter_PreviewKeyDownHandler;
|
||||
@@ -67,6 +67,65 @@ namespace mbts.ui {
|
||||
public static void SetCommitOnEnter(DependencyObject obj, bool value) => obj.SetValue(CommitOnEnterProperty, value);
|
||||
#endregion
|
||||
|
||||
#region CancelOnEnter
|
||||
|
||||
public static readonly DependencyProperty CancelOnEscapeProperty =
|
||||
DependencyProperty.RegisterAttached("CancelOnEscape", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, cancel_on_escape_changed));
|
||||
|
||||
private static void cancel_on_escape_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var tb = (TextBox)d;
|
||||
if ((bool)e.NewValue) {
|
||||
tb.KeyDown += tbx_keydown;
|
||||
}
|
||||
else {
|
||||
tb.KeyDown += tbx_keydown;
|
||||
}
|
||||
}
|
||||
|
||||
static void tbx_keydown(object sender, KeyEventArgs e) {
|
||||
if (e.Key == Key.Escape) {
|
||||
abort_edit(sender as DependencyObject);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the binding value back and forth to generate a set to the source property with the original value
|
||||
private static void abort_edit(DependencyObject sender) {
|
||||
if (sender != null) {
|
||||
var binding = BindingOperations.GetBindingExpression(sender, TextBox.TextProperty);
|
||||
binding?.UpdateTarget();
|
||||
binding?.UpdateSource();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetCancelOnEscape(DependencyObject obj) => (bool)obj.GetValue(CancelOnEscapeProperty);
|
||||
public static void SetCancelOnEscape(DependencyObject obj, bool value) => obj.SetValue(CancelOnEscapeProperty, value);
|
||||
|
||||
#endregion
|
||||
|
||||
#region TryCancelOnFocusLost
|
||||
public static readonly DependencyProperty TryCancelOnFocusLostProperty =
|
||||
DependencyProperty.RegisterAttached("TryCancelOnFocusLost", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, cancel_on_focus_lost_changed));
|
||||
|
||||
private static void cancel_on_focus_lost_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var tb = (TextBox)d;
|
||||
if ((bool)e.NewValue) {
|
||||
tb.LostFocus += tb_lostfocus;
|
||||
}
|
||||
else {
|
||||
tb.LostFocus += tb_lostfocus;
|
||||
}
|
||||
}
|
||||
|
||||
static void tb_lostfocus(object sender, EventArgs e) {
|
||||
abort_edit(sender as DependencyObject);
|
||||
}
|
||||
|
||||
public static bool GetTryCancelOnFocusLost(DependencyObject obj) => (bool)obj.GetValue(TryCancelOnFocusLostProperty);
|
||||
public static void SetTryCancelOnFocusLost(DependencyObject obj, bool value) => obj.SetValue(TryCancelOnFocusLostProperty, value);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ReleaseOnEscape
|
||||
public static readonly DependencyProperty ReleaseOnEscapeProperty = DependencyProperty.RegisterAttached(
|
||||
"ReleaseOnEscape", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, OnReleaseOnEscapeChanged));
|
||||
@@ -79,7 +138,8 @@ namespace mbts.ui {
|
||||
tb.ReleaseMouseCapture();
|
||||
Keyboard.ClearFocus();
|
||||
}));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
tb.ClearKeyBindings(Key.Escape, ModifierKeys.None);
|
||||
tb.ClearCommandBindings(ReleaseCaptureCommand);
|
||||
}
|
||||
@@ -180,17 +240,18 @@ namespace mbts.ui {
|
||||
"IncrementDecrementOnMouseClick", typeof(bool), typeof(TextBoxBehavior), new PropertyMetadata(false, IncrementDecrementOnMouseClickPropertyChanged)
|
||||
);
|
||||
|
||||
public static bool GetIncrementDecrementOnMouseClick(TextBox element) { return (bool)element.GetValue(IncrementDecrementOnMouseClickProperty); }
|
||||
public static bool GetIncrementDecrementOnMouseClick(TextBox element) { return (bool)element.GetValue(IncrementDecrementOnMouseClickProperty); }
|
||||
public static void SetIncrementDecrementOnMouseClick(TextBox element, bool value) { element.SetValue(IncrementDecrementOnMouseClickProperty, value); }
|
||||
|
||||
|
||||
private static void IncrementDecrementOnMouseClickPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
var text_box = d as TextBox;
|
||||
if(text_box != null) {
|
||||
if (text_box != null) {
|
||||
if ((bool)e.NewValue) {
|
||||
text_box.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownDecrementValue;
|
||||
text_box.PreviewMouseRightButtonDown += OnMouseRightButtonDownIncrementValue;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
text_box.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownDecrementValue;
|
||||
text_box.PreviewMouseRightButtonDown -= OnMouseRightButtonDownIncrementValue;
|
||||
}
|
||||
@@ -201,7 +262,8 @@ namespace mbts.ui {
|
||||
var text_box = sender as TextBox;
|
||||
if (text_box == null || String.IsNullOrWhiteSpace(text_box.Text)) {
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (uint.TryParse(text_box.Text, out uint val)) {
|
||||
if (val < uint.MaxValue) {
|
||||
val++;
|
||||
@@ -226,5 +288,6 @@ namespace mbts.ui {
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,9 @@ namespace mbts.ui.shell
|
||||
_recently_closed_windows.Push(() => { undoaction(); });
|
||||
}
|
||||
}
|
||||
_bookmark.save_bookmark(document.Model);
|
||||
if (document.HasCustomTitle) {
|
||||
_bookmark.save_bookmark(document.Model);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove_document(TabDocument document) {
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
ui:TextBoxBehavior.CommitOnEnter="True"
|
||||
ui:TextBoxBehavior.FocusOnLoaded="True"
|
||||
ui:TextBoxBehavior.SelectAllOnFocus="True"
|
||||
ui:TextBoxBehavior.ReleaseOnEscape="True"
|
||||
ui:TextBoxBehavior.CancelOnEscape="True"
|
||||
ui:TextBoxBehavior.TryCancelOnFocusLost="True"
|
||||
Width="150" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0" />
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="_template">
|
||||
|
||||
@@ -376,7 +376,7 @@ namespace mbts.spark
|
||||
return x is SecurityImmutable && y is SecurityImmutable ? Compare((SecurityImmutable)x, (SecurityImmutable)y) : -1;
|
||||
}
|
||||
public int Compare(SecurityImmutable x, SecurityImmutable y) {
|
||||
return x.EarliestExpiration().CompareTo(y.EarliestExpiration());
|
||||
return SecurityHelper.Comparer.Compare(x, y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using bts.api;
|
||||
using System.IO;
|
||||
using mbts.ui.shell;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
public class DataPersistor
|
||||
{
|
||||
private List<IDisposable> _disp_list = new List<IDisposable>();
|
||||
private readonly IBus _bus;
|
||||
private readonly string _loc = @"C:\ProgramData\bts\temp_securities.bin"; /// TODO
|
||||
|
||||
private SparkBusData.PersistedItems _persisted_items = new SparkBusData.PersistedItems();
|
||||
|
||||
public DataPersistor(IBus bus)
|
||||
{
|
||||
_bus = bus;
|
||||
var sub = _bus.subscribe<SparkBusData.PersistMessage>(SparkTags.PersistCommandTag);
|
||||
_disp_list.Add(sub.Subscribe(p => _on_new_persist_message(p)));
|
||||
_persisted_items = _read_data_from_file();
|
||||
_bus.post(SparkTags.PersistedItemsTag, _persisted_items);
|
||||
}
|
||||
|
||||
private void _on_new_persist_message(SparkBusData.PersistMessage p)
|
||||
{
|
||||
_persisted_items.items.Add(p);
|
||||
_write_to_file(_persisted_items);
|
||||
_bus.post(SparkTags.PersistedItemsTag, new SparkBusData.PersistedItems(_persisted_items.items));
|
||||
}
|
||||
|
||||
private void _write_to_file(SparkBusData.PersistedItems items)
|
||||
{
|
||||
byte[] data = SparkUtils.serialize_proto(items);
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(_loc, data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
mbts.Logger.log(bts.log.Level.Error, bts.log.Subsystem.gen, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private SparkBusData.PersistedItems _read_data_from_file()
|
||||
{
|
||||
var rv = new SparkBusData.PersistedItems();
|
||||
try
|
||||
{
|
||||
if (File.Exists(_loc)) {
|
||||
byte[] data = File.ReadAllBytes(_loc);
|
||||
rv = SparkUtils.deserialize_proto<SparkBusData.PersistedItems>(data);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,18 +121,6 @@ namespace mbts.spark {
|
||||
public static string order_type_display_name(CustomOrderDefinition.CustomOneofCase order_type) =>
|
||||
camel_to_title_case(Regex.Replace(order_type.ToString(), "Order", string.Empty, RegexOptions.IgnoreCase));
|
||||
|
||||
public static string order_collection_summary(ICollection<OrderRecordImmutable> orders, Comparison<double> compare_price, ContractSpecificationImmutable spec) {
|
||||
if (orders.Count == 0) return string.Empty;
|
||||
var price_lookup = orders.ToLookup(o => o.State.Price);
|
||||
var best_price = price_lookup.MaxElement(g => g.Key, compare_price);
|
||||
var best_price_volume = best_price.Sum(o => o.working_quantity());
|
||||
return order_collection_summary(
|
||||
best_price.Key,
|
||||
best_price_volume,
|
||||
best_price_volume + price_lookup.Where(g => g.Key != best_price.Key).SelectMany(g => g).Sum(o => o.working_quantity()),
|
||||
spec);
|
||||
}
|
||||
|
||||
public static string order_collection_summary(double best_price, int best_price_volume, int total_volume, ContractSpecificationImmutable spec) {
|
||||
if (total_volume == 0)
|
||||
return string.Empty;
|
||||
|
||||
@@ -315,4 +315,4 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.co2;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -8,15 +7,15 @@ namespace mbts.spark {
|
||||
readonly OrdersModel _order_gateway;
|
||||
|
||||
public LadderModel(SecurityMD source, OrdersModel ordergw) {
|
||||
security = source.security;
|
||||
md = source.md;
|
||||
security_md = source;
|
||||
_order_gateway = ordergw;
|
||||
}
|
||||
|
||||
public enum CancelType { All, Bids, Asks }
|
||||
|
||||
public SecurityImmutable security { get; }
|
||||
public MDConsumerHandle md { get; }
|
||||
public SecurityMD security_md { get; }
|
||||
public SecurityImmutable security => security_md.security;
|
||||
public MDConsumerHandle md => security_md.md;
|
||||
public OrdersModel order_gateway => _order_gateway;
|
||||
|
||||
public Task cancel_orders(CancelType ct) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace mbts.spark {
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
settings_command,
|
||||
order_properties_vm = new OrderPropertiesEditorVM(model.order_gateway, Observable.Return(security), _settings_manager.subscribe_current().Select(s => new InlineOrderProperties(model.order_gateway, s))),
|
||||
order_properties_vm = new OrderPropertiesEditorVM(model.order_gateway, Observable.Return(model.security_md), _settings_manager.subscribe_current().Select(s => new InlineOrderProperties(model.order_gateway, s))),
|
||||
size_calculator = new CalculatorLogic(0, _settings_manager.subscribe_current().Select(s => s.calculator_buttons)),
|
||||
bus.subscribe(SparkTags.LogActivity).OnDispatcher().Subscribe(status_update),
|
||||
model.order_gateway.working_orders.OnDispatcher().Subscribe(working => {
|
||||
@@ -143,6 +143,8 @@ namespace mbts.spark {
|
||||
);
|
||||
}
|
||||
|
||||
public IBus bus => _bus;
|
||||
|
||||
public event Action on_tick;
|
||||
|
||||
public MDConsumerHandle md => _model.md;
|
||||
|
||||
@@ -483,6 +483,7 @@
|
||||
Background="{x:Null}" BorderBrush="{x:Null}"
|
||||
PieBackground="Transparent"
|
||||
SliceBackground="#a000"
|
||||
Bus="{Binding bus}"
|
||||
Command="{Binding RecenterCommand, ElementName=_ladder}">
|
||||
<spark:RecenterCountdown.SliceStroke>
|
||||
<Pen Thickness="1" Brush="Black" />
|
||||
|
||||
@@ -67,8 +67,8 @@ namespace mbts.spark {
|
||||
double up = mid, down = mid;
|
||||
// Starting from the middle iterate out to the edges
|
||||
for (int x = 1; x <= range; ++x) {
|
||||
up = TickUtils.tickup(spec, up);
|
||||
down = TickUtils.tickdown(spec, down);
|
||||
up = spec.tickup(up);
|
||||
down = spec.tickdown(down);
|
||||
result[middle - x] = up;
|
||||
result[middle + x] = down;
|
||||
}
|
||||
|
||||
@@ -1,81 +1,56 @@
|
||||
using bts.api;
|
||||
using bts.api;
|
||||
using mbts.co2;
|
||||
using mbts.ui.controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace mbts.spark {
|
||||
// Implement the recenter logic in a UI control
|
||||
// Kind of a hack but it makes receiving input from the UI easier
|
||||
public class RecenterCountdown : CountDownTimer {
|
||||
DispatcherTimer _timer = new DispatcherTimer(DispatcherPriority.Background) {
|
||||
Interval = TimeSpan.FromSeconds(1)
|
||||
};
|
||||
SecurityImmutable? _security;
|
||||
MDConsumerHandle _handle;
|
||||
DateTime _last_recenter = DateTime.MaxValue;
|
||||
double _last_mid = 0.0;
|
||||
IDisposable _dispose;
|
||||
readonly TimeSpan _seconds_to_center;
|
||||
IBus _bus;
|
||||
|
||||
public RecenterCountdown() {
|
||||
DataContextChanged += RecenterCountdown_DataContextChanged;
|
||||
Unloaded += (o,e) => on_unload(false);
|
||||
_timer.Tick += _timer_Tick;
|
||||
_timer.Start();
|
||||
_seconds_to_center = TimeSpan.FromSeconds(config.Configuration.get<bts.config.LadderKnobs>().seconds_to_recenter);
|
||||
Unloaded += (o, e) => on_unload(false);
|
||||
}
|
||||
|
||||
static (double,double) tick_range(double price, int tick_count, ContractSpecificationImmutable spec) {
|
||||
return (TickUtils.tickdown(spec, price, tick_count), TickUtils.tickup(spec, price, tick_count));
|
||||
public IBus Bus {
|
||||
get { return (IBus)GetValue(busProperty); }
|
||||
set { SetValue(busProperty, value); }
|
||||
}
|
||||
|
||||
private void _timer_Tick(object sender, EventArgs e) {
|
||||
if (_security != null) {
|
||||
var mid = _handle.mid();
|
||||
if (//mid < low || mid > high ||
|
||||
(_last_recenter + _seconds_to_center < DateTime.UtcNow)) {
|
||||
trigger_recenter(mid);
|
||||
}
|
||||
public static readonly DependencyProperty busProperty =
|
||||
DependencyProperty.Register("Bus", typeof(IBus), typeof(RecenterCountdown), new PropertyMetadata(null, bus_changed));
|
||||
|
||||
static void bus_changed(object sender, DependencyPropertyChangedEventArgs args) {
|
||||
if (sender is RecenterCountdown self) {
|
||||
self.setbus(args.NewValue as IBus);
|
||||
}
|
||||
}
|
||||
|
||||
void setbus(IBus value) {
|
||||
if (value != null) {
|
||||
Disposable.dispose(ref _dispose);
|
||||
_bus = value;
|
||||
_dispose =
|
||||
Disposable.Create(
|
||||
_bus?.subscribe(SparkTags.RecenterLadderBegin).OnDispatcher().Subscribe(begin_recenter),
|
||||
_bus?.subscribe(SparkTags.RecenterLadderNow).OnDispatcher().Subscribe(_ => base.OnClick())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void begin_recenter(DateTime _) => start(null);
|
||||
|
||||
private void on_unload(bool _) {
|
||||
Disposable.dispose(ref _dispose);
|
||||
_timer.Stop();
|
||||
}
|
||||
|
||||
private void RecenterCountdown_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) {
|
||||
Disposable.dispose(ref _dispose);
|
||||
var vm = e.NewValue as LadderVM;
|
||||
if (vm != null) {
|
||||
_security = vm.security;
|
||||
_handle = vm.md;
|
||||
reset_counters();
|
||||
vm.OnDisposed += on_unload;
|
||||
_dispose =
|
||||
Disposable.Create(
|
||||
Disposable.Create(() => vm.OnDisposed -= on_unload));
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_recenter(double mid) {
|
||||
reset_counters();
|
||||
start(null);
|
||||
}
|
||||
|
||||
protected override void OnClick() {
|
||||
reset_counters();
|
||||
base.OnClick();
|
||||
}
|
||||
|
||||
void reset_counters() {
|
||||
_last_recenter = DateTime.UtcNow;
|
||||
_last_mid = _handle.mid();
|
||||
_bus?.post(SparkTags.RequestRecenterLadder, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -10,69 +11,111 @@ using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Data;
|
||||
using static bts.shell.PAndLWindowFactory;
|
||||
using static bts.api.orderman.Fill.Types;
|
||||
|
||||
namespace mbts.spark {
|
||||
class PAndLVM : DisposableViewModelBase {
|
||||
|
||||
|
||||
public PAndLVM(IBus bus, PAndLModel model, PAndLStartupData state) {
|
||||
|
||||
public PAndLVM(IBus bus, PAndLModel model, PAndLStartupData startup_data) {
|
||||
_bus = bus;
|
||||
_model = model;
|
||||
_selected_pnl_calc_type = backing<pnl_calc_type>(nameof(selected_pnl_calc_type));
|
||||
// get initial value from settings
|
||||
// else default to midpoint
|
||||
// TODO:
|
||||
// need to respond to settings change somehow as the
|
||||
// combobox that selected_pnl_calc_type is bound to will be
|
||||
// gone from this form.
|
||||
// ditto start of day changes, but this will be a bit more involved
|
||||
// since we'll need to create a new PnLCalculator when this changes. (infrequent, if ever)
|
||||
_selected_pnl_calc_type.Value = (startup_data != null && startup_data.calc_type != pnl_calc_type.none) ? startup_data.calc_type : pnl_calc_type.midpoint;
|
||||
_selected_pnl_calc_type.Changed += pnl_calc_type_changed;
|
||||
_start_of_day_ts = (startup_data != null && startup_data.start_of_day_time.HasValue) ? startup_data.start_of_day_time.Value : new TimeSpan(16, 30, 0);
|
||||
var pnl = model.on_pnl_data
|
||||
.Sample(_POSITION_UPDATE_PERIOD)
|
||||
.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (p != null) {
|
||||
PnLCalculator calc = new PnLCalculator(p.state.Values);
|
||||
PnLCalculator calc = new PnLCalculator(p.state.Values, _selected_pnl_calc_type.Value, _start_of_day_ts);
|
||||
var summary_position_rows = calc.get_summary_position();
|
||||
_pnl_data.sync_with(summary_position_rows);
|
||||
_last_pnl_update_time = DateTime.Now;
|
||||
}
|
||||
});
|
||||
pnl_view = CollectionViewSource.GetDefaultView(_pnl_data);
|
||||
pnl_view.SetSortingCriteria(
|
||||
new List<SortingCriteria>() {
|
||||
new SortingCriteria("account", true)
|
||||
, new SortingCriteria("exchange_string", true)
|
||||
, new SortingCriteria("symbol", true)
|
||||
, new SortingCriteria("security_text", true)
|
||||
}
|
||||
);
|
||||
|
||||
// restore filter state
|
||||
var fs = startup_data?.filters;
|
||||
if (fs != null) {
|
||||
account_filter.load_state(fs.account_regex);
|
||||
exchange_string_filter.load_state(fs.exchange_regex);
|
||||
symbol_filter.load_state(fs.symbol_regex);
|
||||
security_filter.load_state(fs.security_regex);
|
||||
start_position_filter.load_state(fs.start_net_pos);
|
||||
start_pnl_filter.load_state(fs.start_pnl);
|
||||
day_long_quantity_filter.load_state(fs.day_long_pos);
|
||||
day_short_quantity_filter.load_state(fs.day_short_pos);
|
||||
day_pnl_filter.load_state(fs.day_pnl);
|
||||
total_net_position_filter.load_state(fs.total_net_pos);
|
||||
total_pnl_filter.load_state(fs.total_pnl);
|
||||
pnl_price_filter.load_state(fs.pnl_price);
|
||||
security_type_filter.load_state(fs.security_types);
|
||||
}
|
||||
// restore sort state or supply default value
|
||||
if (startup_data?.sort_criteria?.Any() == true) {
|
||||
pnl_view.SetSortingCriteria(startup_data.sort_criteria);
|
||||
} else {
|
||||
pnl_view.SetSortingCriteria(new List<SortingCriteria>() {
|
||||
new SortingCriteria(nameof(PnLSummaryRow.account), true)
|
||||
, new SortingCriteria(nameof(PnLSummaryRow.exchange_string), true)
|
||||
, new SortingCriteria(nameof(PnLSummaryRow.symbol), true)
|
||||
, new SortingCriteria(nameof(PnLSummaryRow.security_text), true)
|
||||
});
|
||||
}
|
||||
pnl_view.CollectionChanged += Pnl_view_CollectionChanged;
|
||||
|
||||
//Account[] accounts =
|
||||
|
||||
|
||||
#region Create grid filters and load state
|
||||
#endregion
|
||||
|
||||
// setup the main Disposable and
|
||||
// hook up grid filters
|
||||
_dispose = Disposable.Create(pnl,
|
||||
DataGridFilterHelper.hook_filters(pnl_view,
|
||||
security_filter, pnl_filter,
|
||||
buy_quantity_filter, sell_quantity_filter,
|
||||
net_position_filter, weighted_average_buy_filter,
|
||||
weighted_average_sell_filter, pnl_price_filter,
|
||||
sod_position_filter, account_filter,
|
||||
exchange_string_filter, symbol_filter),
|
||||
account_filter,
|
||||
exchange_string_filter,
|
||||
symbol_filter,
|
||||
security_filter,
|
||||
start_position_filter,
|
||||
start_pnl_filter,
|
||||
day_long_quantity_filter,
|
||||
day_short_quantity_filter,
|
||||
day_pnl_filter,
|
||||
total_net_position_filter,
|
||||
total_pnl_filter,
|
||||
pnl_price_filter,
|
||||
weighted_average_buy_filter,
|
||||
weighted_average_sell_filter,
|
||||
security_type_filter),
|
||||
WorkLoop.Start("PAndL", _UPDATE_PERIOD, _calculate_pnl)
|
||||
);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
pnl_view.CollectionChanged -= Pnl_view_CollectionChanged;
|
||||
}
|
||||
|
||||
|
||||
private void Pnl_view_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
|
||||
_update_totals();
|
||||
}
|
||||
|
||||
private void pnl_calc_type_changed(pnl_calc_type oldval, pnl_calc_type newval) {
|
||||
_calculate_pnl();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
pnl_view.CollectionChanged -= Pnl_view_CollectionChanged;
|
||||
_selected_pnl_calc_type.Changed -= pnl_calc_type_changed;
|
||||
}
|
||||
|
||||
void _update_totals() {
|
||||
if (pnl_view != null) {
|
||||
total_pnl = pnl_view.Cast<PnLSummaryRow>().Where(r => !Double.IsNaN(r.pnl)).Sum(r => r.pnl);
|
||||
total_net_pos = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.net_position);
|
||||
total_buy_qty = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.buy_quantity);
|
||||
total_sell_qty = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.sell_quantity);
|
||||
total_aggregate_pnl = pnl_view.Cast<PnLSummaryRow>().Where(r => !Double.IsNaN(r.total_pnl)).Sum(r => r.total_pnl);
|
||||
total_aggregate_net_pos = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.total_net_position);
|
||||
total_aggregate_buy_qty = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.day_long_quantity);
|
||||
total_aggregate_sell_qty = pnl_view.Cast<PnLSummaryRow>().Sum(r => r.day_short_quantity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,75 +124,113 @@ namespace mbts.spark {
|
||||
// position update in a while
|
||||
if (DateTime.Now.Subtract(_last_pnl_update_time) > _UPDATE_PERIOD) {
|
||||
foreach (var r in _pnl_data) {
|
||||
r.update_pnl(pnl_calc_type.waterfall);
|
||||
if (_selected_pnl_calc_type.Value != pnl_calc_type.none) {
|
||||
r.update_pnl(_selected_pnl_calc_type.Value);
|
||||
}
|
||||
}
|
||||
_update_totals();
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime _last_pnl_update_time = DateTime.MinValue;
|
||||
private ObservableCollection<PnLSummaryRow> _pnl_data = new ObservableCollection<PnLSummaryRow>();
|
||||
#region Properties
|
||||
|
||||
public ICollectionView pnl_view { get; }
|
||||
|
||||
private double _total_pnl;
|
||||
public double total_pnl {
|
||||
public List<pnl_calc_type> pnl_calc_types { get; } = EnumHelper.get_values_except(pnl_calc_type.none, pnl_calc_type.waterfall, pnl_calc_type.bid_or_ask, pnl_calc_type.open, pnl_calc_type.close).ToList();
|
||||
|
||||
public pnl_calc_type selected_pnl_calc_type {
|
||||
get => _selected_pnl_calc_type.Value;
|
||||
set => _selected_pnl_calc_type.Value = value;
|
||||
}
|
||||
|
||||
public double total_aggregate_pnl {
|
||||
get {
|
||||
return _total_pnl;
|
||||
return _total_aggregate_pnl;
|
||||
}
|
||||
set {
|
||||
_total_pnl = value;
|
||||
RaisePropertyChanged(nameof(total_pnl));
|
||||
_total_aggregate_pnl = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private double _total_net_pos;
|
||||
public double total_net_pos {
|
||||
get { return _total_net_pos; }
|
||||
public double total_aggregate_net_pos {
|
||||
get { return _total_aggregate_net_pos; }
|
||||
set {
|
||||
_total_net_pos = value;
|
||||
RaisePropertyChanged(nameof(total_net_pos));
|
||||
_total_aggregate_net_pos = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private double _total_buy_qty;
|
||||
public double total_buy_qty {
|
||||
get { return _total_buy_qty; }
|
||||
public double total_aggregate_buy_qty {
|
||||
get { return _total_aggregate_buy_qty; }
|
||||
set {
|
||||
_total_buy_qty = value;
|
||||
RaisePropertyChanged(nameof(total_buy_qty));
|
||||
_total_aggregate_buy_qty = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private double _total_sell_qty;
|
||||
public double total_sell_qty {
|
||||
get { return _total_sell_qty; }
|
||||
public double total_aggregate_sell_qty {
|
||||
get { return _total_aggregate_sell_qty; }
|
||||
set {
|
||||
_total_sell_qty = value;
|
||||
RaisePropertyChanged(nameof(total_sell_qty));
|
||||
_total_aggregate_sell_qty = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan start_of_day {
|
||||
get { return _start_of_day_ts; }
|
||||
}
|
||||
|
||||
#region Filters
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> security_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.security_text, nameof(PnLSummaryRow.security_text));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.pnl, nameof(PnLSummaryRow.pnl));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> buy_quantity_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.buy_quantity, nameof(PnLSummaryRow.buy_quantity));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> sell_quantity_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.sell_quantity, nameof(PnLSummaryRow.sell_quantity));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> net_position_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.net_position, nameof(PnLSummaryRow.net_position));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_buy_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_buy, nameof(PnLSummaryRow.weighted_average_buy));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_sell_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_sell, nameof(PnLSummaryRow.weighted_average_sell));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> pnl_price_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.pnl_price, nameof(PnLSummaryRow.pnl_price));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> sod_position_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.sod_position, nameof(PnLSummaryRow.sod_position));
|
||||
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> account_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.account, nameof(PnLSummaryRow.account));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> exchange_string_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.exchange_string, nameof(PnLSummaryRow.exchange_string));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> symbol_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.symbol, nameof(PnLSummaryRow.symbol));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, string> security_filter { get; } = new RegexFilterVM<PnLSummaryRow>(r => r.security_text, nameof(PnLSummaryRow.security_text));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, IntMinMaxState> start_position_filter { get; } = new IntMinMax<PnLSummaryRow>(r => r.start_net_position, nameof(PnLSummaryRow.start_net_position));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> start_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.start_pnl, nameof(PnLSummaryRow.start_pnl));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, UIntMinMaxState> day_long_quantity_filter { get; } = new UIntMinMax<PnLSummaryRow>(r => r.day_long_quantity, nameof(PnLSummaryRow.day_long_quantity));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, UIntMinMaxState> day_short_quantity_filter { get; } = new UIntMinMax<PnLSummaryRow>(r => r.day_short_quantity, nameof(PnLSummaryRow.day_short_quantity));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> day_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.day_pnl, nameof(PnLSummaryRow.day_pnl));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, IntMinMaxState> total_net_position_filter { get; } = new IntMinMax<PnLSummaryRow>(r => r.total_net_position, nameof(PnLSummaryRow.total_net_position));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> total_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.total_pnl, nameof(PnLSummaryRow.total_pnl));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> pnl_price_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.pnl_price, nameof(PnLSummaryRow.pnl_price));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_buy_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_buy, nameof(PnLSummaryRow.weighted_average_buy));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_sell_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_sell, nameof(PnLSummaryRow.weighted_average_sell));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, SecurityType[]> security_type_filter { get; } = DiscreteValuesFilterVM.Create<PnLSummaryRow, SecurityType>(EnumHelper.get_values_except<SecurityType>(SecurityType.Option, SecurityType.Stock, SecurityType.Synthetic).ToArray(), r => r.security_type, nameof(PnLSummaryRow.security_type));
|
||||
|
||||
public FilterStates get_filters_states() => new FilterStates {
|
||||
account_regex = account_filter.get_state(),
|
||||
exchange_regex = exchange_string_filter.get_state(),
|
||||
symbol_regex = exchange_string_filter.get_state(),
|
||||
security_regex = security_filter.get_state(),
|
||||
start_net_pos = start_position_filter.get_state(),
|
||||
start_pnl = start_pnl_filter.get_state(),
|
||||
day_long_pos = day_long_quantity_filter.get_state(),
|
||||
day_short_pos = day_short_quantity_filter.get_state(),
|
||||
day_pnl = day_pnl_filter.get_state(),
|
||||
total_net_pos = total_net_position_filter.get_state(),
|
||||
total_pnl = total_pnl_filter.get_state(),
|
||||
pnl_price = pnl_price_filter.get_state(),
|
||||
security_types = security_type_filter.get_state()
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
public List<SortingCriteria> get_sorting_states() => pnl_view.GetSortingCriteria();
|
||||
|
||||
#endregion
|
||||
|
||||
readonly IBus _bus;
|
||||
readonly PAndLModel _model;
|
||||
static readonly TimeSpan _UPDATE_PERIOD = TimeSpan.FromSeconds(1);
|
||||
static readonly TimeSpan _POSITION_UPDATE_PERIOD = TimeSpan.FromMilliseconds(333);
|
||||
readonly Property<pnl_calc_type> _selected_pnl_calc_type;
|
||||
|
||||
double _total_aggregate_pnl, _total_aggregate_net_pos, _total_aggregate_buy_qty, _total_aggregate_sell_qty;
|
||||
TimeSpan _start_of_day_ts;
|
||||
DateTime _last_pnl_update_time = DateTime.MinValue;
|
||||
ObservableCollection<PnLSummaryRow> _pnl_data = new ObservableCollection<PnLSummaryRow>();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,19 +18,26 @@
|
||||
<Style x:Key="RightAlignedColumn" TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="TextAlignment" Value="Right" />
|
||||
</Style>
|
||||
<convert:DoubleTextConverter x:Key="DoubleWithNaNHiddenConverter" Format="F2" />
|
||||
<convert:NumericSignBrushConverter x:Key="PnLBackgroundColorConverter" Opacity="0.33"/>
|
||||
<convert:DoubleTextConverter x:Key="DoubleWithNaNHiddenConverter" Format="N0" />
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<!-- Table -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<c:DataGridEx Grid.Row="0" Grid.Column="0" x:Name="_grid" IsReadOnly="True" BorderThickness="0" GridLinesVisibility="None" CanUserSortColumns="True" SelectionUnit="FullRow"
|
||||
<ComboBox Grid.Row="0"
|
||||
ItemsSource="{Binding pnl_calc_types}"
|
||||
SelectedItem="{Binding selected_pnl_calc_type}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}">
|
||||
</ComboBox>
|
||||
<c:DataGridEx Grid.Row="1" Grid.Column="0" x:Name="_grid" IsReadOnly="True" BorderThickness="0" GridLinesVisibility="None" CanUserSortColumns="True" SelectionUnit="FullRow"
|
||||
ItemsSource="{Binding pnl_view}" uc:DataGridBehavior.NotifyColumnsOfSorting="True">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn x:Name="account" Header="Account" Binding="{Binding account}" IsReadOnly="true"
|
||||
@@ -57,46 +64,78 @@
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="sod" Header="SOD" Binding="{Binding sod_position}" IsReadOnly="true"
|
||||
<DataGridTextColumn x:Name="security_type" Header="Security Type" Binding="{Binding security_type}" IsReadOnly="true"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.security_type_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_type_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_type_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="start_net_pos" Header="Start Net Pos" Binding="{Binding start_net_position}" IsReadOnly="true"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.sod_position_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.sod_position_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.sod_position_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.start_position_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.start_position_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.start_position_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="buy_qty" Header="Buy Qty" Binding="{Binding buy_quantity}" IsReadOnly="true"
|
||||
<DataGridTextColumn x:Name="start_pnl" Header="Start PnL" IsReadOnly="true" SortMemberPath="start_pnl"
|
||||
Binding="{Binding start_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.start_pnl_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.start_pnl_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.start_pnl_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.ElementStyle>
|
||||
<Style TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="TextAlignment" Value="Right" />
|
||||
<Setter Property="Background" Value="{Binding start_pnl, Converter={StaticResource PnLBackgroundColorConverter}}" />
|
||||
</Style>
|
||||
</DataGridTextColumn.ElementStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn x:Name="day_long" Header="Day Long" Binding="{Binding day_long_quantity}" IsReadOnly="true"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.buy_quantity_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.buy_quantity_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.buy_quantity_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.day_long_quantity_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.day_long_quantity_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.day_long_quantity_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="sell_qty" Header="Sell Qty" Binding="{Binding sell_quantity}" IsReadOnly="true"
|
||||
<DataGridTextColumn x:Name="day_short" Header="Day Short" Binding="{Binding day_short_quantity}" IsReadOnly="true"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.sell_quantity_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.sell_quantity_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.sell_quantity_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.day_short_quantity_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.day_short_quantity_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.day_short_quantity_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="net_pos" Header="Net Pos" Binding="{Binding net_position}" IsReadOnly="true"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.net_position_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.net_position_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.net_position_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="pnl" Header="PnL" IsReadOnly="true" SortMemberPath="pnl"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.pnl_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.pnl_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.pnl_filter.open, Source={uc:DirectReference _grid}}"
|
||||
<DataGridTextColumn x:Name="day_pnl" Header="Day PnL" IsReadOnly="true" SortMemberPath="day_pnl"
|
||||
Binding="{Binding day_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.day_pnl_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.day_pnl_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.day_pnl_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="pnl"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
<DataGridTextColumn.ElementStyle>
|
||||
<Style TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="TextAlignment" Value="Right" />
|
||||
<Setter Property="Background" Value="{Binding day_pnl, Converter={StaticResource PnLBackgroundColorConverter}}" />
|
||||
</Style>
|
||||
</DataGridTextColumn.ElementStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn x:Name="total_net_pos" Header="Total Net Pos" Binding="{Binding total_net_position}" IsReadOnly="true"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.total_net_position_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.total_net_position_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.total_net_position_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="total_pnl" Header="Total PnL" IsReadOnly="true" SortMemberPath="total_pnl"
|
||||
Binding="{Binding total_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.total_pnl_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.total_pnl_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.total_pnl_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<DataGridTextColumn.ElementStyle>
|
||||
<Style TargetType="{x:Type TextBlock}">
|
||||
<Setter Property="TextAlignment" Value="Right" />
|
||||
<Setter Property="Background" Value="{Binding total_pnl, Converter={StaticResource PnLBackgroundColorConverter}}" />
|
||||
</Style>
|
||||
</DataGridTextColumn.ElementStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn x:Name="pnl_price" Header="PnL Price" IsReadOnly="true" SortMemberPath="pnl_price"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
@@ -107,24 +146,17 @@
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="pnl_price"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
<Binding Path="security"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
</DataGridTextColumn>
|
||||
<!-- we are hiding these until we have toggle on/off via settings
|
||||
<DataGridTextColumn x:Name="avg_buy" Header="AvgBuy" Binding="{Binding weighted_average_buy, Converter={StaticResource DoubleWithNaNHiddenConverter}}" IsReadOnly="true" SortMemberPath="weighted_average_buy"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.weighted_average_buy_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.weighted_average_buy_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.weighted_average_buy_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<!--
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="weighted_average_buy"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
-->
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn x:Name="avg_sell" Header="AvgSell" Binding="{Binding weighted_average_sell, Converter={StaticResource DoubleWithNaNHiddenConverter}}" IsReadOnly="true" SortMemberPath="weighted_average_sell"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
@@ -132,26 +164,19 @@
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.weighted_average_sell_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.weighted_average_sell_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DoubleMinMaxTemplate}">
|
||||
<!--
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="weighted_average_sell"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
-->
|
||||
</DataGridTextColumn>
|
||||
-->
|
||||
</DataGrid.Columns>
|
||||
</c:DataGridEx>
|
||||
<StatusBar Grid.Row="1" Grid.Column="0" DockPanel.Dock="Bottom">
|
||||
<StatusBar Grid.Row="2" Grid.Column="0" DockPanel.Dock="Bottom">
|
||||
<StatusBarItem DockPanel.Dock="Right">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Total PnL: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding total_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}" />
|
||||
<TextBlock Grid.Column="0" Text="Aggregate PnL: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding total_aggregate_pnl, Converter={StaticResource DoubleWithNaNHiddenConverter}}" />
|
||||
</Grid>
|
||||
</StatusBarItem>
|
||||
<StatusBarItem DockPanel.Dock="Right">
|
||||
@@ -163,8 +188,8 @@
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Total Net Pos: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding total_net_pos}"/>
|
||||
<TextBlock Grid.Column="0" Text="Aggregate Net Pos: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding total_aggregate_net_pos}"/>
|
||||
</Grid>
|
||||
</StatusBarItem>
|
||||
<StatusBarItem DockPanel.Dock="Right">
|
||||
@@ -176,8 +201,8 @@
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Total Sell Qty: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding total_sell_qty}"/>
|
||||
<TextBlock Grid.Column="0" Text="Aggregate Sell Qty: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding total_aggregate_sell_qty}"/>
|
||||
</Grid>
|
||||
</StatusBarItem>
|
||||
<StatusBarItem DockPanel.Dock="Right">
|
||||
@@ -189,8 +214,8 @@
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Total Buy Qty: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding total_buy_qty}"/>
|
||||
<TextBlock Grid.Column="0" Text="Aggregate Buy Qty: "/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding total_aggregate_buy_qty}"/>
|
||||
</Grid>
|
||||
</StatusBarItem>
|
||||
<StatusBarItem />
|
||||
|
||||
@@ -14,26 +14,31 @@ namespace mbts.spark {
|
||||
internal class PAndLWindow : SparkWindow<PAndLWindowFactory.PAndLStartupData>, ISharedWindow {
|
||||
|
||||
const string _window_type = "PAndL", PNL_THREAD_ID = "PnL";
|
||||
PAndLWindowFactory.PAndLStartupData _state;
|
||||
PAndLWindowFactory.PAndLStartupData _startup_data;
|
||||
PAndLModel _model;
|
||||
PAndLView _view;
|
||||
PAndLVM _vm;
|
||||
PAndLWindowFactory _factory;
|
||||
public PAndLWindow(PAndLModel model, PAndLWindowFactory factory, IBus bus, PAndLWindowFactory.PAndLStartupData state, ImageSource Icon ) : base("P&L", bus, factory, SparkCommands.ShowPAndL.icon) {
|
||||
public PAndLWindow(PAndLModel model, PAndLWindowFactory factory, IBus bus, PAndLWindowFactory.PAndLStartupData startup_data, ImageSource Icon ) : base("P&L", bus, factory, SparkCommands.ShowPAndL.icon) {
|
||||
_model = model;
|
||||
_factory = factory;
|
||||
_state = state;
|
||||
_startup_data = startup_data;
|
||||
}
|
||||
|
||||
public override string window_type => _window_type;
|
||||
public override PAndLWindowFactory.PAndLStartupData SaveState => _state =
|
||||
new PAndLWindowFactory.PAndLStartupData();
|
||||
public override PAndLWindowFactory.PAndLStartupData SaveState => _startup_data =
|
||||
new PAndLWindowFactory.PAndLStartupData(
|
||||
_vm.get_sorting_states(),
|
||||
_vm.get_filters_states(),
|
||||
_vm.selected_pnl_calc_type,
|
||||
_vm.start_of_day
|
||||
);
|
||||
public override UIElement Content => _view;
|
||||
public override double MinWidth => 894;
|
||||
public string thread_id => PNL_THREAD_ID;
|
||||
|
||||
public void thread_init() {
|
||||
_vm = new PAndLVM(_bus, _model, _state);
|
||||
_vm = new PAndLVM(_bus, _model, _startup_data);
|
||||
_view = new PAndLView(_vm);
|
||||
_view.DataContext = _vm;
|
||||
}
|
||||
|
||||
@@ -77,20 +77,31 @@ namespace mbts.spark {
|
||||
|
||||
class PnLCalculator {
|
||||
readonly IEnumerable<PnLData> _data;
|
||||
readonly pnl_calc_type _calc_type;
|
||||
readonly DateTime _start_of_trading_day_utc;
|
||||
|
||||
public PnLCalculator(IEnumerable<PnLData> data) {
|
||||
public PnLCalculator(IEnumerable<PnLData> data, pnl_calc_type calc_type, TimeSpan start_of_day_ts) {
|
||||
_data = data;
|
||||
_calc_type = calc_type;
|
||||
// Note that this isn't the acutal start of the trading day
|
||||
// but it's close enough for pnl purposes.
|
||||
// In reality, instruments have different schedules
|
||||
var _start_of_trading_day_local = DateTime.Now.Date.Add(start_of_day_ts);
|
||||
if (_start_of_trading_day_local >= DateTime.Now) {
|
||||
_start_of_trading_day_local = _start_of_trading_day_local.AddDays(-1);
|
||||
}
|
||||
_start_of_trading_day_utc = _start_of_trading_day_local.ToUniversalTime();
|
||||
}
|
||||
|
||||
public IEnumerable<PnLSummaryRow> get_summary_position() {
|
||||
return (from pnl_item in _data
|
||||
group pnl_item by (pnl_item.account, pnl_item.exchange, pnl_item.symbol, pnl_item.security, pnl_item.sec_md) into grouping
|
||||
let start_of_day = _get_start_of_day(grouping.Key.symbol)
|
||||
select new PnLSummaryRow(grouping.Key,
|
||||
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= start_of_day).Sum(y => y.buy_quantity),
|
||||
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= start_of_day).Sum(y => y.sell_quantity),
|
||||
grouping.Sum(y => y.buy_quantity - y.sell_quantity),
|
||||
grouping.Where(y => y.fill.Timestamp.ToDateTime() < start_of_day).Sum(y => y.buy_quantity - y.sell_quantity),
|
||||
group pnl_item by (pnl_item.account, pnl_item.exchange, pnl_item.symbol, pnl_item.security, pnl_item.sec_md, pnl_item.security_type) into grouping
|
||||
/* let start_of_day = _get_start_of_day(grouping.Key.symbol) */
|
||||
select new PnLSummaryRow(_calc_type, _start_of_trading_day_utc, grouping.Key,
|
||||
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= _start_of_trading_day_utc).Sum(y => (uint)y.buy_quantity),
|
||||
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= _start_of_trading_day_utc).Sum(y => (uint)y.sell_quantity),
|
||||
grouping.Sum(y => (int)(y.buy_quantity - y.sell_quantity)),
|
||||
grouping.Where(y => y.fill.Timestamp.ToDateTime() < _start_of_trading_day_utc).Sum(y => (int)(y.buy_quantity - y.sell_quantity)),
|
||||
grouping.weighted_average(y => y.fill.LastPrice, y => y.buy_quantity),
|
||||
grouping.weighted_average(y => y.fill.LastPrice, y => y.sell_quantity),
|
||||
grouping.Select(i => i.fill)
|
||||
@@ -98,21 +109,26 @@ namespace mbts.spark {
|
||||
);
|
||||
}
|
||||
|
||||
public static double get_driver(SecurityMD sec_md, pnl_calc_type pnl_request) {
|
||||
ulong unused = 0;
|
||||
sec_md.md.get_bbo(ref unused, out NativePriceSize bid, out NativePriceSize ask);
|
||||
sec_md.md.get_last_trade(ref unused, out NativePriceSize last, out DateTime unused1, out ulong unused2);
|
||||
public static bool get_driver(SecurityMD sec_md, pnl_calc_type pnl_request, ref double driver_price) {
|
||||
// we always want to pass in 0 to the md query so that we don't get back NaN if the market
|
||||
// data hasn't changed between query periods
|
||||
ulong bbo_time = 0;
|
||||
ulong last_trade_time = 0;
|
||||
if (!sec_md.md.get_bbo(ref bbo_time, out NativePriceSize bid, out NativePriceSize ask)) return false;
|
||||
if (!sec_md.md.get_last_trade(ref last_trade_time, out NativePriceSize last, out DateTime unused1, out ulong unused2)) return false;
|
||||
|
||||
if (pnl_request.HasFlag(pnl_calc_type.waterfall)) {
|
||||
// last price is available and withing range of available bid and ask
|
||||
if (!Double.IsNaN(last.price) && last.price >= bid.price && last.price <= ask.price) {
|
||||
return last.price;
|
||||
driver_price = last.price;
|
||||
return true;
|
||||
}
|
||||
|
||||
double mid = TickUtils.mid_tick(sec_md.security.ContractSpec, bid.price, ask.price);
|
||||
// last price is not valid, so try the mid
|
||||
if (!Double.IsNaN(mid)) {
|
||||
return mid;
|
||||
driver_price = mid;
|
||||
return true;
|
||||
}
|
||||
|
||||
// last bid
|
||||
@@ -120,9 +136,11 @@ namespace mbts.spark {
|
||||
if (!Double.IsNaN(last.price) && !Double.IsNaN(bid.price) && Double.IsNaN(ask.price)) {
|
||||
// if last >= bid then last else bid
|
||||
if (last.price >= bid.price) {
|
||||
return last.price;
|
||||
driver_price = last.price;
|
||||
return true;
|
||||
} else {
|
||||
return bid.price;
|
||||
driver_price = bid.price;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,15 +149,18 @@ namespace mbts.spark {
|
||||
if (!Double.IsNaN(last.price) && !Double.IsNaN(ask.price) && Double.IsNaN(bid.price)) {
|
||||
// if last <= ask then last else ask
|
||||
if (last.price <= ask.price) {
|
||||
return last.price;
|
||||
driver_price = last.price;
|
||||
return true;
|
||||
} else {
|
||||
return ask.price;
|
||||
driver_price = ask.price;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// last only
|
||||
if (!Double.IsNaN(last.price)) {
|
||||
return last.price;
|
||||
driver_price = last.price;
|
||||
return true;
|
||||
}
|
||||
|
||||
// indicative, no support yet
|
||||
@@ -148,36 +169,43 @@ namespace mbts.spark {
|
||||
if ((!Double.IsNaN(bid.price) || !Double.IsNaN(ask.price)) && !Double.IsNaN(sec_md.md.settlement_price)) {
|
||||
// if bid and no ask and settle and bid > Settle
|
||||
if (!Double.IsNaN(bid.price) && Double.IsNaN(ask.price) && !Double.IsNaN(sec_md.md.settlement_price) && bid.price > sec_md.md.settlement_price) {
|
||||
return bid.price;
|
||||
driver_price = bid.price;
|
||||
return true;
|
||||
}
|
||||
// if ask and no bid and settle and ask < Settle
|
||||
if (!Double.IsNaN(ask.price) && Double.IsNaN(bid.price) && !Double.IsNaN(sec_md.md.settlement_price) && ask.price < sec_md.md.settlement_price) {
|
||||
return ask.price;
|
||||
driver_price = ask.price;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Settlement
|
||||
// neither bid or ask are available
|
||||
if (!Double.IsNaN(sec_md.md.settlement_price)) {
|
||||
return sec_md.md.settlement_price;
|
||||
driver_price = sec_md.md.settlement_price;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Close
|
||||
return sec_md.md.close_price;
|
||||
driver_price = sec_md.md.close_price;
|
||||
return true;
|
||||
|
||||
} // end waterfall
|
||||
|
||||
if (pnl_request.HasFlag(pnl_calc_type.last)) return last.price;
|
||||
if (pnl_request.HasFlag(pnl_calc_type.mid)) return TickUtils.mid_tick(sec_md.security.ContractSpec, bid.price, ask.price);
|
||||
if (pnl_request.HasFlag(pnl_calc_type.open)) return sec_md.md.open_price;
|
||||
if (pnl_request.HasFlag(pnl_calc_type.close)) return sec_md.md.close_price;
|
||||
if (pnl_request.HasFlag(pnl_calc_type.settle)) return sec_md.md.settlement_price;
|
||||
return Double.NaN;
|
||||
if (pnl_request.HasFlag(pnl_calc_type.last)) { driver_price = last.price; return true; }
|
||||
// note that bid_or_ask is not considered here since this method only
|
||||
// supports returning a single driver price; see update_pnl
|
||||
if (pnl_request.HasFlag(pnl_calc_type.midpoint)) { driver_price = (bid.price + ask.price) / 2.0; return true; }
|
||||
if (pnl_request.HasFlag(pnl_calc_type.open)) { driver_price = sec_md.md.open_price; return true; }
|
||||
if (pnl_request.HasFlag(pnl_calc_type.close)) { driver_price = sec_md.md.close_price; return true; }
|
||||
if (pnl_request.HasFlag(pnl_calc_type.settle)) { driver_price = sec_md.md.settlement_price; return true; }
|
||||
driver_price = Double.NaN;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static double get_pnl(double bid_for_long, double ask_for_short, FillImmutable fill, double cpp) {
|
||||
if (fill.Side == Side.Buy) { return _get_long_pnl(bid_for_long, fill.LastPrice, fill.LastShares, cpp); }
|
||||
if (fill.Side == Side.Sell) { return _get_short_pnl(ask_for_short, fill.LastPrice, fill.LastShares, cpp); }
|
||||
if (fill.Side == Side.Buy && !Double.IsNaN(bid_for_long)) { return _get_long_pnl(bid_for_long, fill.LastPrice, fill.LastShares, cpp); }
|
||||
if (fill.Side == Side.Sell && !Double.IsNaN(ask_for_short)) { return _get_short_pnl(ask_for_short, fill.LastPrice, fill.LastShares, cpp); }
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
@@ -197,10 +225,6 @@ namespace mbts.spark {
|
||||
return (price - driver) * quantity * cpp;
|
||||
}
|
||||
|
||||
private static DateTime _get_start_of_day(string symbool) {
|
||||
// I rekcon this will change according to the symbol's session
|
||||
return DateTime.Today;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace mbts.spark {
|
||||
|
||||
[Flags]
|
||||
public enum pnl_calc_type {
|
||||
none = 0,
|
||||
[Description("Last Tradeable Price Avalanche")]
|
||||
waterfall = 1,
|
||||
[Description("Last")]
|
||||
last = 2,
|
||||
bid_and_ask = 4,
|
||||
mid = 8,
|
||||
[Description("Bid/Ask")]
|
||||
bid_or_ask = 4,
|
||||
[Description("Midpoint")]
|
||||
midpoint = 8,
|
||||
[Description("Open")]
|
||||
open = 16,
|
||||
[Description("Close")]
|
||||
close = 32,
|
||||
[Description("Settle")]
|
||||
settle = 64
|
||||
}
|
||||
|
||||
@@ -34,6 +42,7 @@ namespace mbts.spark {
|
||||
public SecurityImmutable? security => sec_md?.security;
|
||||
public double buy_quantity => (fill.Side == Side.Buy) ? fill.LastShares : 0;
|
||||
public double sell_quantity => (fill.Side == Side.Sell) ? fill.LastShares : 0;
|
||||
public SecurityType? security_type => security?.security_type();
|
||||
|
||||
public override string ToString() {
|
||||
return $"Fill: ( {account} {exchange} {security.ToString()} buy: {buy_quantity} sell: {sell_quantity} ) Security: {security.ToString()}";
|
||||
|
||||
@@ -12,70 +12,106 @@ namespace mbts.spark {
|
||||
|
||||
|
||||
internal class PnLSummaryRow : ViewModelBase, IEquatable<PnLSummaryRow> {
|
||||
(string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md) key;
|
||||
double _buy_quantity, _sell_quantity, _net_position, _sod_position, _pnl, _pnl_price, _weighted_average_buy, _weighted_average_sell;
|
||||
DateTime _start_of_day;
|
||||
(string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md, SecurityType? sec_type) key;
|
||||
int _start_net_position, _total_net_position;
|
||||
uint _day_long_quantity, _day_short_quantity;
|
||||
double _total_pnl, _sod_pnl, _start_pnl, _pnl_price, _weighted_average_buy, _weighted_average_sell;
|
||||
IEnumerable<FillImmutable> _fills;
|
||||
//List<MarketSchedule> _schedules = new List<MarketSchedule>() { MarketSchedule.es_schedule };
|
||||
double _driver;
|
||||
static readonly Dictionary<string, double> cpp_dict = config.Configuration.get<bts.config.CppConfig>().cpp_entry;
|
||||
|
||||
|
||||
|
||||
public PnLSummaryRow((string, Venue?, string, SecurityImmutable?, SecurityMD?) key,
|
||||
double buy_quant, double sell_quant, double net_pos, double sod_pos, double wavg_buy, double wavg_sell, IEnumerable<FillImmutable> fills) {
|
||||
public PnLSummaryRow(pnl_calc_type calc_type,
|
||||
DateTime start_of_day,
|
||||
(string, Venue?, string, SecurityImmutable?, SecurityMD?, SecurityType?) key,
|
||||
uint day_buy_quant, uint day_sell_quant, int net_pos, int sod_pos, double wavg_buy, double wavg_sell, IEnumerable<FillImmutable> fills) {
|
||||
this._start_of_day = start_of_day;
|
||||
this.key = key;
|
||||
buy_quantity = buy_quant;
|
||||
sell_quantity = sell_quant;
|
||||
net_position = net_pos;
|
||||
sod_position = sod_pos;
|
||||
day_long_quantity = day_buy_quant;
|
||||
day_short_quantity = day_sell_quant;
|
||||
total_net_position = net_pos;
|
||||
start_net_position = sod_pos;
|
||||
weighted_average_buy = wavg_buy;
|
||||
weighted_average_sell = wavg_sell;
|
||||
_fills = fills;
|
||||
|
||||
// set the initial pnl and pnl_price when there has been a
|
||||
// position update i.e. this constructor
|
||||
update_pnl(pnl_calc_type.waterfall);
|
||||
update_pnl(calc_type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the cash per point (cpp) for the given SecurityImmutable.
|
||||
/// If the SecurityImmutable is a cross-product exchange-traded spread,
|
||||
/// such as a crack spread, where the legs have differing cpps, then
|
||||
/// we'll return Double.Nan;
|
||||
/// </summary>
|
||||
/// <param name="s">The SecurityImmutable for which we will look up a cpp</param>
|
||||
/// <returns>The found cpp, else Double.NaN</returns>
|
||||
private double _get_cpp_for_security(SecurityImmutable s) {
|
||||
if (s.security_type() == SecurityType.Spread) {
|
||||
var distinct_cpps = s.Spread.Legs
|
||||
.Select(l => l.Security.ContractSpec.CashPerPoint)
|
||||
.Select(l => get_cpp_for_symbol(l.Security))
|
||||
.Distinct();
|
||||
bool all_legs_have_same_cpp = distinct_cpps.Count() < 2;
|
||||
if (all_legs_have_same_cpp) {
|
||||
return s.Spread.Legs.First().Security.ContractSpec.CashPerPoint;
|
||||
} else {
|
||||
Logger.error(bts.log.Subsystem.Ui,
|
||||
$"Security {s} contains legs with the following cash multipliers: { string.Join(",", distinct_cpps.Select(d => d.ToString())) } ");
|
||||
return Double.NaN;
|
||||
return all_legs_have_same_cpp ? distinct_cpps.First() : Double.NaN;
|
||||
} else {
|
||||
return get_cpp_for_symbol(s);
|
||||
}
|
||||
|
||||
// local function for looking up cpp for a single SecurityImmutable
|
||||
double get_cpp_for_symbol(SecurityImmutable si) {
|
||||
if (cpp_dict.TryGetValue(si.Symbol, out double cpp_out)) {
|
||||
return cpp_out;
|
||||
}
|
||||
return double.NaN;
|
||||
}
|
||||
return s.ContractSpec.CashPerPoint;
|
||||
}
|
||||
|
||||
|
||||
public void update_pnl(pnl_calc_type pnl_request) {
|
||||
if (key.sec_md.HasValue) {
|
||||
double cpp = _get_cpp_for_security(key.security.Value);
|
||||
if (pnl_request.HasFlag(pnl_calc_type.bid_and_ask)) {
|
||||
ulong unused = 0;
|
||||
key.sec_md.Value.md.get_bbo(ref unused, out NativePriceSize bid, out NativePriceSize ask);
|
||||
if (pnl_request.HasFlag(pnl_calc_type.bid_or_ask)) {
|
||||
ulong bbo_ref = 0;
|
||||
key.sec_md.Value.md.get_bbo(ref bbo_ref, out NativePriceSize bid, out NativePriceSize ask);
|
||||
|
||||
double summary_pnl = 0.0;
|
||||
double start_pnl_total = 0.0;
|
||||
double day_pnl_total = 0.0;
|
||||
foreach (var fill in _fills) {
|
||||
double _tick_pnl = PnLCalculator.get_pnl(bid.price, ask.price, fill, cpp);
|
||||
summary_pnl += _tick_pnl;
|
||||
if (fill.Timestamp.ToDateTime() >= _start_of_day) {
|
||||
day_pnl_total += _tick_pnl;
|
||||
} else {
|
||||
start_pnl_total += _tick_pnl;
|
||||
}
|
||||
}
|
||||
pnl = summary_pnl;
|
||||
total_pnl = summary_pnl;
|
||||
day_pnl = day_pnl_total;
|
||||
start_pnl = start_pnl_total;
|
||||
pnl_price = Double.NaN;
|
||||
} else {
|
||||
double driver = PnLCalculator.get_driver(key.sec_md.Value, pnl_request);
|
||||
|
||||
double summary_pnl = 0.0;
|
||||
foreach (var fill in _fills) {
|
||||
double _tick_pnl = PnLCalculator.get_pnl(driver, fill, cpp);
|
||||
summary_pnl += _tick_pnl;
|
||||
if (PnLCalculator.get_driver(key.sec_md.Value, pnl_request, ref _driver)) {
|
||||
double summary_pnl = 0.0;
|
||||
double start_pnl_total = 0.0;
|
||||
double day_pnl_total = 0.0;
|
||||
foreach (var fill in _fills) {
|
||||
double _tick_pnl = PnLCalculator.get_pnl(_driver, fill, cpp);
|
||||
summary_pnl += _tick_pnl;
|
||||
if (fill.Timestamp.ToDateTime() >= _start_of_day) {
|
||||
day_pnl_total += _tick_pnl;
|
||||
} else {
|
||||
start_pnl_total += _tick_pnl;
|
||||
}
|
||||
}
|
||||
total_pnl = summary_pnl;
|
||||
day_pnl = day_pnl_total;
|
||||
start_pnl = start_pnl_total;
|
||||
pnl_price = _driver;
|
||||
}
|
||||
pnl = summary_pnl;
|
||||
pnl_price = driver;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,9 +125,9 @@ namespace mbts.spark {
|
||||
key.exchange == other.exchange &&
|
||||
key.security == other.security &&
|
||||
key.symbol == other.symbol &&
|
||||
buy_quantity == other.buy_quantity &&
|
||||
sell_quantity == other.sell_quantity &&
|
||||
pnl == other.pnl;
|
||||
day_long_quantity == other.day_long_quantity &&
|
||||
day_short_quantity == other.day_short_quantity &&
|
||||
total_pnl == other.total_pnl;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
@@ -104,7 +140,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"account: {account} exchange: {exchange_string} symbol: {symbol} security: {security_text} buy_quantity: {buy_quantity} sell_quantity: {sell_quantity} pnl: {pnl}";
|
||||
return $"account: {account} exchange: {exchange_string} symbol: {symbol} security: {security_text} buy_quantity: {day_long_quantity} sell_quantity: {day_short_quantity} pnl: {total_pnl}";
|
||||
}
|
||||
|
||||
public string account {
|
||||
@@ -134,62 +170,83 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public SecurityImmutable? security => key.security;
|
||||
public SecurityType security_type => key.sec_type.GetValueOrDefault();
|
||||
|
||||
public string security_text {
|
||||
get => security.HasValue ? security.Value.ToString() : "Unknown Security";
|
||||
set => ignore();
|
||||
}
|
||||
|
||||
|
||||
public double buy_quantity {
|
||||
get => _buy_quantity;
|
||||
|
||||
public int start_net_position {
|
||||
get => _start_net_position;
|
||||
set {
|
||||
if (_buy_quantity != value) {
|
||||
_buy_quantity = value;
|
||||
RaisePropertyChanged(nameof(buy_quantity));
|
||||
if (_start_net_position != value) {
|
||||
_start_net_position = value;
|
||||
RaisePropertyChanged(nameof(start_net_position));
|
||||
}
|
||||
}
|
||||
}
|
||||
public double start_pnl {
|
||||
get => _start_pnl;
|
||||
set {
|
||||
if (_start_pnl != value) {
|
||||
_start_pnl = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
public uint day_long_quantity {
|
||||
get => _day_long_quantity;
|
||||
set {
|
||||
if (_day_long_quantity != value) {
|
||||
_day_long_quantity = value;
|
||||
RaisePropertyChanged(nameof(day_long_quantity));
|
||||
}
|
||||
}
|
||||
}
|
||||
public uint day_short_quantity {
|
||||
get => _day_short_quantity;
|
||||
set {
|
||||
if (_day_short_quantity != value) {
|
||||
_day_short_quantity = value;
|
||||
RaisePropertyChanged(nameof(day_short_quantity));
|
||||
}
|
||||
}
|
||||
}
|
||||
public double day_pnl {
|
||||
get => _sod_pnl;
|
||||
set {
|
||||
if (_sod_pnl != value) {
|
||||
_sod_pnl = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double sell_quantity {
|
||||
get => _sell_quantity;
|
||||
|
||||
public int total_net_position {
|
||||
get => _total_net_position;
|
||||
set {
|
||||
if (_sell_quantity != value) {
|
||||
_sell_quantity = value;
|
||||
RaisePropertyChanged(nameof(sell_quantity));
|
||||
if (_total_net_position != value) {
|
||||
_total_net_position = value;
|
||||
RaisePropertyChanged(nameof(total_net_position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double sod_position {
|
||||
get => _sod_position;
|
||||
public double total_pnl {
|
||||
get => _total_pnl;
|
||||
set {
|
||||
if (_sod_position != value) {
|
||||
_sod_position = value;
|
||||
RaisePropertyChanged(nameof(sod_position));
|
||||
if (_total_pnl != value) {
|
||||
_total_pnl = value;
|
||||
RaisePropertyChanged(nameof(total_pnl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double net_position {
|
||||
get => _net_position;
|
||||
set {
|
||||
if (_net_position != value) {
|
||||
_net_position = value;
|
||||
RaisePropertyChanged(nameof(net_position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double pnl {
|
||||
get => _pnl;
|
||||
set {
|
||||
if (_pnl != value) {
|
||||
_pnl = value;
|
||||
RaisePropertyChanged(nameof(pnl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public double pnl_price {
|
||||
get => _pnl_price;
|
||||
@@ -221,6 +278,9 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
//public IEnumerable<FillImmutable> sod_fills => _fills.Where(f => f.Timestamp.ToDateTime() >= _start_of_day);
|
||||
//public IEnumerable<FillImmutable> pos_fills => _fills.Where(f => f.Timestamp.ToDateTime() < _start_of_day);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace mbts.spark {
|
||||
IOrderBookCell current { get; }
|
||||
//
|
||||
IOrderBookCellStore select_other(CVOrderBookRowVM row);
|
||||
bool refresh();
|
||||
bool refresh(bool force_refresh);
|
||||
void regenerate_cell();
|
||||
}
|
||||
|
||||
@@ -93,9 +93,9 @@ namespace mbts.spark {
|
||||
public IOrderBookCellStore<T> select_other(CVOrderBookRowVM row) => _select_other(row);
|
||||
IOrderBookCellStore IOrderBookCellStore.select_other(CVOrderBookRowVM row) => select_other(row);
|
||||
|
||||
public bool refresh() {
|
||||
public bool refresh(bool force_refresh = false) {
|
||||
T v = _get_value(_row);
|
||||
if (!_eq_comparer.Equals(v, _current.value)) {
|
||||
if (force_refresh || !_eq_comparer.Equals(v, _current.value)) {
|
||||
_current = new OrderBookCell<T>(this, v);
|
||||
_row.RaisePropertyChanged(_prop_name);
|
||||
return true;
|
||||
|
||||
@@ -54,6 +54,19 @@ namespace mbts.spark {
|
||||
EqualityComparer.Create<SecurityImmutable, uint>(s => s.Id),
|
||||
SecurityHelper.Comparer.InstanceImmutable
|
||||
)));
|
||||
// symbol
|
||||
Add(symbol_column = new SimpleTextColumn<string[]>(
|
||||
Columns.Symbol,
|
||||
DefaultColumnHeaders.Symbol,
|
||||
nameof(CVOrderBookRowVM.symbol),
|
||||
r => r.symbol.value,
|
||||
DefaultColumnWidths.Symbol,
|
||||
DiscreteValuesFilterVM.Create<CVOrderBookRowVM, string>(
|
||||
mbts.co2.UserSymbol.get_all().Select(u => u.symbol).Distinct().ToList(),
|
||||
r => r.symbol.value,
|
||||
value_path(nameof(CVOrderBookRowVM.symbol))
|
||||
),
|
||||
DataGridFilterKnownTemplates.DiscreteValues));
|
||||
// time_created
|
||||
Add(time_created_column = new TimeCreatedColumn(
|
||||
new TimeFilterVM<CVOrderBookRowVM>(r => r.time_created.value, value_path(nameof(OrderBookRowVM.time_created)))));
|
||||
@@ -213,7 +226,7 @@ namespace mbts.spark {
|
||||
filled_qty_column.filter.load_state(fs.qty_filled);
|
||||
side_column.filter.load_state(fs.sides);
|
||||
order_type_column.filter.load_state(fs.order_types);
|
||||
// TODO: add "sub_type column.filter" data to FilterStates
|
||||
sub_type_column.filter.load_state(fs.order_sub_types);
|
||||
time_in_force_column.filter.load_state(fs.tifs);
|
||||
exchange_column.filter.load_state(fs.exchanges);
|
||||
|
||||
@@ -223,6 +236,7 @@ namespace mbts.spark {
|
||||
status_column.filter.load_state(fs.statuses);
|
||||
last_message_column.filter.load_state(fs.exchange_message_regex);
|
||||
last_change_column.filter.load_state(fs.last_change);
|
||||
symbol_column.filter.load_state(fs.symbols);
|
||||
}
|
||||
if (startup_data?.sort_criteria is List<SortingCriteria> lsc) {
|
||||
foreach (var sc in lsc)
|
||||
@@ -238,6 +252,7 @@ namespace mbts.spark {
|
||||
public event Action filter_changed;
|
||||
|
||||
public SecurityColumn security_column { get; }
|
||||
public SimpleTextColumn<string[]> symbol_column { get; }
|
||||
public TimeCreatedColumn time_created_column { get; }
|
||||
public LastChangeTimeColumn last_change_column { get; }
|
||||
public SimpleTextColumn<string[]> user_column { get; }
|
||||
@@ -269,6 +284,7 @@ namespace mbts.spark {
|
||||
exchanges = exchange_column.filter.get_state(),
|
||||
tifs = time_in_force_column.filter.get_state(),
|
||||
order_types = order_type_column.filter.get_state(),
|
||||
order_sub_types = sub_type_column.filter.get_state(),
|
||||
sides = side_column.filter.get_state(),
|
||||
qty = qty_column.filter.get_state(),
|
||||
price = price_column.filter.get_state(),
|
||||
@@ -278,7 +294,8 @@ namespace mbts.spark {
|
||||
statuses = status_column.filter.get_state(),
|
||||
exchange_message_regex = last_message_column.filter.get_state(),
|
||||
last_change = last_change_column.filter.get_state(),
|
||||
security_criteria = security_column.filter.get_state()
|
||||
security_criteria = security_column.filter.get_state(),
|
||||
symbols = symbol_column.filter.get_state()
|
||||
};
|
||||
|
||||
public void Dispose() {
|
||||
|
||||
@@ -12,6 +12,8 @@ using OrderStatus = bts.api.orderman.OrderStatus;
|
||||
|
||||
namespace mbts.spark {
|
||||
partial class CVOrderBookRowVM : DisposableViewModelBase, IEquatable<CVOrderBookRowVM>, ui.controls.DIYGrid.IRowVM, ui.controls.DIYGrid.IActionableRowVM {
|
||||
const string _unknown_security_price_format_string = "0.#########";
|
||||
const int _unknown_security_price_num_fractional_digits = 9;
|
||||
readonly CVOrderBookVM _book_vm;
|
||||
readonly OrdersModel _model;
|
||||
readonly Property<bool> _visible, _expanded, _passes_filter;
|
||||
@@ -29,6 +31,7 @@ namespace mbts.spark {
|
||||
IOrderBookCellStore<uint> _security_key_store;
|
||||
IOrderBookCellStore<SecurityImmutable?> _security_store;
|
||||
IOrderBookCellStore<Venue> _exchange_store;
|
||||
IOrderBookCellStore<string> _symbol_store;
|
||||
// Def
|
||||
IOrderBookCellStore<CustomOrderDefinition.CustomOneofCase> _order_type_store;
|
||||
IOrderBookCellStore<Side> _side_store;
|
||||
@@ -57,6 +60,7 @@ namespace mbts.spark {
|
||||
_security_key_store = CellFactory.create(this, _book_vm.columns.security_key_column, nameof(security_key), r => r._order.security_key(), r => r._security_key_store);
|
||||
_parent_order_key_store = CellFactory.create(this, _book_vm.columns.parent_key_column, nameof(parent_order_key), r => r._order.ParentKey, r => r._parent_order_key_store);
|
||||
_order_key_store = CellFactory.create(this, _book_vm.columns.order_key_column, nameof(order_key), r => r._order.Key, r => r._order_key_store);
|
||||
_symbol_store = CellFactory.create(this, _book_vm.columns.symbol_column, nameof(symbol), r => r._security.HasValue ? _security.Value.Symbol : "unknown symbol", r => r._symbol_store);
|
||||
_time_created_store = CellFactory.create(this, _book_vm.columns.time_created_column, nameof(time_created), r => r._order.created(), r => r._time_created_store, format_date);
|
||||
_user_store = CellFactory.create(this, _book_vm.columns.user_column, nameof(user), r => _order.user(), r => r._user_store);
|
||||
_account_store = CellFactory.create(this, _book_vm.columns.account_column, nameof(account), r => r._order.account(), r => r._account_store);
|
||||
@@ -74,7 +78,8 @@ namespace mbts.spark {
|
||||
_status_store = CellFactory.create(this, _book_vm.columns.status_column, nameof(status), r => _order.status(), r => r._status_store);
|
||||
_last_msg_store = CellFactory.create(this, _book_vm.columns.last_message_column, nameof(last_msg), r => _order.last_msg(), r => r._last_msg_store);
|
||||
//
|
||||
string format_price(double v) => _security.HasValue ? _security.Value.ContractSpec.format(v) : v.ToString();
|
||||
string format_price(double v) => _security.HasValue ? _security.Value.ContractSpec.format(v) :
|
||||
Math.Round(v, _unknown_security_price_num_fractional_digits, MidpointRounding.AwayFromZero).ToString(_unknown_security_price_format_string);
|
||||
string format_date(DateTime v) => $"{v:M/d} {v:h:mm:ss.fff tt}";
|
||||
string format_sub_type(OrderSubType v) {
|
||||
switch (v) {
|
||||
@@ -85,7 +90,7 @@ namespace mbts.spark {
|
||||
}
|
||||
//
|
||||
_visible = backing(nameof(is_visible), true);
|
||||
_expanded = backing(nameof(is_expanded), true);
|
||||
_expanded = backing(nameof(is_expanded), false);
|
||||
_depth_level = backing(nameof(depth_level), () => {
|
||||
int i = 0;
|
||||
var curr = this;
|
||||
@@ -117,6 +122,7 @@ namespace mbts.spark {
|
||||
public IOrderBookCell<uint> security_key => _security_key_store.current;
|
||||
public IOrderBookCell<SecurityImmutable?> security => _security_store.current;
|
||||
public IOrderBookCell<Venue> exchange => _exchange_store.current;
|
||||
public IOrderBookCell<string> symbol => _symbol_store.current;
|
||||
// Def
|
||||
public IOrderBookCell<CustomOrderDefinition.CustomOneofCase> order_type => _order_type_store.current;
|
||||
public IOrderBookCell<Side> side => _side_store.current;
|
||||
@@ -187,6 +193,7 @@ namespace mbts.spark {
|
||||
yield return _price_store;
|
||||
yield return _avg_price_store;
|
||||
yield return _last_price_store;
|
||||
yield return _symbol_store;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,6 +286,7 @@ namespace mbts.spark {
|
||||
case Columns.ParentKey: return row._parent_order_key_store;
|
||||
case Columns.SecurityKey: return row._security_key_store;
|
||||
case Columns.Security: return row._security_store;
|
||||
case Columns.Symbol: return row._symbol_store;
|
||||
case Columns.Exchange: return row._exchange_store;
|
||||
case Columns.OrderType: return row._order_type_store;
|
||||
case Columns.Side: return row._side_store;
|
||||
@@ -319,7 +327,11 @@ namespace mbts.spark {
|
||||
void _update_partial(IEnumerable<IOrderBookCellStore> target_cell_stores) {
|
||||
bool invalidate = false;
|
||||
foreach (var cell_store in target_cell_stores) {
|
||||
if (cell_store.refresh()) {
|
||||
// although the price-related stores will be inlcuded in the IEnumerable,
|
||||
// we need to force .refresh b/c the price* backing store's value does not
|
||||
// change when the security becomes known
|
||||
bool force_refresh = cell_store.prop_name.Equals(nameof(price)) || cell_store.prop_name.Equals(nameof(avg_price)) || cell_store.prop_name.Equals(nameof(last_price));
|
||||
if (cell_store.refresh(force_refresh)) {
|
||||
invalidate = true;
|
||||
if (_book_vm.live_sorting_columns.Contains(cell_store.column.key)) {
|
||||
// refresh the sort by calling regenerate_cell() for each descendent-row
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace mbts.spark {
|
||||
SetMap<uint, uint> _parent_to_children = new SetMap<uint, uint>();
|
||||
SetMap<uint, uint> _security_orders = new SetMap<uint, uint>();
|
||||
|
||||
bool _expand_collapse_all_status = false;
|
||||
|
||||
public CVOrderBookVM(IBus bus, OrdersModel model, OrderBookStartupData state) {
|
||||
columns = new CVOrderBookColumnCollection(bus, model, state, _order_securities_subject);
|
||||
foreach (ColumnBase col in columns)
|
||||
@@ -69,6 +71,15 @@ namespace mbts.spark {
|
||||
SetMap<uint, uint> missing_security_orders = new SetMap<uint, uint>();
|
||||
IReadOnlyDictionary<uint, SecurityMD> security_dict = new Dictionary<uint, SecurityMD>();
|
||||
|
||||
expand_collapse_all_rows_command = new DelegateCommand(
|
||||
() => {
|
||||
_order_rows.Where(r => r.is_visible && r.passes_filter).ForEach(r =>
|
||||
r.is_expanded = !_expand_collapse_all_status
|
||||
);
|
||||
_expand_collapse_all_status = !_expand_collapse_all_status;
|
||||
}
|
||||
);
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(on_securities),
|
||||
model.on_orders.OnDispatcher().Subscribe(on_orders),
|
||||
@@ -232,6 +243,8 @@ namespace mbts.spark {
|
||||
public CVOrderBookRowVM current_row { get => _current_row.Value; set => _current_row.Value = value ?? _selected_rows.FirstOrDefault(); }
|
||||
public OrderRecordImmutable? current_order => current_row?.order;
|
||||
public IEnumerable<OrderRecordImmutable> selected_orders => _selected_rows.Select(r => r.order);
|
||||
|
||||
public System.Windows.Input.ICommand expand_collapse_all_rows_command { get; }
|
||||
void IOrderBookVM.on_sorting(DIYGridSortingEventArgs e) { }
|
||||
|
||||
public void row_selection_changed(SelectionChangedEventArgs e) {
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using static bts.shell.OrderBookWindowFactory;
|
||||
using static mbts.spark.OrderBookView;
|
||||
using static mbts.ui.controls.DIYGrid;
|
||||
@@ -427,6 +428,9 @@ namespace mbts.spark {
|
||||
public ObservableCollection<OrderBookRowVM> selected_rows => _selected_rows;
|
||||
public IEnumerable<OrderRecordImmutable> selected_orders => _selected_rows.Select(r => r.order);
|
||||
|
||||
public ICommand expand_collapse_all_rows_command => throw new NotImplementedException();
|
||||
|
||||
|
||||
#region Filters
|
||||
public IDataGridColumnPersistentFilter<OrderBookRowVM, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<OrderBookRowVM>(r => r.price, nameof(OrderBookRowVM.price));
|
||||
public IDataGridColumnPersistentFilter<OrderBookRowVM, UIntMinMaxState> qty_filter { get; } = new UIntMinMax<OrderBookRowVM>(r => r.quantity, nameof(OrderBookRowVM.quantity));
|
||||
@@ -448,7 +452,6 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<OrderBookRowVM, string> exchange_message_filter { get; }
|
||||
public IDataGridColumnPersistentFilter<OrderBookRowVM, string> security_filter { get; } = new RegexFilterVM<OrderBookRowVM>(r => r.security_text, nameof(OrderBookRowVM.security_text));
|
||||
|
||||
|
||||
public FilterStates get_filter_states() =>
|
||||
new FilterStates {
|
||||
order_key_regex = order_key_filter.get_state(),
|
||||
|
||||
@@ -17,10 +17,11 @@ namespace mbts.spark {
|
||||
void on_sorting(DIYGridSortingEventArgs e);
|
||||
List<SortingCriteria> get_sorting_states();
|
||||
OrderBookWindowFactory.FilterStates get_filter_states();
|
||||
System.Windows.Input.ICommand expand_collapse_all_rows_command { get; }
|
||||
}
|
||||
|
||||
internal partial class OrderBookView : UserControl {
|
||||
public enum Columns { Security, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey }
|
||||
public enum Columns { Security, Time, LastChange, User, Account, Exchange, TimeInForce, OrderType, SubType, Side, Size, Price, Filled, AvgFilled, LastPrice, Status, LastMessage, SecurityKey, OrderKey, ParentKey, Symbol }
|
||||
public const string
|
||||
SubTypeFilterTemplateKey = "SubTypeFilterTemplate",
|
||||
ExpandCollapseToggleStyleKey = "ExpandCollapseToggleStyle",
|
||||
|
||||
@@ -27,7 +27,9 @@ namespace mbts.spark {
|
||||
, _cancel_buy_orders_command
|
||||
, _cancel_sell_orders_command
|
||||
, _cancel_all_orders_command
|
||||
, _cancel_selected_orders_command;
|
||||
, _cancel_selected_orders_command
|
||||
, _recreate_selected_orders_command
|
||||
, _toggle_collapse_expand_all_command;
|
||||
|
||||
|
||||
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon) : base("Order Book", bus, factory, SparkCommands.ShowOrders.icon) {
|
||||
@@ -107,21 +109,41 @@ namespace mbts.spark {
|
||||
can_execute: () => _vm.selected_orders.Any(x => x.status() == OrderStatus.Working)
|
||||
);
|
||||
|
||||
_recreate_selected_orders_command = SparkCommands.RecreateSelectedOrders.complete(
|
||||
() =>
|
||||
_vm.selected_orders.Where(x => _passes_recreate_search_criteria(x))
|
||||
.Select(x => (x.Def, x.State.Price, x.State.Quantity - x.State.FilledQuantity)),
|
||||
can_execute: () => _vm.selected_orders.Any(x => _passes_recreate_search_criteria(x))
|
||||
);
|
||||
|
||||
_toggle_collapse_expand_all_command = new SparkCommandCommand("Expand / Collapse All",
|
||||
Icons.UpArrow,
|
||||
_vm.expand_collapse_all_rows_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
|
||||
_cancel_all_orders_command,
|
||||
_recreate_selected_orders_command,
|
||||
_toggle_collapse_expand_all_command
|
||||
};
|
||||
_vm.selected_order_changed += _vm_selected_order_changed;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (e.HasValue) {
|
||||
_modify_order_command.raiseCanExecuteChanged();
|
||||
_cancel_selected_orders_command.raiseCanExecuteChanged();
|
||||
_recreate_selected_orders_command.raiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,4 +91,4 @@ namespace mbts.spark.utilities {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,10 +32,12 @@ namespace mbts.spark.utilities {
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Trigger,
|
||||
}, _spreader_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Spreader,
|
||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
||||
CustomOrderDefinition.CustomOneofCase.TimeBox
|
||||
CustomOrderDefinition.CustomOneofCase.TimeBox,
|
||||
CustomOrderDefinition.CustomOneofCase.Trigger,
|
||||
};
|
||||
readonly string _user;
|
||||
readonly AdminModel _admin_model;
|
||||
@@ -213,6 +215,12 @@ namespace mbts.spark.utilities {
|
||||
Child = custom_order(),
|
||||
TopThreshold = new QuantityMeasure(),
|
||||
Hedge = custom_order(),
|
||||
};
|
||||
public TriggerOrderDefinition trigger_order() => new TriggerOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
TriggerCondition = new TriggerCondition(),
|
||||
TriggerAction = new TriggerAction(),
|
||||
};
|
||||
|
||||
|
||||
@@ -231,6 +239,7 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.Lean: rv.Lean = lean_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: rv.Join = join_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: rv.Top = top_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: rv.Trigger = trigger_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = spreader_order(); break;
|
||||
}
|
||||
return rv;
|
||||
@@ -250,6 +259,7 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.Lean: return new OrderMetadata { KeyBinding = "L", Color = BTSColors.Orders.lean_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.top_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: goto default;
|
||||
case CustomOrderDefinition.CustomOneofCase.None: goto default;
|
||||
default: return _default_order_metadata;
|
||||
@@ -267,6 +277,7 @@ namespace mbts.spark.utilities {
|
||||
case OrderState.CustomOneofCase.Lean: return new OrderMetadata { KeyBinding = "L", Color = BTSColors.Orders.lean_order };
|
||||
case OrderState.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
|
||||
case OrderState.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
|
||||
case OrderState.CustomOneofCase.Trigger: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.top_order };
|
||||
case OrderState.CustomOneofCase.Spreader: goto default;
|
||||
case OrderState.CustomOneofCase.None: goto default;
|
||||
default: return _default_order_metadata;
|
||||
@@ -302,6 +313,7 @@ namespace mbts.spark.utilities {
|
||||
case nameof(LeanOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Lean;
|
||||
case nameof(JoinOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Join;
|
||||
case nameof(TopOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Top;
|
||||
case nameof(TriggerOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Trigger;
|
||||
default: throw new ArgumentException($"Unable to match '{order_type_def?.Descriptor.Name}' to an order definition type");
|
||||
}
|
||||
}
|
||||
@@ -311,8 +323,8 @@ namespace mbts.spark.utilities {
|
||||
public static PriceType PriceType => new PriceType { Status = PriceType.Types.Status.Bid };
|
||||
public static PriceOffset PriceOffset => new PriceOffset { PriceType = PriceType };
|
||||
public static QuantityTrigger QuantityTrigger => new QuantityTrigger { QuantityMeasure = QuantityMeasure, LessThanEqualto = true };
|
||||
public static AbsolutePriceTrigger AbsolutePriceTrigger => new AbsolutePriceTrigger { PriceType = PriceType, QuantityTrigger = QuantityTrigger };
|
||||
public static TrailingTrigger TrailingTrigger => new TrailingTrigger { PriceType = PriceType, Stop = true };
|
||||
public static bts.spark.AbsolutePriceTrigger AbsolutePriceTrigger => new bts.spark.AbsolutePriceTrigger { PriceType = PriceType, QuantityTrigger = QuantityTrigger };
|
||||
public static bts.spark.TrailingTrigger TrailingTrigger => new bts.spark.TrailingTrigger { PriceType = PriceType, Stop = true };
|
||||
public static PriceTrigger PriceTrigger => new PriceTrigger { AbsolutePriceTrigger = AbsolutePriceTrigger, TrailingTrigger = TrailingTrigger };
|
||||
public static OrderTime OrderTime => new OrderTime { Now = true };
|
||||
public static BTSTimedWindowParameters TimeWindow => new BTSTimedWindowParameters { Cancel = true, StartTime = OrderTime, StopTime = new OrderTime() };
|
||||
|
||||
@@ -43,4 +43,4 @@ namespace mbts.spark {
|
||||
public static readonly SecurityImmutable SecurityImmutable = new SecurityImmutable(Security);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,10 @@ using System.Threading.Tasks;
|
||||
namespace bts.api.core {
|
||||
static class ApiNativeMethods {
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr api_create_quickbus(byte[] qbconfig, long length);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_destroy_quickbus(IntPtr qbus);
|
||||
|
||||
|
||||
@@ -51,24 +51,24 @@ namespace bts.api.core {
|
||||
int change_count,
|
||||
IntPtr extra);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr api_client_create(IntPtr qbus, string type_name, string service_qualifier);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_client_destroy(IntPtr client);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
[return:MarshalAs(UnmanagedType.I1)]
|
||||
internal static extern bool api_client_is_on_qb_thread(IntPtr client);
|
||||
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_client_send(IntPtr nchannel, byte[] buffer, int length, raw_pb_callback p, IntPtr extra);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr api_client_subscribe(IntPtr client, string method_and_qualifier, raw_subscription_callback cb, IntPtr extra);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_client_unsubscribe(IntPtr client, IntPtr subscription);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_client_log_error(string msg);
|
||||
|
||||
#endregion
|
||||
@@ -81,10 +81,10 @@ namespace bts.api.core {
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal unsafe delegate void raw_rpc_request_callback(byte* buffer, long buffer_size, IntPtr cb, IntPtr extra);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr api_server_create(IntPtr qbus, string type_name, string service_qualifier, raw_rpc_request_callback callback, IntPtr extra);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_server_call_callback(ref IntPtr callback, byte[] buffer, int length);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
@@ -95,11 +95,11 @@ namespace bts.api.core {
|
||||
public IntPtr buffer;
|
||||
}
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr api_server_create_table(IntPtr server, string method_and_qualifier);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_server_update_table(IntPtr table, [In] StreamUpdate[] changes, long change_count);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void api_server_destroy_table(IntPtr table);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -60,28 +60,36 @@ namespace bts.api.core {
|
||||
bool first_update = true;
|
||||
var result = Observable.Create<RawUpdate<TOut>>(observer => {
|
||||
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
|
||||
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
|
||||
var added_items = new List<TOut>(change_count);
|
||||
var added_keys = new List<int>(change_count);
|
||||
var removed_keys = new List<int>(change_count);
|
||||
foreach (var change in changes) {
|
||||
if (change.type != ApiNativeMethods.ChangeType.REMOVE) {
|
||||
// create or change
|
||||
added_items.Add( converter( parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
|
||||
added_keys.Add(change.key);
|
||||
}
|
||||
if (change.type != ApiNativeMethods.ChangeType.CREATE) {
|
||||
// removed or changed
|
||||
removed_keys.Add(change.key);
|
||||
try {
|
||||
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
|
||||
var added_items = new List<TOut>(change_count);
|
||||
var added_keys = new List<int>(change_count);
|
||||
var removed_keys = new List<int>(change_count);
|
||||
foreach (var change in changes) {
|
||||
if (change.type != ApiNativeMethods.ChangeType.REMOVE) {
|
||||
// create or change
|
||||
added_items.Add( converter( parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
|
||||
added_keys.Add(change.key);
|
||||
}
|
||||
if (change.type != ApiNativeMethods.ChangeType.CREATE) {
|
||||
// removed or changed
|
||||
removed_keys.Add(change.key);
|
||||
}
|
||||
}
|
||||
observer.OnNext(new RawUpdate<TOut>(added_items, added_keys, removed_keys));
|
||||
}
|
||||
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
|
||||
if (!first_update) {
|
||||
observer.OnNext(RawUpdate<TOut>.Reset());
|
||||
}
|
||||
first_update = false;
|
||||
}
|
||||
observer.OnNext(new RawUpdate<TOut>(added_items, added_keys, removed_keys));
|
||||
}
|
||||
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
|
||||
if (!first_update) {
|
||||
observer.OnNext(RawUpdate<TOut>.Reset());
|
||||
}
|
||||
first_update = false;
|
||||
catch (Exception e) {
|
||||
ApiNativeMethods.api_client_log_error($"Exception in subscription callback for sub: '{method_and_qualifier}' \n--\n '{e}'");
|
||||
// Sleep to let the log message get asyncronously written to disk
|
||||
System.Threading.Thread.Sleep(100);
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
};
|
||||
var subscription = ApiNativeMethods.api_client_subscribe(_client, method_and_qualifier, cb, IntPtr.Zero);
|
||||
|
||||
@@ -8,19 +8,19 @@ namespace mbts.utils
|
||||
{
|
||||
internal static class BTSNativeMethods
|
||||
{
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
public static extern void provide_command_line(
|
||||
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] argv, int argc);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
public static extern int get_random_port();
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
public static extern void init_spark_lib();
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
public static extern void finit_spark_lib();
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
public static extern UInt64 ll_now();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace mbts.co2
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr create_md_consumer(
|
||||
[MarshalAs(UnmanagedType.LPArray)] UserSymbol[] symbols,
|
||||
UInt32 count, bool shared);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void destroy_md_consumer(IntPtr consumer);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern bool md_consumer_all_heartbeats_ok(IntPtr consumer);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
@@ -42,72 +42,72 @@ namespace mbts.co2
|
||||
public readonly IntPtr cold_header;
|
||||
}
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern bool md_consumer_get_handle(IntPtr consumer, security_key key, out BlitPointerTuple tup);
|
||||
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal unsafe delegate void raw_security_callback(security_key key, byte* buffer, UInt64 buffer_size, BlitPointerTuple tup);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern UInt64 md_consumer_walk_security_list(IntPtr consumer, raw_security_callback on_security);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void md_consumer_scan_securities(IntPtr consumer);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void md_consumer_set_on_security(IntPtr consumer, raw_security_callback on_security);
|
||||
internal unsafe delegate void raw_just_security_callback(security_key key, byte* buffer, UInt64 buffer_size);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern void md_consumer_get_single_security(MDConsumerHandle.ColdEntry* entry, raw_just_security_callback on_security);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern bool md_consumer_is_heart_beating(MDConsumerHandle.ColdHeader* header);
|
||||
|
||||
internal unsafe delegate void raw_receive_pb(byte* buffer, UInt64 buffer_size);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern int md_consumer_read_trades(IntPtr consumer, UserSymbol channel, ref Int64 cursor, raw_receive_pb cb);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern int md_consumer_read_rfqs(IntPtr consumer, UserSymbol channel, ref Int64 cursor, raw_receive_pb cb);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal unsafe delegate void raw_usersymbol_callback(UserSymbol us);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern void md_consumer_get_system_md_symbols(raw_usersymbol_callback cb);
|
||||
|
||||
|
||||
//
|
||||
// Relay control
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern IntPtr start_private_relay(IntPtr consumer, UInt32 interval);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern IntPtr stop_relay(IntPtr relay);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern IntPtr add_relay_symbol(IntPtr relay, UserSymbol symbol, bts.co2.MDRequest.Types.Flags flags);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal unsafe static extern void post_on_scan_security_thread(Action callback);
|
||||
}
|
||||
|
||||
internal static class Co2NativeTestMethods
|
||||
{
|
||||
[DllImport("spark_lib", EntryPoint = "co2_get_1234_handle")]
|
||||
[DllImport("libspark", EntryPoint = "co2_get_1234_handle")]
|
||||
internal static extern void get_1234_handle(out UInt64 size, out Co2NativeMethods.BlitPointerTuple pt);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void co2_get_sample_hot(out ulong size, ref MDConsumerHandle.HotEntry entry);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void co2_get_sample_depth(out ulong size, ref MDConsumerHandle.HotDepthEntry entry);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void co2_get_sample_cold(out ulong size, ref MDConsumerHandle.ColdEntry entry);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void co2_get_sample_cold_header(ref MDConsumerHandle.ColdHeader entry);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern IntPtr co2_produce_test_md();
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void co2_destroy_test_md(IntPtr handle);
|
||||
|
||||
}
|
||||
|
||||
@@ -124,8 +124,8 @@ namespace mbts.co2
|
||||
public double session_high { get { return _cold->session_high;} }
|
||||
public double session_low { get { return _cold->session_low; } }
|
||||
public double open_interest { get { return _cold->open_interest; } }
|
||||
public double session_high_bid { get { return _cold->session_high_bid; } }
|
||||
public double session_low_offer { get { return _cold->session_low_offer; } }
|
||||
// public double session_high_bid { get { return _cold->session_high_bid; } }
|
||||
// public double session_low_offer { get { return _cold->session_low_offer; } }
|
||||
public double fixing_price { get { return _cold->fixing_price; } }
|
||||
|
||||
//
|
||||
|
||||
@@ -60,16 +60,16 @@ namespace mbts.config
|
||||
/// private
|
||||
////////////
|
||||
|
||||
[DllImport("spark_lib", EntryPoint = "get_buffer")]
|
||||
[DllImport("libspark", EntryPoint = "get_buffer")]
|
||||
private static extern IntPtr raw_get_buffer(string key_name, ref int length);
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
private static extern bool initialize_nothrow(StringBuilder buffer, int length);
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
private static extern void reset();
|
||||
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
internal static extern void configcheck(int count, string[] args);
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace mbts.config
|
||||
public static bool is_running_nunit() {
|
||||
return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(ass => ass.FullName.ToLower().StartsWith("nunit.framework")) != null;
|
||||
}
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
public static extern void force_unittest_environment();
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace mbts
|
||||
return "Log file does not exist";
|
||||
}
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
private static extern void Logger_log (Level level, Subsystem subsystem, string message, string file, int line);
|
||||
|
||||
delegate void on_crash_callback([MarshalAs(UnmanagedType.LPStr)]string msg, [MarshalAs(UnmanagedType.LPStr)] string details);
|
||||
@@ -120,4 +120,4 @@ namespace mbts
|
||||
}
|
||||
public static event Action<string, object> OnSystemCrashing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace mbts {
|
||||
return string.Format(input, vals);
|
||||
}
|
||||
|
||||
[DllImport("spark_lib")]
|
||||
[DllImport("libspark")]
|
||||
private static extern void ll_to_parts(UInt64 ll_time, out int year, out int month, out int day, out int hour, out int minute, out int second, out int ms);
|
||||
|
||||
public static DateTime lldatetime(this ulong ll) {
|
||||
|
||||
@@ -97,14 +97,14 @@ namespace bts.sdk
|
||||
{
|
||||
check_subscriptions(flags, x =>
|
||||
(x.Exchange == product.Exchange && x.Type == product.Type && x.Symbol == product.Symbol));
|
||||
return subscribe(
|
||||
return _subscribe(
|
||||
x => (x.Exchange == product.Exchange && ((SecurityImmutable)x).security_type() == product.Type && x.Symbol == product.Symbol),
|
||||
update_interval,
|
||||
scheduler);
|
||||
}
|
||||
|
||||
public IObservable<ChannelUpdate> subscribe(Func<Security, bool> filter,
|
||||
TimeSpan? update_interval = null, IScheduler scheduler = null)
|
||||
IObservable<ChannelUpdate> _subscribe(Func<Security, bool> filter,
|
||||
TimeSpan? update_interval = null, IScheduler scheduler = null)
|
||||
{
|
||||
Data[] slots = new Data[0]; long local_security_count = 0;
|
||||
return timer_pump<Data>((last_update, observer) =>
|
||||
@@ -173,7 +173,7 @@ namespace bts.sdk
|
||||
long last_update_time = 0;
|
||||
return Observable.Create<T>(observer =>
|
||||
{
|
||||
return Observable.Timer(interval.Value, scheduler ?? Scheduler.Default).Subscribe(_ =>
|
||||
return Observable.Timer(interval.Value, interval.Value, scheduler ?? Scheduler.Default).Subscribe(_ =>
|
||||
{
|
||||
tick(last_update_time, observer);
|
||||
last_update_time = DateTime.UtcNow.Ticks;
|
||||
|
||||
@@ -61,12 +61,13 @@ namespace bts.sdk
|
||||
Venue ven = parse_venue(exch);
|
||||
SecurityType st = parse_security_type(sectype);
|
||||
Product prod = new Product() { Exchange = ven, Type = st, Symbol = symbol };
|
||||
|
||||
Func<Security, bool> filter = (x => x.Glbx.SecurityDesc == security_desc);
|
||||
_disps.Add(_md.subscribe(filter).Subscribe(x =>
|
||||
{
|
||||
print_update(x);
|
||||
}));
|
||||
|
||||
//if you have a security_id, we could make a more specifics subscription, but this is a command line driver....
|
||||
_disps.Add(_md.subscribe(prod).Where(x => filter(x.Security)).Subscribe(x =>
|
||||
{
|
||||
print_update(x);
|
||||
}));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace bts.sdk
|
||||
s.print_channel(args[1], args[2], args[3]);
|
||||
}
|
||||
else
|
||||
md_poll_help();
|
||||
md_help();
|
||||
}
|
||||
else
|
||||
md_help();
|
||||
@@ -101,7 +101,6 @@ namespace bts.sdk
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Commands:");
|
||||
Console.WriteLine(" md <exchange> <security_type> <symbol> [<security_desc>]");
|
||||
Console.WriteLine(" mdpoll <exchange> <security_type> <symbol> [<security_desc>]");
|
||||
Console.WriteLine(" ui <exchange> <security_type> <symbol> <security_desc> <poll_interval_in_ms>");
|
||||
Console.WriteLine(" order <exchange> <security_type> <symbol> <security_desc> <signed_quantity> [<price>]");
|
||||
Console.WriteLine("Arguments:");
|
||||
@@ -131,17 +130,6 @@ namespace bts.sdk
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
static void md_poll_help()
|
||||
{
|
||||
Console.WriteLine("mdpoll <exchange> <security_type> <symbol> [<security_desc>]");
|
||||
Console.WriteLine("This command subscribes to market data for an exchange-type-symbol channel and writes to console.");
|
||||
Console.WriteLine("Marketdata is read off the wire on one thread and read and printed on another through IScheduler");
|
||||
Console.WriteLine(" example: md_threaded GLBX Future ES");
|
||||
Console.WriteLine(" md_threaded GLBX Future ES ESZ8");
|
||||
Console.WriteLine(" md_threaded GLBX Spread ES");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
static void ui_help()
|
||||
{
|
||||
Console.WriteLine("ui <exchange> <security_type> <symbol> <security_desc> <poll_interval_in_ms>");
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
|
||||
<ItemGroup>
|
||||
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
|
||||
<BuildOutputInPackage Include="$(OutputPath)..\net471\spark_lib.dll"/>
|
||||
<BuildOutputInPackage Include="$(OutputPath)..\net471\libspark.dll"/>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1 @@
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/src/data_files/files/Onixs.BTS.lic;/home/ppinto/Dropbox/bts/spark/src/data_files/files/cert_secure_logon.txt)
|
||||
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(data_files)
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
|
||||
|
||||
# DEPENDENCIES
|
||||
file (GLOB_RECURSE To_Copy ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR} files/*)
|
||||
update_deps_file("${To_Copy}")
|
||||
# END DEPENDENCIES
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/data_files
|
||||
COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/copy_data_files.sh
|
||||
ARGS ${CMAKE_BINARY_DIR}/data_files ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Copying data files"
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/copy_data_files.sh ${To_Copy})
|
||||
|
||||
add_custom_target(data_files_dir ALL DEPENDS ${CMAKE_BINARY_DIR}/data_files)
|
||||
|
||||
Loaded 100 of 304 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user