Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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...433d413283.
@@ -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,10 @@
|
||||
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>();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,8 @@ namespace bts.shell {
|
||||
public TimeFilterTuple last_change;
|
||||
[ProtoMember(18)]
|
||||
public string security_regex;
|
||||
[ProtoMember(19)]
|
||||
public api.orderman.OrderSubType[] order_sub_types;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:NumericSignBrushConverter x:Key="PnLBackgroundColorConverter" Opacity="0.33"/>
|
||||
<convert:DoubleTextConverter x:Key="DoubleWithNaNHiddenConverter" Format="F2" />
|
||||
</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;
|
||||
|
||||
@@ -213,7 +213,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);
|
||||
|
||||
@@ -269,6 +269,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(),
|
||||
|
||||
@@ -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;
|
||||
@@ -74,7 +76,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) {
|
||||
@@ -319,7 +322,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
|
||||
|
||||
@@ -27,7 +27,8 @@ 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;
|
||||
|
||||
|
||||
public OrderBookWindow(OrdersModel model, OrderBookWindowFactory factory, IBus bus, OrderBookWindowFactory.OrderBookStartupData state, ImageSource icon) : base("Order Book", bus, factory, SparkCommands.ShowOrders.icon) {
|
||||
@@ -107,21 +108,36 @@ 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))
|
||||
);
|
||||
|
||||
_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
|
||||
};
|
||||
_vm.selected_order_changed += _vm_selected_order_changed;
|
||||
}
|
||||
|
||||
private bool _passes_recreate_search_criteria(OrderRecordImmutable record) {
|
||||
return record.status() == OrderStatus.Canceled
|
||||
&& !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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -rf $1
|
||||
mkdir $1
|
||||
cp -r $2/files/* $1
|
||||
@@ -0,0 +1,21 @@
|
||||
<license version="1.0">
|
||||
<type>production</type>
|
||||
<customer>
|
||||
<name>Blue Trading Systems</name>
|
||||
</customer>
|
||||
<period>
|
||||
<since>2017-04-24</since>
|
||||
<till>2018-08-29</till>
|
||||
</period>
|
||||
<dotNetProducts>
|
||||
<product id="41" name="FIX Analyser" />
|
||||
</dotNetProducts>
|
||||
<dotNetCheckSum>jqN4XONnMeiDSJIAa6CK9m9SitquhMq3MhJW97fG26Fp+GPog9ObMLS2elPvippdZsRWmJGsjTmFxAg8d41Vr+tpNgObi3VT3DtfYjGESTmi+u4vvJ9PNi8tQWQ7HPYBJe1X6j84ijhe5e1jHEcaXZ08ExDbESebn2ndHb4Phv4=</dotNetCheckSum>
|
||||
<products>
|
||||
<product id="1" name="OnixS FIX Engine for C++" />
|
||||
<product id="4" name="CME Market Data Handler for C++" />
|
||||
<product id="7" name="ICE iMpact Multicast Price Feed Handler for C++" />
|
||||
<product id="13" name="NYSE ArcaBook Multicast for Equities Handler for C++" />
|
||||
</products>
|
||||
<checksum>F5502F3BD9CE954022ABCBF94A7E4052</checksum>
|
||||
</license>
|
||||
@@ -0,0 +1,12 @@
|
||||
Session ID Access ID Secret Key Creation Date Expiration Date Environment
|
||||
1U7 i9pLY5mVhWLXs7FWNb9X 8esbwiaHaqmR2rXZMkJRHt00fW6l_-ppCGoheWWuBZQ 2018-04-05 2019-04-05 CERT
|
||||
1U7 ozXmORijPtJriTg6Wwny A1KOwrhH8GojNotjkCfVwHRSi_OS280xihaRZwJLFKo 2018-04-03 2018-05-03 CERT
|
||||
ANS 6ogfYoF54g695tqaHsFC CtDkFnuDuStB1S05nC_pWvANb6FWsV2IMNKkmM3EcP4 2018-04-05 2019-04-05 NEWRELEASE
|
||||
KTK IrUp518ekr8SzbfJGdPZ QmilC-cKgqlIiIBs_BV59fowbPfuBWn0QjeEc1dYj38 2018-04-05 2019-04-05 NEWRELEASE
|
||||
M5Y bUFtyvZgPhZFCYa434ap rfZ2XLLwtHt26szWwMxYRw9uPb7jE4evEWaKuKf2uyU 2018-04-05 2019-04-05 CERT
|
||||
MBX tbna9JLxT4Jlrzj0wtSS EcYqrLmR4ysSIT_NCBeC2AjDo7IAHsVZ0rOZ0cpAh_o 2018-04-05 2019-04-05 CERT
|
||||
MC1 0N10hKkE2B2KUt86ewd8 dJ5I52JNA-6aZZ8hnO0byVKcyqA8Of6kkstfIF89AAM 2018-04-05 2019-04-05 CERT
|
||||
MC5 e4uBmhQ1ebaXAeWHa7HK PNOM86eofsiCwzzKUsB5yAmrNoyZypnYD72pT2A8zgc 2018-04-05 2019-04-05 CERT
|
||||
MC7 S6KfCnay5UJ67P55ibHd 5lSs1TbX7N0p6Duu63_JDMghPUqBAWacvkM91MUv1uM 2018-04-05 2019-04-05 CERT
|
||||
MC8 AbEEtBn63WIAuItPDz1c n3ndjYb03Hx6wBO3me35HoONyDgO5TZKWk0tDSaX9KA 2018-04-05 2019-04-05 CERT
|
||||
T39 UWggBgHWAe07aqW3ps2I tbb3it0rUQSSTa0TC9QcRwkrHEMDLWhaJH4VQ0FqGB4 2018-04-05 2019-04-05 NEWRELEASE
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/ShellDiagnosticService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/testkeys.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPriceLantencyTester.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/windows_test_driver.cpp;utils/Dispatcher.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp)
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/services/AdminService.proto;api/services/ApiUnitTestService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/services/UserService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/Executor.conf;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cpp_config.conf;config/defaults/edge.conf;config/defaults/expiration_display_offset.conf;config/defaults/jetfix.conf;config/defaults/logging.conf;config/defaults/testkeys.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cpp_config.proto;config/protos/environment.proto;config/protos/expiration_display_offset.proto;config/protos/jetfix.proto;config/protos/logging.proto;config/protos/quickbus_cfg.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPriceLantencyTester.cpp;md/Diag/ShmDumper.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MDRecoverableConsumer.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/common.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2TestInstrument.cpp;md/test/md2_tests.cpp;md/test/synthetic_book_merge_tests.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/Dispatcher.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/datetime.cpp;utils/download.cpp;utils/file_utils.cpp;utils/init_spark_lib.cpp;utils/is_unittest_environment.cpp)
|
||||
+33
-28
@@ -1,28 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(spark_lib)
|
||||
|
||||
project(libspark)
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
include(${CMAKE_SOURCE_DIR}/tools/safe_dep_glob.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/tools/Protobuf.cmake)
|
||||
|
||||
# DEPENDENCIES
|
||||
# config protos
|
||||
file(GLOB_RECURSE Config_Protos RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} config/protos/*.proto)
|
||||
|
||||
# service protos
|
||||
file (GLOB_RECURSE Service_Protos RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} api/services/*.proto)
|
||||
|
||||
# non config/non service protos
|
||||
file(GLOB_RECURSE NonConfig_Protos RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.proto)
|
||||
list(REMOVE_ITEM NonConfig_Protos ${Service_Protos} ${Config_Protos})
|
||||
|
||||
# normal cpps
|
||||
file(GLOB_RECURSE SparkLib_Sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
|
||||
|
||||
# defaults
|
||||
file(GLOB_RECURSE Conf_Defaults RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} config/defaults/*.conf)
|
||||
|
||||
# this is used to communicate to other repositories that the build
|
||||
# needs to happen again (because a file was added)
|
||||
if (WIN32)
|
||||
file(GLOB_RECURSE SparkLib_Sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
api/*.cpp
|
||||
config/*.cpp
|
||||
domain_model/*.cpp
|
||||
expression/*.cpp
|
||||
logging/*.cpp
|
||||
md/*.cpp
|
||||
os/*.cpp
|
||||
quickbus/*.cpp
|
||||
sparkproto/*.cpp
|
||||
testing/*.cpp
|
||||
utils/*.cpp)
|
||||
else ()
|
||||
file(GLOB_RECURSE SparkLib_Sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
|
||||
endif()
|
||||
|
||||
update_deps_file("${SparkLib_Sources};${Config_Protos};${Service_Protos};${NonConfig_Protos};${Conf_Defaults}")
|
||||
# END DEPENDENCIES
|
||||
|
||||
@@ -55,15 +60,15 @@ foreach (cfg_file_path ${Conf_Defaults})
|
||||
configure_file(${cfg_file_path} ${CMAKE_BINARY_DIR}/src/server/sparkd/defaults/${cfg_file_name})
|
||||
endforeach(cfg_file_path)
|
||||
|
||||
# spark_lib
|
||||
# lib
|
||||
if (WIN32)
|
||||
add_library(spark_lib SHARED
|
||||
add_library(libspark SHARED
|
||||
${SparkLib_Sources}
|
||||
${PROTO_SRCS}
|
||||
${SERVICE_COMPILER_SRCS}
|
||||
)
|
||||
else ()
|
||||
add_library(spark_lib STATIC
|
||||
add_library(libspark SHARED
|
||||
${SparkLib_Sources}
|
||||
${PROTO_SRCS}
|
||||
${SERVICE_COMPILER_SRCS}
|
||||
@@ -71,10 +76,10 @@ else ()
|
||||
endif()
|
||||
|
||||
#include paths
|
||||
target_include_directories( spark_lib PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src)
|
||||
target_include_directories( libspark PUBLIC ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries( spark_lib
|
||||
target_link_libraries( libspark
|
||||
libcurl
|
||||
libfmt
|
||||
libtbb
|
||||
@@ -88,12 +93,8 @@ if (WIN32)
|
||||
libprotobuf
|
||||
)
|
||||
else ()
|
||||
target_link_libraries( spark_lib
|
||||
curl
|
||||
ssl
|
||||
crypto
|
||||
fmt
|
||||
tbb
|
||||
target_link_libraries( libspark
|
||||
protobuf
|
||||
boost_system
|
||||
boost_thread
|
||||
boost_filesystem
|
||||
@@ -101,9 +102,13 @@ else ()
|
||||
boost_chrono
|
||||
boost_date_time
|
||||
boost_atomic
|
||||
protobuf
|
||||
uuid.a
|
||||
sqlite3
|
||||
curl
|
||||
uuid
|
||||
ssl
|
||||
crypto
|
||||
fmt
|
||||
tbb
|
||||
cryptopp
|
||||
-lrt
|
||||
-ldl
|
||||
@@ -116,4 +121,4 @@ if (WIN32)
|
||||
include(${CMAKE_SOURCE_DIR}/tools/vs_filters.cmake)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ internal abstract class {{name}}Base : ApiServer, IDisposable {
|
||||
}
|
||||
|
||||
ApiNativeMethods.StreamUpdate pack({{output_type_full}} item, bool is_change, List<GCHandle> pinned_buffers) {
|
||||
var data = item.ToByteArray();
|
||||
var data = Google.Protobuf.MessageExtensions.ToByteArray(item);
|
||||
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
pinned_buffers.Add(handle);
|
||||
return new ApiNativeMethods.StreamUpdate() {
|
||||
|
||||
@@ -59,18 +59,18 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
|
||||
{{#each method}}{{#if server_streaming}}
|
||||
|
||||
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher(std::string const&qualifier) const {
|
||||
TableHandle handle = _channel.create_table(std::string("{{name}}/") + qualifier);
|
||||
bts::api::TableHandle handle = _channel.create_table(std::string("{{name}}/") + qualifier);
|
||||
return make_{{name}}_publisher(handle);
|
||||
}
|
||||
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher(const {{input_type_cpp}}& qualifier) const {
|
||||
TableHandle handle = _channel.create_table(format_qualifier("{{name}}", qualifier));
|
||||
bts::api::TableHandle handle = _channel.create_table(format_qualifier("{{name}}", qualifier));
|
||||
return make_{{name}}_publisher(handle);
|
||||
}
|
||||
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher() const {
|
||||
TableHandle handle = _channel.create_table("{{name}}");
|
||||
bts::api::TableHandle handle = _channel.create_table("{{name}}");
|
||||
return make_{{name}}_publisher(handle);
|
||||
}
|
||||
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher(TableHandle handle) const {
|
||||
{{../name}}Base::{{cap name}}Publisher {{../name}}Base::make_{{name}}_publisher(bts::api::TableHandle handle) const {
|
||||
{{#unless key_type.natural_key}}
|
||||
std::unordered_map<{{key_type.cpptype}}, int> _map;
|
||||
int _next_id;
|
||||
@@ -97,9 +97,9 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
|
||||
int total_updates = add_count + change_count + remove_count;
|
||||
|
||||
// Optimize small collections
|
||||
StreamUpdate stack_buffer[256];
|
||||
std::vector<StreamUpdate> heap_buffer;
|
||||
StreamUpdate* pupdates = stack_buffer;
|
||||
bts::api::StreamUpdate stack_buffer[256];
|
||||
std::vector<bts::api::StreamUpdate> heap_buffer;
|
||||
bts::api::StreamUpdate* pupdates = stack_buffer;
|
||||
if (total_updates > 256) {
|
||||
heap_buffer.resize(total_updates);
|
||||
pupdates = &heap_buffer[0];
|
||||
@@ -108,13 +108,13 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
|
||||
int dest = 0;
|
||||
if (added) {
|
||||
for(const auto& item : *added) {
|
||||
StreamUpdate& update = pupdates[dest++];
|
||||
update.type = StreamUpdate::CREATE;
|
||||
bts::api::StreamUpdate& update = pupdates[dest++];
|
||||
update.type = bts::api::StreamUpdate::CREATE;
|
||||
if (removed != nullptr) {
|
||||
auto iter = removed->find({{name}}_key(item));
|
||||
if (iter != removed->end()) {
|
||||
removed->erase(iter);
|
||||
update.type = StreamUpdate::REPLACE;
|
||||
update.type = bts::api::StreamUpdate::REPLACE;
|
||||
}
|
||||
}
|
||||
{{#if key_type.natural_key}}
|
||||
@@ -130,8 +130,8 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
|
||||
}
|
||||
if (changed) {
|
||||
for (const auto& item : *changed) {
|
||||
StreamUpdate& update = pupdates[dest++];
|
||||
update.type = StreamUpdate::REPLACE;
|
||||
bts::api::StreamUpdate& update = pupdates[dest++];
|
||||
update.type = bts::api::StreamUpdate::REPLACE;
|
||||
{{#if key_type.natural_key}}
|
||||
update.key = (int)({{name}}_key(item));
|
||||
{{else}}
|
||||
@@ -145,8 +145,8 @@ void {{name}}Base::dispatch(const bts::api::RequestHeader& header, google::proto
|
||||
}
|
||||
if (removed) {
|
||||
for (const auto& key : *removed) {
|
||||
StreamUpdate& update = pupdates[dest++];
|
||||
update.type = StreamUpdate::REMOVE;
|
||||
bts::api::StreamUpdate& update = pupdates[dest++];
|
||||
update.type = bts::api::StreamUpdate::REMOVE;
|
||||
{{#if key_type.natural_key}}
|
||||
update.key = (int)(key);
|
||||
{{else}}
|
||||
|
||||
@@ -61,7 +61,7 @@ protected:
|
||||
private:
|
||||
void dispatch(const bts::api::RequestHeader&, google::protobuf::io::ZeroCopyInputStream&, OnResponse) override;
|
||||
{{#each method}}{{#if server_streaming}}
|
||||
{{cap name}}Publisher make_{{name}}_publisher(TableHandle) const;
|
||||
{{cap name}}Publisher make_{{name}}_publisher(bts::api::TableHandle) const;
|
||||
{{/if}}{{/each}}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "lib/api/services/AdminService.server.pb.h"
|
||||
#include "lib/api/service_helpers/AdminServiceUtils.h"
|
||||
#include "lib/md/get_symbols.h"
|
||||
|
||||
namespace bts{
|
||||
namespace admin{
|
||||
@@ -30,6 +31,9 @@ bool AdminServiceUtils::has_exchange(const bts::api::admin::ExchangeConnection&
|
||||
(conn.cme_connection().has_cgw_connection() || conn.cme_connection().has_msgw_connection());
|
||||
break;
|
||||
case api::Venue::VENUE_UNSET:
|
||||
case api::Venue::VENUE_SIM:
|
||||
rv = conn.has_sim_connection();
|
||||
break;
|
||||
case api::Venue::VENUE_BTS:
|
||||
case api::Venue::VENUE_CBOE:
|
||||
case api::Venue::VENUE_CBOECFE:
|
||||
@@ -70,6 +74,20 @@ AdminServiceUtils::resolve_cme_connection(const bts::api::admin::AdminData& data
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bts::api::admin::ExchangeConnection*
|
||||
AdminServiceUtils::resolve_sim_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec){
|
||||
bts_assert(sec.exchange() == api::Venue::VENUE_SIM);
|
||||
if(account.has_connections())
|
||||
for (const std::string& conn_name : account.connections().exchange_connection_names()){
|
||||
const bts::api::admin::ExchangeConnection* conn = find_connection(data, conn_name);
|
||||
if (conn!=nullptr && conn->has_sim_connection())
|
||||
return conn;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bts::api::admin::ExchangeConnection*
|
||||
AdminServiceUtils::resolve_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
@@ -81,6 +99,9 @@ AdminServiceUtils::resolve_connection(const bts::api::admin::AdminData& data,
|
||||
break;
|
||||
case api::Venue::VENUE_UNSET:
|
||||
case api::Venue::VENUE_BTS:
|
||||
case api::Venue::VENUE_SIM:
|
||||
rv = resolve_sim_connection(data, account, sec);
|
||||
break;
|
||||
case api::Venue::VENUE_CBOE:
|
||||
case api::Venue::VENUE_CBOECFE:
|
||||
case api::Venue::VENUE_ICE:
|
||||
@@ -104,16 +125,26 @@ void AdminServiceUtils::fill_connections_from_config(bts::api::admin::AdminData&
|
||||
admin_data.clear_connections();
|
||||
auto &all_connections = config::get<config::JetFIX>().connection();
|
||||
for (auto const &c : all_connections){
|
||||
// TODO:(assumes all exchanges are CME)
|
||||
bts::api::admin::ExchangeConnection connection;
|
||||
uint32_t new_id = find_max_connection_id(admin_data)+1;
|
||||
connection.set_id(new_id);
|
||||
connection.set_name(c.name());
|
||||
if (c.cme_market_segment_id()==0)
|
||||
connection.mutable_cme_connection()->mutable_cgw_connection();
|
||||
else
|
||||
connection.mutable_cme_connection()->mutable_msgw_connection()->set_market_segment_id(c.cme_market_segment_id());
|
||||
add_connection(admin_data,connection);
|
||||
|
||||
switch (c.platform()) {
|
||||
case config::JetFIX::Connection::GLBX:
|
||||
if (c.cme_market_segment_id()==0)
|
||||
connection.mutable_cme_connection()->mutable_cgw_connection();
|
||||
else
|
||||
connection.mutable_cme_connection()->mutable_msgw_connection()->set_market_segment_id(c.cme_market_segment_id());
|
||||
add_connection(admin_data,connection);
|
||||
break;
|
||||
case config::JetFIX::Connection::CBOE:
|
||||
break;
|
||||
case config::JetFIX::Connection::SIM:
|
||||
connection.mutable_sim_connection();
|
||||
add_connection(admin_data,connection);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(int i=0;i<admin_data.accounts_size();i++)
|
||||
sync_account_connections(admin_data, *admin_data.mutable_accounts(i));
|
||||
@@ -193,6 +224,19 @@ const bts::api::admin::CMEAccountData* AdminServiceUtils::get_account_cme_data(c
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bts::api::admin::SIMAccountData* AdminServiceUtils::get_account_sim_data(const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::admin::AdminData& data){
|
||||
if (account.has_exchange_account_data() && account.exchange_account_data().has_sim_account_data())
|
||||
return &(account.exchange_account_data().sim_account_data());
|
||||
else if (has_id(account.parent_account_id())){
|
||||
const bts::api::admin::BTSAccount* parent_account = find_account(data, account.parent_account_id());
|
||||
if (parent_account != nullptr)
|
||||
return get_account_sim_data (*parent_account, data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AdminServiceUtils::account_has_connection(const bts::api::admin::BTSAccount& account,
|
||||
const std::string& connection_name){
|
||||
if(account.has_connections()){
|
||||
@@ -207,15 +251,21 @@ std::string AdminServiceUtils::find_account_exchange_account (const bts::api::ad
|
||||
api::Venue exchange,
|
||||
bts::api::admin::AdminServiceOperationResult& out_result){
|
||||
out_result.set_is_success(true);
|
||||
const api::admin::CMEAccountData* cme_account_data;
|
||||
std::string rv = "";
|
||||
switch(exchange)
|
||||
{
|
||||
case api::Venue::VENUE_GLBX:
|
||||
cme_account_data = get_account_cme_data(account, data);
|
||||
case api::Venue::VENUE_GLBX: {
|
||||
auto cme_account_data = get_account_cme_data(account, data);
|
||||
if (cme_account_data != nullptr)
|
||||
rv = cme_account_data->clearing_account();
|
||||
break;
|
||||
}
|
||||
case api::Venue::VENUE_SIM: {
|
||||
auto sim_account_data = get_account_sim_data(account, data);
|
||||
if (sim_account_data != nullptr)
|
||||
rv = sim_account_data->clearing_account();
|
||||
break;
|
||||
}
|
||||
case api::Venue::VENUE_UNSET:
|
||||
case api::Venue::VENUE_BTS:
|
||||
case api::Venue::VENUE_CBOE:
|
||||
@@ -228,6 +278,7 @@ std::string AdminServiceUtils::find_account_exchange_account (const bts::api::ad
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::map<uint32_t,bool> AdminServiceUtils::accounts_map(const bts::api::admin::AdminData& admin_data){
|
||||
std::map<uint32_t,bool> rv;
|
||||
auto account_ptrs = accounts(admin_data, false);
|
||||
@@ -315,7 +366,8 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
|
||||
out_result.set_error_text("Could not find connection");
|
||||
return rv;
|
||||
}
|
||||
if(sec.exchange()==bts::api::Venue::VENUE_GLBX){
|
||||
switch (sec.exchange()) {
|
||||
case bts::api::Venue::VENUE_GLBX: {
|
||||
const bts::api::admin::CMEUserData* cme_user_data = get_user_cme_data(*user_ptr, data);
|
||||
if(cme_user_data==nullptr){
|
||||
out_result.set_is_success(false);
|
||||
@@ -334,6 +386,11 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
|
||||
out_result.set_error_text("Operator ID is not set");
|
||||
return rv;
|
||||
}
|
||||
if (cme_user_data->location().length() == 0){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Location is not set");
|
||||
return rv;
|
||||
}
|
||||
if (cme_account_data->clearing_account().length() == 0){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Clearing Account is not set");
|
||||
@@ -346,17 +403,77 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
|
||||
}
|
||||
rv.set_session(conn_ptr->name());
|
||||
rv.set_operator_id(cme_user_data->operator_id());
|
||||
rv.set_location(cme_user_data->location());
|
||||
rv.set_smp_id(cme_user_data->smp_id());
|
||||
if (cme_user_data->smp_instruction()== "O")
|
||||
rv.set_smp_instruction(api::orderman::SelfMatchPreventionInstruction::SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_RESTING);
|
||||
else if (cme_user_data->smp_instruction()== "N")
|
||||
rv.set_smp_instruction(api::orderman::SelfMatchPreventionInstruction::SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_AGRESSING);
|
||||
else
|
||||
rv.set_smp_instruction(api::orderman::SelfMatchPreventionInstruction::SELF_MATCH_PREVENTION_INSTRUCTION_UNDEFINED);
|
||||
|
||||
rv.set_account(cme_account_data->clearing_account());
|
||||
rv.set_cti_code(cme_account_data->cti());
|
||||
rv.set_customer_or_firm(cme_account_data->origin());
|
||||
return rv;
|
||||
}
|
||||
else{
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Only contracts at the CME are supported right now");
|
||||
return rv;
|
||||
case api::Venue::VENUE_SIM: {
|
||||
const bts::api::admin::SIMUserData* sim_user_data = get_user_sim_data(*user_ptr, data);
|
||||
if(sim_user_data==nullptr){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Could not find SIM data for user");
|
||||
return rv;
|
||||
}
|
||||
const bts::api::admin::SIMAccountData* sim_account_data = get_account_sim_data(*account_ptr, data);
|
||||
if(sim_account_data==nullptr){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Could not find SIM data for account");
|
||||
return rv;
|
||||
}
|
||||
// check for data corectness
|
||||
if (sim_user_data->operator_id().length() == 0){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Operator ID is not set");
|
||||
return rv;
|
||||
}
|
||||
if (sim_user_data->location().length() == 0){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Location is not set");
|
||||
return rv;
|
||||
}
|
||||
if (sim_account_data->clearing_account().length() == 0){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Clearing Account is not set");
|
||||
return rv;
|
||||
}
|
||||
if (sim_account_data->cti()==bts::api::orderman::CTICode::CTI_CODE_UNDEFINED){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("CTI Code is not set");
|
||||
return rv;
|
||||
}
|
||||
rv.set_session(conn_ptr->name());
|
||||
rv.set_operator_id(sim_user_data->operator_id());
|
||||
rv.set_location(sim_user_data->location());
|
||||
rv.set_account(sim_account_data->clearing_account());
|
||||
rv.set_cti_code(sim_account_data->cti());
|
||||
rv.set_customer_or_firm(sim_account_data->origin());
|
||||
return rv;
|
||||
}
|
||||
break;
|
||||
case api::Venue::VENUE_UNSET:
|
||||
case api::Venue::VENUE_BTS:
|
||||
case api::Venue::VENUE_CBOE:
|
||||
case api::Venue::VENUE_CBOECFE:
|
||||
case api::Venue::VENUE_ICE:
|
||||
case api::Venue::Venue_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case api::Venue::Venue_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Unsupported exchange");
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// account mutators ////////////////////////
|
||||
////////////////////////////////////////////
|
||||
@@ -493,6 +610,19 @@ const bts::api::admin::CMEUserData* AdminServiceUtils::get_group_cme_data(const
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bts::api::admin::SIMUserData* AdminServiceUtils::get_group_sim_data(const bts::api::admin::BTSUserGroup& group,
|
||||
const bts::api::admin::AdminData& data){
|
||||
if (group.has_user_data() && group.user_data().has_exchange_user_data() && group.user_data().exchange_user_data().has_sim_data())
|
||||
return &(group.user_data().exchange_user_data().sim_data());
|
||||
else if (has_id(group.parent_group_id())){
|
||||
const bts::api::admin::BTSUserGroup* parent_group = find_group(data, group.parent_group_id());
|
||||
if (parent_group != nullptr)
|
||||
return get_group_sim_data(*parent_group, data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bts::api::admin::UserAccountPermissions* const AdminServiceUtils::get_group_user_permissions (const bts::api::admin::AdminData& admin_data,
|
||||
uint32_t group_id){
|
||||
const bts::api::admin::BTSUserGroup* const group = find_group(admin_data, group_id);
|
||||
@@ -598,6 +728,7 @@ uint32_t AdminServiceUtils::find_max_user_id (const bts::api::admin::AdminData&
|
||||
rv = user_ptr->id();
|
||||
return rv;
|
||||
}
|
||||
|
||||
const bts::api::admin::CMEUserData* AdminServiceUtils::get_user_cme_data(const bts::api::admin::BTSUser& user,
|
||||
const bts::api::admin::AdminData& data){
|
||||
if (user.has_user_data() && user.user_data().has_exchange_user_data() && user.user_data().exchange_user_data().has_cme_data())
|
||||
@@ -609,22 +740,41 @@ const bts::api::admin::CMEUserData* AdminServiceUtils::get_user_cme_data(const b
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bts::api::admin::SIMUserData* AdminServiceUtils::get_user_sim_data(const bts::api::admin::BTSUser& user,
|
||||
const bts::api::admin::AdminData& data){
|
||||
if (user.has_user_data() && user.user_data().has_exchange_user_data() && user.user_data().exchange_user_data().has_sim_data())
|
||||
return &(user.user_data().exchange_user_data().sim_data());
|
||||
else{
|
||||
const bts::api::admin::BTSUserGroup* parent_group = find_group(data, user.parent_group_id());
|
||||
if (parent_group != nullptr)
|
||||
return get_group_sim_data(*parent_group, data);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// finds the "exchange user" (ex: "operator_id" at CME or "authorized_trader"
|
||||
// at ICE) for a user
|
||||
std::string AdminServiceUtils::find_user_exchange_id (const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSUser& user,
|
||||
api::Venue exchange,
|
||||
bts::api::admin::AdminServiceOperationResult& out_result){
|
||||
const api::admin::CMEUserData* cme_user_data;
|
||||
out_result.set_is_success(true);
|
||||
std::string rv = "";
|
||||
switch(exchange)
|
||||
{
|
||||
case api::Venue::VENUE_GLBX:
|
||||
cme_user_data = get_user_cme_data(user, data);
|
||||
case api::Venue::VENUE_GLBX: {
|
||||
auto cme_user_data = get_user_cme_data(user, data);
|
||||
if (cme_user_data != nullptr)
|
||||
rv = cme_user_data->operator_id();
|
||||
break;
|
||||
}
|
||||
case api::Venue::VENUE_SIM: {
|
||||
auto sim_user_data = get_user_sim_data(user, data);
|
||||
if (sim_user_data != nullptr)
|
||||
rv = sim_user_data->operator_id();
|
||||
break;
|
||||
}
|
||||
|
||||
case api::Venue::VENUE_UNSET:
|
||||
case api::Venue::VENUE_BTS:
|
||||
case api::Venue::VENUE_CBOE:
|
||||
@@ -963,6 +1113,46 @@ api::SecurityType AdminServiceUtils::security_type(const api::Security& security
|
||||
////////////////////////////////////////////
|
||||
// boostrapping ////////////////////////////
|
||||
////////////////////////////////////////////
|
||||
|
||||
void add_product_position_limits(bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type, uint32_t limit)
|
||||
{
|
||||
std::vector<md::UserSymbol> symbols;
|
||||
md::get_system_md_symbols(venue, sec_type,&symbols);
|
||||
|
||||
for (auto const &s : symbols) {
|
||||
auto acct_a_prod_limit = to_account->add_product_position_limits();
|
||||
acct_a_prod_limit->mutable_product()->set_exchange(s.exchange());
|
||||
acct_a_prod_limit->mutable_product()->set_security_type(s.type());
|
||||
acct_a_prod_limit->mutable_product()->set_security_symbol(s.symbol());
|
||||
acct_a_prod_limit->set_long_position_limit(limit);
|
||||
acct_a_prod_limit->set_short_position_limit(limit);
|
||||
}
|
||||
}
|
||||
|
||||
void add_security_clip_sizes_to_account(bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type,uint32_t limit)
|
||||
{
|
||||
std::vector<md::UserSymbol> symbols;
|
||||
md::get_system_md_symbols(venue, sec_type,&symbols);
|
||||
|
||||
for (auto const &s : symbols) {
|
||||
auto acct_a_clip_size = to_account->mutable_security_clip_limits()->add_limit();
|
||||
acct_a_clip_size->mutable_security()->set_exchange(s.exchange());
|
||||
acct_a_clip_size->mutable_security()->set_security_type(s.type());
|
||||
acct_a_clip_size->mutable_security()->set_security_symbol(s.symbol());
|
||||
acct_a_clip_size->set_clip_size(limit);
|
||||
}
|
||||
}
|
||||
|
||||
void add_security_position_limits (bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type)
|
||||
{
|
||||
auto acct_a1_sec_limit = to_account->add_security_position_limits();
|
||||
acct_a1_sec_limit->mutable_security()->set_exchange(venue);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_type(sec_type);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_symbol("*");
|
||||
acct_a1_sec_limit->set_long_position_limit (10000);
|
||||
acct_a1_sec_limit->set_short_position_limit (10000);
|
||||
}
|
||||
|
||||
void AdminServiceUtils::bootstrap_data (bts::api::admin::AdminData& data){
|
||||
data.set_id(1);
|
||||
bts::api::admin::AdminServiceOperationResult result;
|
||||
@@ -975,7 +1165,7 @@ void AdminServiceUtils::bootstrap_data (bts::api::admin::AdminData& data){
|
||||
auto account_3 = create_account(data, parent_account->id(), std::string("ACC A3"), std::string("description"), true, account_result); account_3->set_is_active(true);
|
||||
create_account(data, parent_account->id(), std::string("ACC A4"), std::string("description"), true, account_result);
|
||||
// add group
|
||||
auto def_group = create_group(data, 0, std::string("DEFAULT"), result);
|
||||
auto def_group = create_group(data, 0, std::string("default"), result);
|
||||
// adding users to the group
|
||||
bts::api::admin::LicenseData license_data;
|
||||
bts::api::admin::LicenseEntitlements license_entitlements;
|
||||
@@ -994,68 +1184,90 @@ void AdminServiceUtils::bootstrap_data (bts::api::admin::AdminData& data){
|
||||
set_user_accounts(user_1, account_permissions);
|
||||
set_user_accounts(user_2, account_permissions);
|
||||
set_user_accounts(user_3, account_permissions);
|
||||
// add cme account data
|
||||
|
||||
// add cme and sim account data
|
||||
bts::api::admin::ExchangeAccountData exchange_account_data;
|
||||
exchange_account_data.mutable_cme_account_data()->set_cti(bts::api::orderman::CTICode::CTI_CODE_CTI_CODE1);
|
||||
exchange_account_data.mutable_cme_account_data()->set_origin(bts::api::orderman::CustomerOrFirm::CUSTOMER_OR_FIRM_FIRM);
|
||||
exchange_account_data.mutable_cme_account_data()->set_clearing_account("ca_" + parent_account->name());
|
||||
exchange_account_data.mutable_sim_account_data()->set_cti(bts::api::orderman::CTICode::CTI_CODE_CTI_CODE1);
|
||||
exchange_account_data.mutable_sim_account_data()->set_origin(bts::api::orderman::CustomerOrFirm::CUSTOMER_OR_FIRM_FIRM);
|
||||
exchange_account_data.mutable_sim_account_data()->set_clearing_account("ca_" + parent_account->name());
|
||||
set_account_exchange_data(parent_account, exchange_account_data);
|
||||
|
||||
|
||||
exchange_account_data.mutable_cme_account_data()->set_clearing_account("ca_" + account_1->name());
|
||||
exchange_account_data.mutable_sim_account_data()->set_clearing_account("ca_" + account_1->name());
|
||||
set_account_exchange_data(account_1, exchange_account_data);
|
||||
|
||||
exchange_account_data.mutable_cme_account_data()->set_clearing_account("ca_" + account_2->name());
|
||||
exchange_account_data.mutable_sim_account_data()->set_clearing_account("ca_" + account_2->name());
|
||||
set_account_exchange_data(account_2, exchange_account_data);
|
||||
|
||||
exchange_account_data.mutable_cme_account_data()->set_clearing_account("ca_" + account_3->name());
|
||||
exchange_account_data.mutable_sim_account_data()->set_clearing_account("ca_" + account_3->name());
|
||||
set_account_exchange_data(account_3, exchange_account_data);
|
||||
// add cme user data
|
||||
|
||||
// add cme and sim user data
|
||||
bts::api::admin::ExchangeUserData exchange_user_data;
|
||||
exchange_user_data.mutable_cme_data()->set_operator_id("op_id_" + user_1->name());
|
||||
exchange_user_data.mutable_cme_data()->set_location("us,nc");
|
||||
exchange_user_data.mutable_sim_data()->set_operator_id("op_id_" + user_1->name());
|
||||
exchange_user_data.mutable_sim_data()->set_location("us,nc");
|
||||
set_user_exchange_data(user_1, exchange_user_data);
|
||||
|
||||
exchange_user_data.mutable_cme_data()->set_operator_id("op_id_" + user_2->name());
|
||||
exchange_user_data.mutable_cme_data()->set_location("us,nc");
|
||||
exchange_user_data.mutable_sim_data()->set_operator_id("op_id_" + user_2->name());
|
||||
exchange_user_data.mutable_sim_data()->set_location("us,nc");
|
||||
set_user_exchange_data(user_2, exchange_user_data);
|
||||
|
||||
exchange_user_data.mutable_cme_data()->set_operator_id("op_id_" + user_3->name());
|
||||
exchange_user_data.mutable_cme_data()->set_location("us,nc");
|
||||
exchange_user_data.mutable_sim_data()->set_operator_id("op_id_" + user_3->name());
|
||||
exchange_user_data.mutable_sim_data()->set_location("us,nc");
|
||||
set_user_exchange_data(user_3, exchange_user_data);
|
||||
|
||||
// associate all connections to all accounts
|
||||
for(int i=0; i<data.connections_size();i++){
|
||||
std::string conn_name = data.connections(i).name();
|
||||
for(int j=0; j<data.accounts_size();j++)
|
||||
data.mutable_accounts(j)->mutable_connections()->add_exchange_connection_names(conn_name);
|
||||
}
|
||||
|
||||
// add prod limit for ACC A
|
||||
auto acct_a_prod_limit = parent_account->add_product_position_limits();
|
||||
acct_a_prod_limit->mutable_product()->set_exchange(api::Venue::VENUE_GLBX);
|
||||
acct_a_prod_limit->mutable_product()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a_prod_limit->mutable_product()->set_security_symbol("*");
|
||||
acct_a_prod_limit->set_long_position_limit(500000);
|
||||
acct_a_prod_limit->set_short_position_limit(500000);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE,50000);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD,50000);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE,50000);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD,50000);
|
||||
|
||||
// security clip sizes for ACC A
|
||||
auto acct_a_clip_size = parent_account->mutable_security_clip_limits()->add_limit();
|
||||
acct_a_clip_size->mutable_security()->set_exchange(api::Venue::VENUE_GLBX);
|
||||
acct_a_clip_size->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a_clip_size->mutable_security()->set_security_symbol("*");
|
||||
acct_a_clip_size->set_clip_size(50);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE,50);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD,50);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE,50);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD,50);
|
||||
|
||||
// add sec limit for ACC A1
|
||||
auto acct_a1_sec_limit = account_1->add_security_position_limits();
|
||||
acct_a1_sec_limit->mutable_security()->set_exchange(api::Venue::VENUE_GLBX);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_symbol("*");
|
||||
acct_a1_sec_limit->set_long_position_limit (10000);
|
||||
acct_a1_sec_limit->set_short_position_limit (10000);
|
||||
// cme
|
||||
add_security_position_limits(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
add_security_position_limits(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD);
|
||||
add_security_position_limits(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
add_security_position_limits(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD);
|
||||
|
||||
// add prod limit for ACC A1
|
||||
auto acct_a1_prod_limit = account_1->add_product_position_limits();
|
||||
acct_a1_prod_limit->mutable_product()->set_exchange(api::Venue::VENUE_GLBX);
|
||||
acct_a1_prod_limit->mutable_product()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_prod_limit->mutable_product()->set_security_symbol("*");
|
||||
acct_a1_prod_limit->set_long_position_limit(1000000);
|
||||
acct_a1_prod_limit->set_short_position_limit(1000000);
|
||||
add_product_position_limits(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE,100000);
|
||||
add_product_position_limits(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD,100000);
|
||||
add_product_position_limits(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE,100000);
|
||||
add_product_position_limits(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD,100000);
|
||||
|
||||
// security clip sizes for ACC A1
|
||||
auto acct_a1_clip_size = account_1->mutable_security_clip_limits()->add_limit();
|
||||
acct_a1_clip_size->mutable_security()->set_exchange(api::Venue::VENUE_GLBX);
|
||||
acct_a1_clip_size->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_clip_size->mutable_security()->set_security_symbol("*");
|
||||
acct_a1_clip_size->set_clip_size(100);
|
||||
add_security_clip_sizes_to_account(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE,100);
|
||||
add_security_clip_sizes_to_account(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD,100);
|
||||
add_security_clip_sizes_to_account(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE,100);
|
||||
add_security_clip_sizes_to_account(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD,100);
|
||||
}
|
||||
bool AdminServiceUtils::has_id (uint32_t id){return id>0;}
|
||||
bool AdminServiceUtils::is_symbol_set (const std::string& symbol){
|
||||
return symbol !="*";
|
||||
}
|
||||
}}
|
||||
}}
|
||||
@@ -32,6 +32,9 @@ public:
|
||||
static const bts::api::admin::ExchangeConnection* resolve_cme_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec);
|
||||
static const bts::api::admin::ExchangeConnection* resolve_sim_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec);
|
||||
static const bts::api::admin::ExchangeConnection* resolve_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec);
|
||||
@@ -67,6 +70,8 @@ public:
|
||||
// if needed)
|
||||
static const bts::api::admin::CMEAccountData* get_account_cme_data(const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::admin::AdminData& data);
|
||||
static const bts::api::admin::SIMAccountData* get_account_sim_data(const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::admin::AdminData& data);
|
||||
// returns true if an account has a connection
|
||||
bool static account_has_connection(const bts::api::admin::BTSAccount& account, const std::string& connection_name);
|
||||
// finds the external/clearing account associated
|
||||
@@ -132,6 +137,8 @@ public:
|
||||
// travels up the group tree until it finds a populated cme data
|
||||
static const bts::api::admin::CMEUserData* get_group_cme_data(const bts::api::admin::BTSUserGroup& group,
|
||||
const bts::api::admin::AdminData& data);
|
||||
static const bts::api::admin::SIMUserData* get_group_sim_data(const bts::api::admin::BTSUserGroup& group,
|
||||
const bts::api::admin::AdminData& data);
|
||||
static const bts::api::admin::UserAccountPermissions* const get_group_user_permissions (const bts::api::admin::AdminData& admin_data,
|
||||
uint32_t group_id);
|
||||
////////////////////////////////////////////
|
||||
@@ -153,6 +160,8 @@ public:
|
||||
static uint32_t find_max_user_id (const bts::api::admin::AdminData& data);
|
||||
static const bts::api::admin::CMEUserData* get_user_cme_data(const bts::api::admin::BTSUser& user,
|
||||
const bts::api::admin::AdminData& data);
|
||||
static const bts::api::admin::SIMUserData* get_user_sim_data(const bts::api::admin::BTSUser& user,
|
||||
const bts::api::admin::AdminData& data);
|
||||
// finds the "exchange user" (ex: "operator_id" at CME or "authorized_trader"
|
||||
// at ICE) for a user
|
||||
static std::string find_user_exchange_id (const bts::api::admin::AdminData& data,
|
||||
@@ -243,4 +252,4 @@ public:
|
||||
static bool is_symbol_set (const std::string& symbol);
|
||||
static void bootstrap_data (bts::api::admin::AdminData& data);
|
||||
};
|
||||
}}
|
||||
}}
|
||||
@@ -190,11 +190,13 @@ message ExchangeUserMarketDataEntitlementData{
|
||||
message ExchangeUserData{
|
||||
CMEUserData cme_data = 10;
|
||||
ICEUserData ice_data = 20;
|
||||
SIMUserData sim_data = 30;
|
||||
}
|
||||
|
||||
message ExchangeAccountData{
|
||||
CMEAccountData cme_account_data = 10;
|
||||
ICEAccountData ice_account_data = 20;
|
||||
SIMAccountData sim_account_data = 30;
|
||||
}
|
||||
|
||||
message MarketDataEntitlement{
|
||||
@@ -207,6 +209,7 @@ message ExchangeConnection{
|
||||
oneof connection{
|
||||
CMEConnection cme_connection = 30;
|
||||
ICEConnection ice_connection = 40;
|
||||
SIMConnection sim_connection = 50;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +232,9 @@ message CMECGWConnection{
|
||||
string backup_id = 70;
|
||||
}
|
||||
|
||||
message SIMConnection {
|
||||
}
|
||||
|
||||
message CMEMSGWConnection{
|
||||
uint32 port = 10;
|
||||
string session_and_firm_id = 20;
|
||||
@@ -254,6 +260,12 @@ message CMEAccountData{
|
||||
map<string, string> misc_tags = 120;
|
||||
}
|
||||
|
||||
message SIMAccountData {
|
||||
api.orderman.CTICode cti = 10;
|
||||
api.orderman.CustomerOrFirm origin = 20;
|
||||
string clearing_account = 40;
|
||||
}
|
||||
|
||||
message CMEUserData{
|
||||
string operator_id = 10;
|
||||
string ats_operator_id = 20;
|
||||
@@ -263,6 +275,12 @@ message CMEUserData{
|
||||
map<string, string> misc_tags = 60;
|
||||
string smp_instruction = 70;
|
||||
string max_order_rate_per_connection = 80;
|
||||
string location = 90;
|
||||
}
|
||||
|
||||
message SIMUserData {
|
||||
string operator_id = 10;
|
||||
string location = 90;
|
||||
}
|
||||
|
||||
message CMEUserMarketDataEntitlementData{
|
||||
|
||||
@@ -68,6 +68,7 @@ message CustomOrderDefinition
|
||||
LeanOrderDefinition lean = 19;
|
||||
JoinOrderDefinition join = 20;
|
||||
TopOrderDefinition top = 21;
|
||||
TriggerOrderDefinition trigger = 22;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +85,7 @@ message TickOrderDefinition {uint32 tick_offset_on_deploy = 1; spark.QuantityMe
|
||||
message LeanOrderDefinition {spark.QuantityMeasure cross_threshold = 1; uint32 max_chase_ticks = 2; CustomOrderDefinition child = 3; }
|
||||
message JoinOrderDefinition {uint32 tick_offset_on_deploy = 1; spark.QuantityMeasure join_threshold = 2; bool enable_hedge = 3; CustomOrderDefinition hedge = 4; int32 hedge_ticks = 5; CustomOrderDefinition child = 6;}
|
||||
message TopOrderDefinition {uint32 watch_level_tick_offset = 1; spark.QuantityMeasure top_threshold = 2; bool enable_hedge = 3; CustomOrderDefinition hedge = 4; int32 hedge_ticks = 5; CustomOrderDefinition child = 6;}
|
||||
message TriggerOrderDefinition {TriggerCondition trigger_condition=1; TriggerAction trigger_action=2; CustomOrderDefinition child=3;}
|
||||
|
||||
message OrderRecord {
|
||||
uint32 key = 1 [(bts.api.key)=true];
|
||||
@@ -119,6 +121,7 @@ message OrderState {
|
||||
LeanOrderState lean = 18;
|
||||
JoinOrderState join = 19;
|
||||
TopOrderState top = 20;
|
||||
TriggerOrderState trigger = 21;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +136,12 @@ message Fill {
|
||||
double last_shares = 6;
|
||||
string fill_key = 7 [(bts.api.key)=true];
|
||||
google.protobuf.Timestamp timestamp = 8;
|
||||
|
||||
enum FillKind {
|
||||
FILL_KIND_STANDARD = 0;
|
||||
FILL_KIND_LEG = 1;
|
||||
}
|
||||
FillKind fill_kind = 10;
|
||||
}
|
||||
|
||||
enum OrderSubType {
|
||||
@@ -147,6 +156,9 @@ message RoutingRecord {
|
||||
string operator_id = 3;
|
||||
CTICode cti_code = 4;
|
||||
CustomerOrFirm customer_or_firm = 5;
|
||||
string smp_id = 6 ;
|
||||
SelfMatchPreventionInstruction smp_instruction = 7;
|
||||
string location = 8;
|
||||
}
|
||||
|
||||
message NativeOrderState {
|
||||
@@ -175,6 +187,7 @@ message TickOrderState {}
|
||||
message LeanOrderState {}
|
||||
message JoinOrderState {}
|
||||
message TopOrderState {}
|
||||
message TriggerOrderState {}
|
||||
|
||||
enum OrderStatus {
|
||||
|
||||
@@ -236,6 +249,13 @@ enum CTICode {
|
||||
CTI_CODE_CTI_CODE4 = 52;
|
||||
}
|
||||
|
||||
enum SelfMatchPreventionInstruction {
|
||||
SELF_MATCH_PREVENTION_INSTRUCTION_UNDEFINED = 0;
|
||||
SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_RESTING = 79;
|
||||
SELF_MATCH_PREVENTION_INSTRUCTION_CANCEL_AGRESSING = 78;
|
||||
|
||||
}
|
||||
|
||||
enum NativeOrderStatus
|
||||
{
|
||||
NATIVE_ORDER_STATUS_UNACKNOWLEDGED = 0;
|
||||
@@ -309,3 +329,46 @@ enum OrderError
|
||||
ORDER_ERROR_OK = 9;
|
||||
}
|
||||
|
||||
// Trigger Condition /////////////////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
enum TriggerType{
|
||||
STOP = 0;
|
||||
INVERSE_STOP = 1;
|
||||
}
|
||||
enum TriggerPriceType{
|
||||
BID = 0;
|
||||
ASK = 1;
|
||||
LAST_TRADE_PRICE = 2;
|
||||
}
|
||||
message TriggerQuantity{
|
||||
spark.QuantityMeasure quantity_measure = 10;
|
||||
uint32 threshold_quantity = 20;
|
||||
}
|
||||
message AbsolutePriceTrigger{
|
||||
// we can leave out qty for now
|
||||
TriggerQuantity trigger_quantity = 10;
|
||||
}
|
||||
message TrailingTrigger{
|
||||
TriggerType trigger_type = 10;
|
||||
int32 number_of_ticks = 20;
|
||||
}
|
||||
message TriggerCondition{
|
||||
TriggerPriceType price_type = 10;
|
||||
oneof trigger{
|
||||
AbsolutePriceTrigger absolute_price_trigger = 20;
|
||||
TrailingTrigger trailing_trigger = 30;
|
||||
}
|
||||
}
|
||||
// Trigger Action ////////////////////////////////////
|
||||
//////////////////////////////////////////////////////
|
||||
message TriggerPayupPrice{
|
||||
TriggerPriceType price_type = 10;
|
||||
int32 payup_ticks = 20;
|
||||
}
|
||||
message TriggerAction{
|
||||
uint32 security_id = 10;
|
||||
oneof orderprice{
|
||||
TriggerPayupPrice payup = 20;
|
||||
double price = 30;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
syntax="proto3";
|
||||
package bts.metrics;
|
||||
option csharp_namespace="bts.metrics";
|
||||
import "lib/api/apioptions.proto";
|
||||
import "lib/api/apicore.proto";
|
||||
|
||||
message Tick {
|
||||
int32 key = 1 [(bts.api.key)=true];
|
||||
uint64 timestamp = 2;
|
||||
}
|
||||
|
||||
message Report {
|
||||
string host_name = 1;
|
||||
string version = 2;
|
||||
uint64 timestamp = 10;
|
||||
}
|
||||
|
||||
service ShellLatencyService {
|
||||
rpc clock(bts.api.VoidMessage) returns (stream Tick);
|
||||
rpc report(Report) returns (bts.api.VoidMessage);
|
||||
}
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
MarketDataConfig {
|
||||
value {
|
||||
use_replay: true
|
||||
use_replay: false
|
||||
}
|
||||
}
|
||||
PlatformMap {
|
||||
value {
|
||||
glbx_platform: "PLATFORM_SIM"
|
||||
glbx_platform: "PLATFORM_MDP3ONIXS"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +48,3 @@ CMEOptions {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RecordOptions {
|
||||
rule: "System.platform == 'Windows'"
|
||||
value {
|
||||
output_path: "recording"
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,28 @@
|
||||
|
||||
SymbolList {
|
||||
value {
|
||||
symbol: "ES"
|
||||
symbol: "EW"
|
||||
symbol: "GC"
|
||||
symbol: "GE"
|
||||
symbol: "GF"
|
||||
symbol: "GLB"
|
||||
symbol: "HE"
|
||||
symbol: "LE"
|
||||
symbol: "NQ"
|
||||
symbol: "CL"
|
||||
symbol: "RB"
|
||||
symbol: "HO"
|
||||
symbol: "BZ"
|
||||
symbol: "NG"
|
||||
symbol: "GC"
|
||||
symbol: "SI"
|
||||
symbol: "SO"
|
||||
symbol: "ZB"
|
||||
symbol: "ZF"
|
||||
symbol: "ZN"
|
||||
symbol: "HG"
|
||||
symbol: "ZW"
|
||||
symbol: "ZC"
|
||||
symbol: "ZS"
|
||||
symbol: "ZM"
|
||||
symbol: "ZL"
|
||||
symbol: "ZO"
|
||||
symbol: "ZR"
|
||||
symbol: "ZB"
|
||||
symbol: "ZN"
|
||||
symbol: "ZF"
|
||||
symbol: "ZT"
|
||||
symbol: "UB"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +32,9 @@ Symbols {
|
||||
future: @SymbolList.symbol
|
||||
spread: @SymbolList.symbol
|
||||
}
|
||||
sim {
|
||||
future: @SymbolList.symbol
|
||||
spread: @SymbolList.symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 283 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user