Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,58 @@ 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)]
|
||||
public Fill.Types.FillKind[] fill_kind;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
fill_kind_filter.load_state(fs.fill_kind);
|
||||
}
|
||||
// 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,
|
||||
fill_kind_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, FillKind[]> fill_kind_filter { get; } = DiscreteValuesFilterVM.Create<PnLSummaryRow, FillKind>(EnumHelper.get_values<FillKind>().ToArray(), r => r.fill_kind, nameof(PnLSummaryRow.fill_kind));
|
||||
|
||||
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(),
|
||||
fill_kind = fill_kind_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,93 @@
|
||||
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="fill_kind" Header="Fill Kind" Binding="{Binding fill_kind}" IsReadOnly="true"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.fill_kind_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.fill_kind_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.fill_kind_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"
|
||||
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.FilterTemplate="{StaticResource DoubleMinMaxTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="sell_qty" Header="Sell Qty" Binding="{Binding sell_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.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="start_pnl" Header="Start PnL" IsReadOnly="true" SortMemberPath="start_pnl"
|
||||
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.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="pnl"/>
|
||||
<Binding Path="start_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 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.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="day_short" Header="Day Short" Binding="{Binding day_short_quantity}" IsReadOnly="true"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
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="day_pnl" Header="Day PnL" IsReadOnly="true" SortMemberPath="day_pnl"
|
||||
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="day_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"
|
||||
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.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="total_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 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 +161,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 +179,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 +203,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 +216,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 +229,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,27 @@ 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
|
||||
_start_of_trading_day_utc = DateTime.Now.Date.Add(start_of_day_ts).AddDays(-1).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.fill_kind) 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 +105,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 +132,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 +145,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 +165,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 = TickUtils.mid_tick(sec_md.security.ContractSpec, bid.price, ask.price); 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 +221,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 Fill.Types.FillKind fill_kind => fill.FillKind;
|
||||
|
||||
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, Fill.Types.FillKind fill_kind) 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?, Fill.Types.FillKind) 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);
|
||||
}
|
||||
|
||||
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;
|
||||
return distinct_cpps.First();
|
||||
} else {
|
||||
Logger.error(bts.log.Subsystem.Ui,
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
} else {
|
||||
Logger.error(bts.log.Subsystem.Ui, $"No cash multiplier found for Security {si}, Symbol {si.Symbol}");
|
||||
}
|
||||
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 {
|
||||
@@ -140,56 +176,78 @@ namespace mbts.spark {
|
||||
set => ignore();
|
||||
}
|
||||
|
||||
|
||||
public double buy_quantity {
|
||||
get => _buy_quantity;
|
||||
public Fill.Types.FillKind fill_kind => key.fill_kind;
|
||||
|
||||
|
||||
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 +279,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
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 281 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user