Compare commits

..
Author SHA1 Message Date
Pedro Pinto cfebab0488 Merged in api_improvements (pull request #405)
Api improvements

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2020-04-28 20:06:53 +00:00
Pedro Pinto eacd766359 Updated release notes. 2020-04-28 16:05:21 -04:00
Pedro Pinto 8fb8f8ff58 Merge branch 'master' into api_improvements 2020-04-28 15:58:43 -04:00
Pedro Pinto 41cf66a943 Added the hability to retrieve all securities in the API. 2020-04-24 08:00:13 -04:00
Pedro Pinto 1c57c9a012 Added position updates in the API. 2020-04-24 07:03:40 -04:00
Eric Harding 68a593af7b Add more info to system test 2020-04-17 16:48:10 -04:00
John Colvin 17c2321a82 Merged in SP-289-naudio-crash (pull request #407)
SP-289 naudio crash

Approved-by: Eric Harding <eric.harding@bluetradesys.com>
2020-04-16 18:11:18 +00:00
John Colvin 0affc8a942 SP-289 naudio crash 2020-04-16 12:07:44 -05:00
John Colvin ce36ea4109 move Delegates.cs to spark_core 2020-04-16 10:47:41 -05:00
Pedro Pinto cb268f505a Added settlements to the new algo API. 2020-04-13 08:19:55 -04:00
Pedro Pinto dfd93b836b Updated externals 2020-04-13 07:44:52 -04:00
Pedro Pinto 1ba6e3280c Updated CMakeDeps 2020-04-06 09:38:19 -04:00
Eric Harding 3a6f50dfea Merge branch 'prod' of bitbucket.org:bluetradesys/spark 2020-04-03 15:40:30 -04:00
Eric Harding d711f99548 fix externals pointer 2020-04-03 15:40:15 -04:00
pfisher 07ace80871 fixed keycode.conf 2020-04-03 13:40:56 -05:00
John Colvin 93c6277bfc Merged prod into master 2020-04-03 13:20:33 -05:00
Jake Norbut 7ac9d05ae2 Merged in manual_fills_permissions (pull request #403)
add permission filter to manual fills

Approved-by: John Colvin <john.colvin@bluetradesys.com>
2020-04-03 18:18:16 +00:00
Eric Harding 5a4b31ccc0 add cpp sample to service.md api page 2020-03-20 12:10:40 -04:00
John Colvin fb568aab39 SP-288 ladder crash (IndexOutOfRange ex accessing price array) 2020-03-19 15:28:27 -05:00
Eric Harding 64416de10d Fix the braces in my sample. 2020-03-16 20:41:23 -04:00
Eric Harding 46953cb639 add c# market data sample 2020-03-16 20:39:48 -04:00
Eric Harding 7e68a235ee Add securities sub to client_sdk_example 2020-03-16 15:18:39 -04:00
Jake Norbut 85f2285ac0 add permission filter to manual fills 2020-03-09 09:28:23 -05:00
Eric Harding 70e0979836 override sim port for sdk image 2020-03-06 12:45:09 -05:00
Kevin Darby 1f80c3366e Merged in master (pull request #401)
Master
2020-03-03 19:16:43 +00:00
Kevin Darby 19e66a1c32 add a new stunnel path for Pete 2020-02-25 11:16:40 -05:00
56 changed files with 539 additions and 215 deletions

No files matched your search

@@ -124,4 +124,17 @@ service OrderService {
```
### C++ Example
```cpp
#include "lib/api/public/ServiceContext.h"
#include "lib/api/services/OrderService.client.pb.h"
using namespace bts::api;
ServiceContext ctx;
auto orders = ctx.locate<orderman::OrderService>();
auto unsub = orders.subscribe_fills().subscribe([](const auto& update) {
std::cout
<< "Added " << update.added().size() << " items, "
<< "Removed " << update.removed().size() << " items, "
<< "Updated " << update.updated().size() << " items, "
<< "Total items " << update.all().size() << std::endl;
});
std::this_thread::sleep_for(std::chrono::minutes(5));
```
+49 -1
View File
@@ -1,3 +1,51 @@
+++
title = "Market Data"
+++
+++
Use the MarketDataService to subscribe enumerate all available securities.
```cs
using System;
using System.Threading;
using bts.api;
using bts.api.core;
namespace sample {
class Program {
static void Main(string[] args) {
using var context = new ServiceContext();
var md = context.locate<MarketDataService>();
using var sub = md.subscribe_securities().Subscribe(update => {
foreach (var security in update.added) {
Console.WriteLine($"Added security {security.Id}: {security.Symbol}");
}
});
Thread.Sleep(TimeSpan.FromMinutes(5));
}
}
}
```
```cpp
#include "lib/api/public/ServiceContext.h"
#include "spark/lib/domain_model/Security.h"
#include "lib/api/services/MarketDataService.client.pb.h"
#include <thread>
using namespace bts::api;
int main(int argc, char** argv) {
ServiceContext ctx;
auto md_service = ctx.locate<bts::api::MarketDataService>();
auto sub = md_service.subscribe_securities().subscribe([](const auto& update) {
for (const auto& s : update.added()) {
std::cout << "Added security: " << s->id() << ": " << s->symbol() << std::endl;
}
});
std::this_thread::sleep_for(std::chrono::minutes(5));
return 0;
}
```
+7
View File
@@ -4,6 +4,13 @@ weight = 3
sort_by = "weight"
insert_anchor_links = "right"
+++
## 2.15.10
### Bug Fixes
- Solved an issue that could cause the user interface to fail when playing some alert sounds
### API Changes
- Added settlement and position information to the API
- Added the ability for user algos to report custom state to the user interface
- Added the ability for algos to discover all active securities
## 2.15.3
### Bug Fixes
- Fixed a bug where some CME currency spreads exhibited incorrect tick values
+10 -1
View File
@@ -115,7 +115,7 @@ namespace mbts.ui.shell
{
try
{
var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\BTS\\stunnel\\";
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\BTS\\stunnel\\";
if (File.Exists(path + "config\\stunnel.conf"))
{
uint job_error = BTSUINativeMethods.setup_process_job_object();
@@ -124,6 +124,15 @@ namespace mbts.ui.shell
kill_stunnel();
start_stunnel(path, job_error);
}
path = AppDomain.CurrentDomain.BaseDirectory + "\\..\\stunnel\\";
if (File.Exists(path + "config\\stunnel.conf"))
{
uint job_error = BTSUINativeMethods.setup_process_job_object();
//if we can't put this process and stunnel into a job, try to stop stunnel then restart
if (job_error != 0)
kill_stunnel();
start_stunnel(path, job_error);
}
}
catch(Exception ex)
{
@@ -1,7 +1,6 @@
using bts.api;
using bts.co2;
using bts.utils;
using bts.utils.csv;
using mbts;
using mbts.spark;
using mbts.ui.controls;
@@ -45,18 +44,6 @@ namespace bts.shell.time_and_sales {
public void raise_invalidated() =>
invalidated?.Invoke();
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(
typeof(TimeAndSalesRowVM),
new string[] {
nameof(security),
nameof(time),
nameof(price),
nameof(size),
nameof(aggressor_side),
nameof(security_key),
});
public bool Equals(TimeAndSalesRowVM other) =>
other != null &&
row_key == other.row_key;
@@ -2,7 +2,6 @@
using bts.shell.fills;
using bts.spark;
using bts.view;
using bts.utils.csv;
using mbts;
using mbts.co2;
using mbts.persist;
@@ -115,8 +114,6 @@ namespace bts.shell.time_and_sales {
show_in_context_menu = _can_pause_updates
};
_commands = new SparkCommand[] {
// csv export
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.rows.ToCsv(TimeAndSalesRowVM.csv_ordered_properties), "TaS")),
// ladder
SparkCommands.ShowLadderAutoLookup(SparkCommands.KeyGestures.ShowLadder)
.complete(current_sec_key_src, sec_keys => sec_keys.Count > 0),
@@ -5,7 +5,6 @@ using bts.collections;
using bts.shell.fills;
using bts.spark;
using bts.utils;
using bts.utils.csv;
using mbts.collections;
using mbts.persist;
using mbts.spark.filters;
@@ -331,27 +330,6 @@ namespace mbts.spark {
public override bool Equals(object obj) => Equals(obj as Row);
public override int GetHashCode() => _fill.FillKey.GetHashCode() ^ _order.Key.GetHashCode();
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(typeof(Row),
new string[] {
nameof(security),
nameof(order_type),
nameof(root_algo_display_name),
nameof(side),
nameof(fill_time),
nameof(user),
nameof(account),
nameof(exchange),
nameof(time_in_force),
nameof(size),
nameof(price),
nameof(fill_kind),
nameof(fill_status),
nameof(fill_key),
nameof(order_key),
nameof(security_key)
});
public event Action invalidated;
void IRowVM.raise_invalidated() => invalidated?.Invoke();
}
@@ -4,7 +4,6 @@ using bts.shell;
using bts.shell.fills;
using bts.spark;
using bts.view;
using bts.utils.csv;
using mbts.persist;
using mbts.ui;
using mbts.ui.settings;
@@ -62,7 +61,6 @@ namespace mbts.spark {
_view = new FillsView { DataContext = _vm };
_commands = new List<SparkCommand> {
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.rows.ToCsv(FillsVM.Row.csv_ordered_properties), "Fills")),
// wrap the grid settings view factory in a dialogCommand here since the vm is destroyed/recreated when the security criteria changes
SparkCommands.window_settings_command(new DialogCommand(() => {
var vm = new ShellSettingsDialogVM<GridSettings, SimpleGridSettingsVM>(
@@ -534,8 +534,7 @@ namespace mbts.spark {
_size_from_row(row, _price_list, _asks, _vm.security.ContractSpec);
uint _volume_from_row(int row) {
if (_price_list != null) {
var price = _price_list[row];
if (_price_list.SafeTryGetAt(row, out var price)) {
var vapls = _volume_container.read_new_value();
if (vapls != null) {
return vapls.SelectFirstOrDefault(v => MathUtils.epsilon_equals(v.Price, price), v => v.Volume);
@@ -2,6 +2,7 @@ using bts.api;
using bts.api.core;
using bts.api.position;
using bts.collections;
using bts.utils;
using mbts;
using mbts.spark;
using mbts.ui;
@@ -4,7 +4,6 @@ using bts.api.orderman;
using bts.collections;
using bts.spark;
using bts.utils;
using bts.utils.csv;
using mbts.ui;
using mbts.ui.controls;
using System;
@@ -12,7 +11,6 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Reactive.Linq;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Windows.Input;
using System.Windows.Media;
using Columns = mbts.spark.OrderBookGridView.ColumnKeys;
@@ -62,35 +60,6 @@ namespace mbts.spark {
public event Action<(OrderSecurityRootPair oldval, OrderSecurityRootPair newval)> updated;
public event PropertyChangedEventHandler PropertyChanged;
public static System.Collections.Specialized.OrderedDictionary csv_ordered_properties =>
CsvHelper.create_ordering_maintainer(
typeof(OrderBookRowVM),
new string[] {
nameof(security_text),
nameof(symbol),
nameof(time_created),
nameof(last_change),
nameof(user),
nameof(account),
nameof(exchange),
nameof(time_in_force),
nameof(order_type),
nameof(root_algo_display_name),
nameof(order_sub_type),
nameof(side),
nameof(quantity),
nameof(filled_quantity),
nameof(price),
nameof(avg_price),
nameof(last_price),
nameof(piq),
nameof(status),
// last message (could contains commas, so skipping)
nameof(security_key),
nameof(order_key),
nameof(parent_order_key),
});
// Core
public OrderSecurityRootPair order_tuple { get; private set; }
public OrderRecordImmutable order => order_tuple.order.order;
@@ -4,7 +4,6 @@ using bts.shell;
using bts.shell.fills;
using bts.shell.orderbook;
using bts.spark;
using bts.utils.csv;
using bts.view;
using mbts.persist;
using mbts.ui;
@@ -195,7 +194,6 @@ namespace mbts.spark {
show_grid_settings_command.group = CommandGroups.Auxiliary;
_commands = new SparkCommand[] {
new SparkCommandCommand("Export to csv", Icons.Export, FileDialogCommand.export_csv(() => _vm.rows.ToCsv(OrderBookRowVM.csv_ordered_properties), "OrderBook")),
toggle_show_right_panel_command,
attach_selected_orders_command,
detach_selected_orders_command,
+18 -5
View File
@@ -2,6 +2,7 @@
using bts.api;
using bts.shell;
using bts.spark;
using bts.utils;
using mbts.config;
using mbts.persist;
using mbts.spark;
@@ -114,6 +115,7 @@ namespace mbts.ui {
void dispose_core() {
Disposable.dispose(ref _settings_sub);
Disposable.dispose(ref _toast_controller);
Disposable.dispose(ref _sound_player);
if (_dispatcher != null && !_dispatcher.HasShutdownStarted) {
_dispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
}
@@ -158,15 +160,25 @@ namespace mbts.ui {
}
}
class SoundNotificationPlayer {
readonly IReadOnlyDictionary<SoundNotification.SystemSound, NAudioPlayer> _players;
public static SoundNotificationPlayer create() => new SoundNotificationPlayer(_all_players());
class SoundNotificationPlayer : IDisposable {
IReadOnlyDictionary<SoundNotification.SystemSound, NAudioPlayer> _players;
SoundNotificationPlayer(IReadOnlyDictionary<SoundNotification.SystemSound, NAudioPlayer> players) =>
_players = players;
public static SoundNotificationPlayer create() =>
new SoundNotificationPlayer(_all_players());
public void Dispose() {
if (_players != null) {
_players.Values.DisposeAll();
_players = null;
}
}
public void play_sounds(IEnumerable<SoundNotification> sounds) {
if (_players == null)
return;
foreach (var s in sounds) {
switch (s.sound_type_case) {
case SoundNotification.SoundTypeOneofCase.System: _players.GetValueOrDefault(s.system_sound.Value)?.play(); break;
@@ -177,7 +189,8 @@ namespace mbts.ui {
}
static IReadOnlyDictionary<SoundNotification.SystemSound, NAudioPlayer> _all_players() =>
EnumHelper.get_values<SoundNotification.SystemSound>().ToReadOnlyDictionary(s => s, NAudioPlayer.create);
EnumHelper.get_values<SoundNotification.SystemSound>()
.ToReadOnlyDictionaryWhere<SoundNotification.SystemSound, NAudioPlayer>(NAudioPlayer.try_create);
}
// This class adopts to role of view-controller in the mvc paradigm, rather than view-model in mvvm.
+75 -22
View File
@@ -1,4 +1,5 @@
using bts.shell;
using bts;
using bts.shell;
using mbts.ui.utility;
using NAudio.Wave;
using System;
@@ -6,29 +7,71 @@ using System.IO;
using System.Threading.Tasks;
namespace mbts.ui {
class NAudioPlayer {
class NAudioPlayer : IDisposable {
// number of milliseconds to delay playing sound,
// during which time all subsequent requests to play the same sound will be ignored.
const int _delay_ms = 75;
readonly int _start_position;
IDisposable _stream_in;
WaveOutEvent _wave_out = new WaveOutEvent();
WaveFileReader _wave_reader;
bool _is_playing, _is_play_requested;
public NAudioPlayer(Stream uwave_stream, int start_position = 0) {
var ms = new MemoryStream();
uwave_stream.CopyTo(ms);
ms.Position = 0;
_wave_reader = new WaveFileReader(ms);
/// <paramref name="stream_in"/> should not be disposed by the caller,
/// as NAudioPlayer handles that in its own Dispose method;
/// this constructor is private is to prevent such misuse.
NAudioPlayer(Stream stream_in, int start_position = 0) {
_stream_in = stream_in;
_wave_reader = new WaveFileReader(stream_in);
_wave_reader.Position = _start_position = (start_position < _wave_reader.Length ? start_position : 0);
_wave_out.Init(_wave_reader);
_wave_out.PlaybackStopped += _on_playback_stopped;
}
public static NAudioPlayer create(SoundNotification.SystemSound system_sound) {
var stream = _get_stream(system_sound);
var sb = EnumHelper.get_attribute<SoundNotification.SystemSound, SilenceBufferAttribute>(system_sound);
return sb != null ? new NAudioPlayer(stream, sb.start_position) : new NAudioPlayer(stream);
/// <see cref="_playback_stopped"/> is a proxy/buffer for the _wave_out.PlaybackStopped event.
/// It serves a couple purposes:
/// 1) The main callback (<see cref="_on_playback_stopped(object, StoppedEventArgs)"/>)
/// is guarenteed to be executed first (though it would be anyways, since that
/// callback is hooked up in the constructor, the proxy event makes it obvious).
/// 2) <see cref="_playback_stopped"/> is only raised after playback is truly complete,
/// meaning there are no pending requests to repeat the sound.
event Action _playback_stopped;
public static bool try_create(SoundNotification.SystemSound system_sound, out NAudioPlayer result) {
if (_try_get_stream(system_sound, out var stream)) {
var sb = EnumHelper.get_attribute<SoundNotification.SystemSound, SilenceBufferAttribute>(system_sound);
result = sb != null ? new NAudioPlayer(stream, sb.start_position) : new NAudioPlayer(stream);
return true;
}
result = null;
return false;
}
public static void play(SoundNotification.SystemSound system_sound) {
if (try_create(system_sound, out var player)) {
try {
var temp = player;
temp._playback_stopped += temp.Dispose;
player.play();
player = null;
} catch (Exception ex) {
Log.Ui.warn($"Encountered exception playing the system sound '{system_sound}': {ex}.");
} finally {
player?.Dispose();
}
} else {
Log.Ui.warn($"Unable to play the system sound '{system_sound}': unexpected sound type (did not find an audio stream that corresponds to this enum value).");
}
}
public void Dispose() {
if (_wave_out != null) {
_wave_out.PlaybackStopped -= _on_playback_stopped;
_wave_out.Dispose();
_wave_out = null;
}
Disposable.dispose(ref _wave_reader);
Disposable.dispose(ref _stream_in);
}
public void play() {
@@ -46,6 +89,8 @@ namespace mbts.ui {
if (_is_play_requested) {
_is_play_requested = false;
_play_core();
} else {
_playback_stopped?.Invoke();
}
}
@@ -56,18 +101,26 @@ namespace mbts.ui {
_is_playing = false;
}
static Stream _get_stream(SoundNotification.SystemSound system_sound) {
static bool _try_get_stream(SoundNotification.SystemSound system_sound, out Stream stream) {
UnmanagedMemoryStream um_stream;
switch (system_sound) {
case SoundNotification.SystemSound.GameCorrect: return bts.Properties.Resources.game_sound_correct;
case SoundNotification.SystemSound.GameIncorrect: return bts.Properties.Resources.game_sound_wrong;
case SoundNotification.SystemSound.GameOverArcade: return bts.Properties.Resources.game_over_arcade;
case SoundNotification.SystemSound.Whistle: return bts.Properties.Resources.whistle;
case SoundNotification.SystemSound.BellCounter: return bts.Properties.Resources.bell_counter;
case SoundNotification.SystemSound.Powerup: return bts.Properties.Resources.powerup;
case SoundNotification.SystemSound.CashRegister: return bts.Properties.Resources.cash_register_purchase;
case SoundNotification.SystemSound.Reload: return bts.Properties.Resources.reload_1911;
default: throw new NotImplementedException($"Unknown {nameof(SoundNotification.SystemSound)}: {system_sound}.");
case SoundNotification.SystemSound.GameCorrect: um_stream = bts.Properties.Resources.game_sound_correct; break;
case SoundNotification.SystemSound.GameIncorrect: um_stream = bts.Properties.Resources.game_sound_wrong; break;
case SoundNotification.SystemSound.GameOverArcade: um_stream = bts.Properties.Resources.game_over_arcade; break;
case SoundNotification.SystemSound.Whistle: um_stream = bts.Properties.Resources.whistle; break;
case SoundNotification.SystemSound.BellCounter: um_stream = bts.Properties.Resources.bell_counter; break;
case SoundNotification.SystemSound.Powerup: um_stream = bts.Properties.Resources.powerup; break;
case SoundNotification.SystemSound.CashRegister: um_stream = bts.Properties.Resources.cash_register_purchase; break;
case SoundNotification.SystemSound.Reload: um_stream = bts.Properties.Resources.reload_1911; break;
default:
stream = null;
return false;
}
stream = new MemoryStream();
um_stream.CopyTo(stream);
um_stream.Dispose();
stream.Position = 0;
return true;
}
}
}
}
+5 -2
View File
@@ -215,8 +215,11 @@ namespace mbts.spark {
FillTuple.key_fn);
on_fill_tuples_with_root = fill_order_tuples_with_root_join.Cache();
// manual fills
on_manual_fills_securities = _position_service.subscribei_manual_fills()
.Naggle(interval, scheduler)
on_manual_fills_securities = admin_model
.permission_filter(
_position_service.subscribei_manual_fills()
.Naggle(interval, scheduler),
f => f.AccountId)
.Filter(f => f.FillType == ManualFill.Types.FillType.Local)
.join(sstore, mf => mf.SecurityKey)
.Table().Cache();
@@ -38,11 +38,7 @@ namespace mbts.ui.controls {
}
public static ICommand PreviewSoundCommand =>
new DelegateCommand<SoundNotification.SystemSound>(s => {
try {
NAudioPlayer.create(s).play();
} catch (NAudio.MmException) { }
});
new DelegateCommand<SoundNotification.SystemSound>(NAudioPlayer.play);
#region SoundTypes
public static readonly DependencyProperty SoundTypesProperty =
@@ -64,25 +64,6 @@ namespace mbts.ui {
}
}, can_execute == null ? null : new Func<object, bool>(_ => can_execute()));
public static IDialogCommand export_csv(
Func<string> get_file_content,
string source_window_name) =>
new AnonymousFileDialogCommand<object>(_ =>
_create_save_file_dialog(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
$"BTS_Spark_{source_window_name}_{DateTime.Now.ToString("yyyyMMdd_HHmm")}.csv",
new[] { ("CSV (comma delimited)", new string[] { "csv" }) }
),
(_, file) => {
try {
File.WriteAllText(file, get_file_content.Invoke());
} catch (Exception ex) {
BTSMessageBox.Show($"Error exporting data to csv: {ex}.", "CSV Export Error", BTSMessageBoxButtons.OK, System.Windows.MessageBoxImage.Error, true, false, true);
}
},
// we are always able to perform an export, even when no rows (it will just be a header)
(_) => true);
static TDialog _create_file_dialog<TDialog>(string initial_dir = null, IReadOnlyList<(string name, string[] extensions)> filter_items = null) where TDialog : FileDialog, new() {
var rv = new TDialog();
if (!string.IsNullOrEmpty(initial_dir))
@@ -390,6 +390,15 @@ namespace System.Collections.Generic {
public static ReadOnlyDictionary<TKey, TElement> ToReadOnlyDictionary<T, TKey, TElement>(this IEnumerable<T> enumerable, Func<T, TKey> key_selector, Func<T, TElement> element_selector, IEqualityComparer<TKey> comparer) =>
new ReadOnlyDictionary<TKey, TElement>(enumerable.ToDictionary(key_selector, element_selector, comparer));
public static ReadOnlyDictionary<TKey, TElement> ToReadOnlyDictionaryWhere<TKey, TElement>(this IEnumerable<TKey> keys, TryGetValue<TKey, TElement> value_selector) {
var mdict = new Dictionary<TKey, TElement>();
foreach (var k in keys) {
if (value_selector(k, out var val))
mdict[k] = val;
}
return new ReadOnlyDictionary<TKey, TElement>(mdict);
}
public static ILookup<TKey, TElement> ToFlatLookup<T, TKey, TElement>(this IEnumerable<T> source, Func<T, TKey> key_selector, Func<T, IEnumerable<TElement>> selector) =>
source.SelectMany(v => {
var k = key_selector(v);
@@ -103,6 +103,15 @@ namespace bts.collections {
return array;
}
public static bool SafeTryGetAt<T>(this IList<T> list, int index, out T value) {
if (list != null && index >= 0 && index < list.Count) {
value = list[index];
return true;
}
value = default;
return false;
}
public static bool TryGetAt<T>(this IList<T> list, int index, out T value) {
if (index >= 0 && index < list.Count) {
value = list[index];
-52
View File
@@ -1,52 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
namespace bts.utils.csv {
public static class CsvHelper {
public static string ToCsv<T>(this IEnumerable<T> items, OrderedDictionary properties = null) where T : class {
var output = "";
var delimiter = ",";
if (properties == null) {
properties = new OrderedDictionary();
foreach (var pi in typeof(T).GetProperties()) {
properties.Add(pi.Name, pi);
}
}
using (var sw = new StringWriter()) {
var dict_values = properties.Values.OfType<PropertyInfo>();
var header = dict_values.Select(n => n.Name).Aggregate((first, second) => first + delimiter + second);
sw.WriteLine(header);
foreach (var i in items) {
var row = dict_values.Select(x => x.GetValue(i, null))
.Select(x => x == null ? "null" : x.ToString())
.Aggregate((first, second) => first + delimiter + second);
sw.WriteLine(row);
}
output = sw.ToString();
}
return output;
}
public static OrderedDictionary create_ordering_maintainer(Type t, string[] property_names_in_order) {
OrderedDictionary od = new OrderedDictionary();
var props = t.GetProperties();
foreach (var n in property_names_in_order) {
var query = props.Where(pi => pi.Name == n);
if (query.Any()) {
var property_info = query.First();
if (!od.Contains(property_info)) {
od.Add(n, property_info);
}
}
}
return od;
}
}
}
@@ -1,11 +1,11 @@
using System;
using System.Globalization;
namespace mbts.utils {
delegate bool TryGetValue<T>(out T value);
delegate bool TryGetValue<TKey, TValue>(TKey key, out TValue value);
namespace bts.utils {
public delegate bool TryGetValue<T>(out T value);
public delegate bool TryGetValue<TKey, TValue>(TKey key, out TValue value);
static class LogicalDelegates {
public static class LogicalDelegates {
public static readonly Func<bool, bool, bool>
And = (a, b) => a && b,
Or = (a, b) => a || b,
@@ -13,7 +13,7 @@ namespace mbts.utils {
None = (a, b) => !a && !b;
}
static class FormattingDelegates {
public static class FormattingDelegates {
public static class Price {
public static readonly Func<double, string> Value =
p => double.IsNaN(p) ? "" : p.ToString("#,0.00");
@@ -26,4 +26,4 @@ namespace mbts.utils {
p => p.ToString("#,0.00", CultureInfo.CurrentCulture);
}
}
}
}
@@ -61,7 +61,7 @@ public static async Task<IEnumerable<SecurityImmutable>> get_multiple_securities
return Enumerable.Empty<SecurityImmutable>();
}
public static async Task<IEnumerable<FutureContract>> get_expirations(MarketDataRelay relay, string symbol, SecurityType security_type, MDRequest.Types.Flags flags = MDRequest.Types.Flags.Bbo, Venue v = Venue.Sim) {
public static async Task<(bool, List<FutureContract>)> get_expirations(MarketDataRelay relay, string symbol, SecurityType security_type, MDRequest.Types.Flags flags = MDRequest.Types.Flags.Bbo, Venue v = Venue.Sim) {
var request = new MDRequest() {
Host = _MD_HOST,
Channels = {
@@ -89,7 +89,7 @@ public static async Task<IEnumerable<SecurityImmutable>> get_multiple_securities
}
}
return (IEnumerable<FutureContract>) futures;
return (result.success, futures);
}
}
@@ -22,8 +22,9 @@ namespace bts.tests
public class SpreaderUtilities {
public static async Task<(SecurityImmutable s1, SecurityImmutable s2)> get_securities_for_common_setup_one(MarketDataRelay relay, string symbol) {
var exps = (await MarketDataUtilities.get_expirations(relay, symbol, SecurityType.Future)).ToList();
var (success, exps) = await MarketDataUtilities.get_expirations(relay, symbol, SecurityType.Future);
Assert.IsTrue(success, "Could not reach market data service");
Assert.IsNotNull(exps);
Assert.IsTrue(exps.Count() > 2, "Need at least two expirations");
+1 -1
View File
@@ -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/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.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/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.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/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.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;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/Product.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityMapper.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/service/MarketDataServiceServer.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;md/vendor/OnixsUtils.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metricsLine truncated
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/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.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/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.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/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.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;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/Product.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityMapper.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/service/MarketDataServiceServer.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;md/vendor/OnixsUtils.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metricsLine truncated
+3 -6
View File
@@ -1,13 +1,10 @@
#nusers: 1
#allow concurrent : true
#expires: 2020-Mar-31 00:00:00 637212096000000000
#generation_time: 2020-Jan-06 18:56:23.406931 5248825356261457214
#expires: 2021-Jan-01 00:00:00 637450560000000000
#generation_time: 2020-Apr-03 18:39:31.010863 5248901378137496534
EncryptedKeyCode {
value {
keycode: "Ixb/z6hwFQmojpm4GwNEpQmsqpYT9OTHbcKtROIqj38GwGqyVn54JMDB04V048quO+NAI78DKJXsUWgjDH0y5HQlwN6Lq48s/bdwKqR1OTBPr6xoTImtCb+l5b+8fUGlg7t3X8gfMdxohHgcUi9k9n7ZrnNvqcjdYKoP0CaTZT8zHuBQx3f4Lqrz32gq9rJ5skRjH6ZqNblNdlsF+kOAqXCkvI5p0QfyKgtCodr33qsW2cbqY5gUD9KwsnmFVa8/zuFBucsoM7dHB47yRaIeOFFu7xGayAdAHA8SwLyoxE5y+adIX1SYnAiqAu1raZpeXgRE1LNQk3c7uhUjt0coag=="
keycode: "BJBQWsbdTXWa/iLmOf3ekE8vP/NYkf0Q/nUsp/rJ78Ze3W9pNXICCMabmmsr1xOM7aqRIclPgZd955uSSfvFkdNWFTxwPQ5IfnDGopj3QdhUVxhzgVLaXHN+vC0H/MWrDQ5K4WY5J/6VXgUn1LSN/2HNb5iR2sKXeh3Alamg87YtCWEi+scelffE7HK5ERT/3A3Y/e0pqUFhP6xouIjHRhC2FRBb6OoJQVvFwVDADSuKZNo9B6GhC3H0YdHqjFhGDoky5ecgzeKIX8uWJEQNnQXQJ0iTgQ95+YR5IMU+XWdlVfSrc0fuTmj3YWcBQ8Kl2ItEcobyZzJYega3tu6Ftw=="
}
}
+5
View File
@@ -901,6 +901,10 @@ public:
return _cache.get_security(key);
}
std::vector<api::Security const *> get_securities() const {
return _cache.get_securities();
}
MDConsumerChannel get_channel(const std::string& symbol, SecurityType type, Venue exchange) const {
for (auto& inst : _instances) {
if ((inst->symbol() == symbol && inst->type() == type && inst->exchange() == exchange) ||
@@ -976,6 +980,7 @@ size_t MDConsumer::walk_security_list(SecurityCallback cb, const string& symbol,
size_t MDConsumer::scan_securities(bool resolve_legs) { return _pimpl->scan_securities(resolve_legs); }
void MDConsumer::on_security(SecurityCallback cb, bool resolve_legs) { _pimpl->on_security(cb, resolve_legs); }
const api::Security* MDConsumer::get_security(SecurityKey key) const { return _pimpl->get_security(key).get(); }
std::vector<api::Security const*> MDConsumer::get_securities() const {return _pimpl->get_securities();}
MDConsumerChannel MDConsumer::get_channel(const std::string& symbol, SecurityType type, Venue exchange) const {
return _pimpl->get_channel(symbol, type, exchange);
+3
View File
@@ -59,6 +59,9 @@ public:
// You still need to call scan_securities() periodically to discover new securities
const api::Security* get_security(SecurityKey key) const;
// Retrieve all cached securities
std::vector<api::Security const*> get_securities() const;
MDConsumerChannel get_channel(const std::string& symbol, SecurityType type, Venue exchange) const;
MDConsumerChannel get_channel(const UserSymbol&) const;
+12 -1
View File
@@ -69,6 +69,17 @@ SecurityPtr SecurityCache::get_security(SecurityKey key) {
}
}
std::vector<api::Security const*> SecurityCache::get_securities()
{
std::vector<api::Security const*> result;
result.reserve(100000);
std::unique_lock<std::mutex> lock(_me->cache_mutex);
for (auto const&p : _me->cache)
result.push_back(p.second.get());
return result;
}
SecurityPtr SecurityCache::add_db_security(const SecurityPtr& security) {
bts_assert(security);
std::lock_guard<std::mutex> lock(_me->cache_mutex);
@@ -90,4 +101,4 @@ bool SecurityCache::add_live_security(const SecurityPtr& security) {
void SecurityCache::flush_to_disk() { _me->flush(); }
}}//ns
}}//ns
+4 -1
View File
@@ -22,6 +22,9 @@ public:
//Load security from cache with fallback to database
SecurityPtr get_security(SecurityKey key);
// Load all securities from cache, no fallback to database
std::vector<api::Security const*> get_securities();
// Get the security if it's in the cache otherwise return nullptr. Does not query security database.
SecurityPtr try_get_security(SecurityKey key) const;
@@ -33,4 +36,4 @@ public:
};
}}//ns
}}//ns
+5 -1
View File
@@ -10,4 +10,8 @@ const api::Security* SecurityOracle::get_security(SecurityKey key) const {
return _client.current_consumer().get_security(key);
}
}}//namespace
std::vector<api::Security const*> SecurityOracle::get_securities() const {
return _client.current_consumer().get_securities();
}
}}//namespace
+4 -1
View File
@@ -1,6 +1,8 @@
#pragma once
#include "lib/domain_model/SecurityKey.h"
#include <memory>
#include <vector>
namespace bts {
namespace api {class Security;}
@@ -12,6 +14,7 @@ public:
SecurityOracle(const MDClient&);
~SecurityOracle();
const api::Security* get_security(SecurityKey key) const;
std::vector<api::Security const*> get_securities() const;
};
}}//namespace
}}//namespace
@@ -157,6 +157,7 @@ void OrderManager::on_filled_position(
}
for (auto const &ev : *(update.events)) {
me->services_data.position.update(ev);
me->services_data.limiter.set_filled_position(
*ev.security, ev.account_id, ev.long_quantity, ev.short_quantity);
}
@@ -550,6 +551,8 @@ void OrderManager::init_order_services() {
std::make_unique<ContentSubscription<TimerEvent, NoKey>>(*this, log);
me->services_data.order_creation_subs =
std::make_unique<ContentSubscription<CreateOrderReplyEvent>>(*this, log);
me->services_data.position_subs =
std::make_unique<ContentSubscription<FilledPositionEvent,NoKey>>(*this, log);
}
void OrderManager::after_event() {
@@ -32,6 +32,7 @@ void OrderServices::init(OrderServicesData &data) {
_status_updates.init(*data.status_subs);
_settlement_updates.init(*data.settlement_subs);
_time_change.init(*data.time_subs);
_position_update.init(*data.position_subs);
if (_sessions.empty())
for (auto &sd : data.sessions)
_sessions.emplace_back(*sd);
@@ -8,6 +8,7 @@
#include "lib/orderman/actors/order_manager/SessionServices.h"
#include "lib/orderman/events/TickerUpdateEvent.h"
#include "lib/orderman/events/StatusUpdateEvent.h"
#include "lib/orderman/events/FilledPositionEvent.h"
#include "lib/orderman/governor/Governor.h"
#include "lib/orderman/orderman_utils/EventStream.h"
#include "lib/orderman/orders/Order.h"
@@ -62,6 +63,10 @@ class OrderServices {
_data->log.log(corid(), entry_kind, uint64_1, txt_1, uint32_1);
}
std::vector<PositionUpdate> const& current_positions() {
return _data->position.current_positions();
}
trace::ScopedPerfLogEntry log_scope(const char *entry_kind,
uint64_t uint64_1 = 0,
const char *txt_1 = "",
@@ -123,6 +128,15 @@ class OrderServices {
void unsubscribe_from_time() { _time_change.unsub(_time_change_sub); }
// Time
// Position Updates
template <class CB> void subscribe_to_position_updates(CB &&cb) {
_position_update_sub = _position_update.sub(
[this, cb](auto const &ev) { this->on_external_event(ev, cb); });
}
void unsubscribe_from_position_updates() { _position_update.unsub(_position_update_sub); }
// Position Updates
// Execution Reports
using ExeReportSubID = EventStream<NewExecutionReportEvent>::SubID;
template <class CB>
@@ -298,8 +312,10 @@ class OrderServices {
EventStream<StatusUpdateEvent> _status_updates;
EventStream<SettlementUpdateEvent> _settlement_updates;
EventStream<mercury::TimerEvent, mercury::NoKey> _time_change;
EventStream<orderman::FilledPositionEvent, mercury::NoKey> _position_update;
boost::container::small_vector<SessionServices, 20> _sessions;
EventStream<mercury::TimerEvent, mercury::NoKey>::SubID _time_change_sub;
EventStream<orderman::FilledPositionEvent, mercury::NoKey>::SubID _position_update_sub;
EventStream<CreateOrderReplyEvent> _create_order_reply;
}; // namespace orderman
} // namespace orderman
@@ -35,6 +35,7 @@
#include "lib/orderman/order_router/OrderRouter.h"
#include "lib/utils/datetime.h"
#include "lib/utils/Puid.h"
#include "lib/orderman/actors/order_manager/PositionIndex.h"
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -87,6 +88,7 @@ class OrderServicesData {
OrderManager &owner;
trace::PerfLog &log;
securities::MapSecurityDB sec_db;
PositionIndex position;
OrderRouter order_router;
Limiter limiter;
OrderFactory order_factory;
@@ -101,6 +103,8 @@ class OrderServicesData {
time_subs;
std::unique_ptr<mercury::ContentSubscription<CreateOrderReplyEvent>>
order_creation_subs;
std::unique_ptr<mercury::ContentSubscription<FilledPositionEvent,mercury::NoKey>>
position_subs;
mercury::Stream<CreateOrderRequestEvent> create_order_req_pub;
mercury::Stream<CreateFIXBufferRequestEvent> create_fix_req_pub;
@@ -0,0 +1,31 @@
#include "PositionIndex.h"
#include "lib/orderman/events/FilledPositionEvent.h"
#include "lib/orderman/orders/algo/PositionUpdate.h"
namespace bts {
namespace orderman {
struct PositionIndex::PImpl {
PImpl() : position_idx(), current_positions() {}
std::map<std::pair<SecurityKey,AccountID>,size_t> position_idx;
std::vector<PositionUpdate> current_positions;
};
void PositionIndex::update(FilledPositionEvent const&ev) {
auto &idx = me->position_idx[{SecurityKey(ev.security->id()),ev.account_id}];
if (idx == 0) {
me->current_positions.push_back({});
idx = me->current_positions.size();
}
me->current_positions[idx-1] = {ev.security,ev.account_id,ev.long_quantity, ev.short_quantity};
}
std::vector<PositionUpdate> const& PositionIndex::current_positions() const {
return me->current_positions;
}
PositionIndex::PositionIndex() : me (new PImpl()) {}
PositionIndex::~PositionIndex() {}
}}
@@ -0,0 +1,23 @@
#pragma once
#include <memory>
#include <vector>
namespace bts {
namespace orderman {
class FilledPositionEvent;
struct PositionUpdate;
class PositionIndex {
public:
PositionIndex();
~PositionIndex();
void update(FilledPositionEvent const&);
std::vector<PositionUpdate> const &current_positions() const;
private:
struct PImpl;
std::unique_ptr<PImpl> me;
};
}}
@@ -56,11 +56,12 @@ struct AlgoWrapperOrder::PImpl {
OrderState const &state() const { return _state; }
Map<SecurityKey, OrderServices::BookSubID> book_subs;
Map<SecurityKey, OrderServices::SettlementSubID> settlement_subs;
Map<SecurityKey, OrderServices::TickerSubID> ticker_subs;
Map<SecurityKey, OrderServices::StatusSubID> status_subs;
Map<OrderKey, ChildRecord> children;
bool has_timer_sub = false;
bool has_position_sub = false;
bool is_state_dirty = false;
Map<Edge, Action> transition_actions;
@@ -382,6 +383,10 @@ api::Security const *AlgoWrapperOrder::lookup_security(SecurityKey k) const {
return me->security_db->resolve(k);
}
std::vector<api::Security const *> AlgoWrapperOrder::known_securities() const {
return me->security_db->get_securities();
}
Price AlgoWrapperOrder::price() const { return Price(me->state().price()); }
Quantity AlgoWrapperOrder::quantity() const {
@@ -396,6 +401,10 @@ bool AlgoWrapperOrder::is_shutting_down() const {
return me->status == Status::SHUTTING_DOWN;
}
std::vector<PositionUpdate> const&AlgoWrapperOrder::current_positions() const {
return me->services->current_positions();
}
void AlgoWrapperOrder::subscribe_book(SecurityKey k) {
if (!is_working())
return;
@@ -416,6 +425,26 @@ void AlgoWrapperOrder::unsubscribe_book(SecurityKey k) {
me->services->unsubscribe_from_book(it->second);
}
void AlgoWrapperOrder::subscribe_settlement(SecurityKey k) {
if (!is_working())
return;
bts_assert(me->settlement_subs.find(k) == me->settlement_subs.end());
me->settlement_subs[k] =
me->services->subscribe_to_settlements(k, [this, k](auto const &ev) {
ReportStateGuard _(*this);
me->algo->on_settlement(k, Price(ev.settlement_price.price()));
});
}
void AlgoWrapperOrder::unsubscribe_settlement(SecurityKey k) {
if (!is_working())
return;
bts_assert(me->settlement_subs.find(k) != me->settlement_subs.end());
if (auto it = me->settlement_subs.find(k); it != me->settlement_subs.end())
me->services->unsubscribe_from_settlements(it->second);
}
void AlgoWrapperOrder::subscribe_ticker(SecurityKey k) {
if (!is_working())
return;
@@ -497,6 +526,25 @@ void AlgoWrapperOrder::unsubscribe_timer() {
me->has_timer_sub = false;
}
void AlgoWrapperOrder::subscribe_position() {
if (!is_working())
return;
bts_assert(!me->has_position_sub);
me->has_position_sub = true;
me->services->subscribe_to_position_updates([this](auto const &ev) {
ReportStateGuard _(*this);
me->algo->on_position({ev.security,ev.account_id,ev.long_quantity, ev.short_quantity});
});
}
void AlgoWrapperOrder::unsubscribe_position() {
if (!is_working())
return;
bts_assert(me->has_position_sub);
me->services->unsubscribe_from_position_updates();
me->has_position_sub = false;
}
bool AlgoWrapperOrder::is_attaching() const {
return me->attach_target != null_order_key();
}
@@ -841,6 +889,8 @@ void AlgoWrapperOrder::check_invariants() {
bts_assert(is_attaching());
bts_assert(me->external_status() == ORDER_STATUS_WORKING);
bts_assert(me->book_subs.empty());
bts_assert(me->settlement_subs.empty());
bts_assert(me->ticker_subs.empty());
bts_assert(me->children.empty());
bts_assert(!me->has_timer_sub);
bts_assert(!me->pending_terminate.has_value());
@@ -860,6 +910,8 @@ void AlgoWrapperOrder::check_invariants() {
bts_assert(me->security);
bts_assert(me->external_status() == ORDER_STATUS_WORKING);
bts_assert(me->book_subs.empty());
bts_assert(me->settlement_subs.empty());
bts_assert(me->ticker_subs.empty());
bts_assert(!me->has_timer_sub);
bts_assert(!me->pending_update.has_value());
bts_assert(me->pending_terminate.has_value());
@@ -873,6 +925,8 @@ void AlgoWrapperOrder::check_invariants() {
me->external_status() == ORDER_STATUS_ERROR ||
me->external_status() == ORDER_STATUS_REJECTED_BY_SPARK);
bts_assert(me->book_subs.empty());
bts_assert(me->settlement_subs.empty());
bts_assert(me->ticker_subs.empty());
bts_assert(!me->has_timer_sub);
bts_assert(!me->pending_update.has_value());
bts_assert(!me->pending_terminate.has_value());
@@ -919,10 +973,18 @@ void AlgoWrapperOrder::shutdown(api::orderman::OrderStatus st,
}
void AlgoWrapperOrder::cleanup() {
for (auto const &s : me->book_subs) {
for (auto const &s : me->book_subs)
me->services->unsubscribe_from_book(s.second);
me->book_subs.clear();
}
me->book_subs.clear();
for (auto const &s : me->settlement_subs)
me->services->unsubscribe_from_settlements(s.second);
me->settlement_subs.clear();
for (auto const &s : me->ticker_subs)
me->services->unsubscribe_from_ticker(s.second);
me->ticker_subs.clear();
for (auto &c : me->children)
c.second.order.cancel();
if (me->has_timer_sub)
@@ -33,6 +33,8 @@ class AlgoWrapperOrder : public Order,
OrderKey order_key() const override;
OrderKey parent_order_key() const override;
api::Security const *lookup_security(SecurityKey k) const override;
std::vector<api::Security const *> known_securities() const override;
bool is_attaching() const override;
ChildTemplate &make_child_template(api::orderman::OrderDefinition const &,
size_t) override;
@@ -41,8 +43,12 @@ class AlgoWrapperOrder : public Order,
Quantity quantity() const override;
Quantity total_fills() const override;
bool is_shutting_down() const override;
std::vector<PositionUpdate> const&current_positions() const override;
void subscribe_book(SecurityKey) override;
void unsubscribe_book(SecurityKey) override;
void subscribe_settlement(SecurityKey) override;
void unsubscribe_settlement(SecurityKey) override;
void subscribe_child_change(OrderKey) override;
void unsubscribe_child_change(OrderKey) override;
void subscribe_timer() override;
@@ -51,6 +57,8 @@ class AlgoWrapperOrder : public Order,
void unsubscribe_ticker(SecurityKey) override;
void subscribe_status(SecurityKey) override;
void unsubscribe_status(SecurityKey) override;
void subscribe_position() override;
void unsubscribe_position() override;
OrderKey launch_child(ChildTemplate &, Price, Quantity,
bool subscribe = true) override;
@@ -3,6 +3,7 @@
namespace bts {
namespace orderman {
#include "lib/orderman/orders/algo/IAlgoOrder.h"
IAlgoOrder::~IAlgoOrder() {}
OrderOpResult IAlgoOrder::do_before_launch(IBeforeLaunchAlgoServices &) {
@@ -2,6 +2,8 @@
#include "lib/orderman/orders/Order.h"
#include "lib/orderman/events/TickerUpdateEvent.h"
#include "lib/orderman/orders/algo/PositionUpdate.h"
#include <memory>
namespace bts {
@@ -44,6 +46,12 @@ class IAlgoOrder {
// The trading status for the specified security has changed
virtual void on_status(SecurityKey, co2::TradingStatus) {};
// The settlement price for the specified security has changed
virtual void on_settlement(SecurityKey, Price) {};
// The current position for the security, account combination
virtual void on_position(PositionUpdate const&) {};
// A periodic timer has fired
virtual void on_timer() {}
@@ -1,10 +1,15 @@
#pragma once
#include "lib/orderman/orders/algo/IBeforeLaunchAlgoServices.h"
#include "lib/orderman/orders/algo/PositionUpdate.h"
namespace bts {
namespace orderman {
// Services to an algo *after* it has been launched. See
// IBeforeLaunchAlgoServices for services available before launch,
// that is outside of the latency sensitive path.
class IAlgoServices : public IBeforeLaunchAlgoServices {
public:
// --------------------------------------------------------------
@@ -26,6 +31,13 @@ class IAlgoServices : public IBeforeLaunchAlgoServices {
// cancel child orders will be ignored.
virtual bool is_shutting_down() const = 0;
// Returns the current position per security and account. The
// returned reference is only valid immediately after the
// call. Callers should make a copy if they wish to use it beyond
// that. Callers can keep track of changes to the position by
// subscribing to the position update events below.
virtual std::vector<PositionUpdate> const&current_positions() const = 0;
// --------------------------------------------------------------
// Event Subscriptions
// --------------------------------------------------------------
@@ -34,6 +46,10 @@ class IAlgoServices : public IBeforeLaunchAlgoServices {
virtual void subscribe_book(SecurityKey) = 0;
virtual void unsubscribe_book(SecurityKey) = 0;
// Subscribe to settlement prices for the specified security
virtual void subscribe_settlement(SecurityKey) = 0;
virtual void unsubscribe_settlement(SecurityKey) = 0;
// Subscribe to child order updates for the specified child
// order. This is rarely needed since launch_child() by default
// automatically subscribes to new orders.
@@ -52,6 +68,10 @@ class IAlgoServices : public IBeforeLaunchAlgoServices {
virtual void subscribe_status(SecurityKey) = 0;
virtual void unsubscribe_status(SecurityKey) = 0;
// Subscribe to position updates
virtual void subscribe_position() = 0;
virtual void unsubscribe_position() = 0;
// --------------------------------------------------------------
// Child management
// --------------------------------------------------------------
@@ -31,6 +31,9 @@ class IBeforeLaunchAlgoServices {
// The security identified by k or nullptr if no such security exists
virtual api::Security const *lookup_security(SecurityKey k) const = 0;
// Known active securities
virtual std::vector<api::Security const*> known_securities() const = 0;
// Whether this algo is being attached to an existing order
// If an algo does not support attaching it can return an error
// if is_attaching() == true from do_before_launch.
@@ -0,0 +1,15 @@
#include "PositionUpdate.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
namespace bts {
namespace orderman {
std::ostream& operator<<(std::ostream&out,const PositionUpdate &update) {
out << "Security: " << update.security->id()
<< " Account: " << update.account_id.value()
<< " Long Qty: " << update.long_quantity.value()
<< " Short Qty: " << update.short_quantity.value();
return out;
}
}}
@@ -0,0 +1,21 @@
#pragma once
#include "lib/domain_model/common.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
namespace bts {
namespace api { class Security;}
namespace orderman {
struct PositionUpdate {
api::Security const *security;
AccountID account_id;
Quantity long_quantity;
Quantity short_quantity;
};
std::ostream& operator<<(std::ostream&,const PositionUpdate &);
}}
@@ -19,7 +19,7 @@ class TopAlgo : public IAlgoOrder {
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_child_change(OrderKey, ChildState const &) override;
private:
struct PImpl;
std::unique_ptr<PImpl> me;
@@ -56,16 +56,19 @@ struct AlgoTestDriverBase::Impl : public RecursionCounter {
api::Security security;
OrderKey order_key, parent_key;
std::tuple<Quantity, Price> fill_state;
std::vector<PositionUpdate> positions;
AlgoState state;
std::vector<ChildOrder> orders;
std::vector<ChildOrder> detached_orders;
std::map<SecurityKey, Book> books;
std::map<SecurityKey, Price> settlements;
std::map<SecurityKey, bool> book_subs;
std::map<SecurityKey, bool> settlement_subs;
std::map<SecurityKey, bool> ticker_subs;
std::map<SecurityKey, bool> status_subs;
bool sub_timer = false;
bool sub_position = false;
// Callbacks
std::vector<std::function<void(OrderKey)>> on_child_launching;
@@ -158,6 +161,15 @@ void AlgoTestDriverBase::update_book(const Book& book) {
me->algo.on_book(key, book);
}
}
void AlgoTestDriverBase::update_settlement(SecurityKey key, Price price) {
me->settlements[key] = price;
auto iter = me->settlement_subs.find(key);
if (iter != me->settlement_subs.end()) {
me->algo.on_settlement(key, price);
}
}
void AlgoTestDriverBase::update_ticker(SecurityKey key, const TradeVector& data) {
auto iter = me->ticker_subs.find(key);
if (iter != me->ticker_subs.end() && iter->second) {
@@ -301,6 +313,15 @@ const api::Security* AlgoTestDriverBase::lookup_security(SecurityKey key) const
}
return nullptr;
}
std::vector<api::Security const *> AlgoTestDriverBase::known_securities() const {
std::vector<api::Security const *> result;
auto s = get_some_securities();
for (auto const &p : s) {
result.push_back(&p.second);
}
return result;
}
bool AlgoTestDriverBase::is_attaching() const { return has_flag(me->flags, ATDFlags::IS_ATTACHING); }
ChildTemplate& AlgoTestDriverBase::make_child_template(const api::orderman::OrderDefinition& def, size_t) {
REQUIRE(def.custom_def().custom_case() != CustomOrderDefinition::CUSTOM_NOT_SET);
@@ -321,6 +342,10 @@ bool AlgoTestDriverBase::is_shutting_down() const {
return false;
}
std::vector<PositionUpdate> const & AlgoTestDriverBase::current_positions() const {
return me->positions;
}
void AlgoTestDriverBase::subscribe_book(SecurityKey key){
me->book_subs[key] = true;
auto iter = me->books.find(key);
@@ -332,6 +357,18 @@ void AlgoTestDriverBase::unsubscribe_book(SecurityKey key) {
me->book_subs.erase(key);
}
void AlgoTestDriverBase::subscribe_settlement(SecurityKey key){
me->settlement_subs[key] = true;
auto iter = me->settlements.find(key);
if (iter != me->settlements.end()) {
me->algo.on_settlement(key, iter->second);
}
}
void AlgoTestDriverBase::unsubscribe_settlement(SecurityKey key) {
me->settlement_subs.erase(key);
}
void AlgoTestDriverBase::subscribe_ticker(SecurityKey key) {
me->ticker_subs[key] = true;
}
@@ -363,6 +400,8 @@ void AlgoTestDriverBase::unsubscribe_child_change(OrderKey k) {
void AlgoTestDriverBase::subscribe_timer() { me->sub_timer = true; }
void AlgoTestDriverBase::unsubscribe_timer() { me->sub_timer = false; }
void AlgoTestDriverBase::subscribe_position() { me->sub_position= true; }
void AlgoTestDriverBase::unsubscribe_position() { me->sub_position = false; }
OrderKey AlgoTestDriverBase::launch_child(ChildTemplate&, Price price, Quantity qty, bool sub) {
OrderKey key = me->next_order();
@@ -452,4 +491,4 @@ void AlgoTestDriverBase::complete() {
void AlgoTestDriverBase::set_ladder_visibility(bool) { /* whatever, ok */ }
void AlgoTestDriverBase::update_fill_state(Quantity q, Price p) { me->fill_state = { q, p }; }
}}}//namespace
}}}//namespace
@@ -60,7 +60,7 @@ class AlgoTestDriverBase : private IAlgoServices {
Quantity quantity() const override;
Quantity total_fills() const override;
bool is_shutting_down() const override;
std::vector<PositionUpdate> const & current_positions() const override;
//
// Change state
@@ -70,6 +70,7 @@ class AlgoTestDriverBase : private IAlgoServices {
void do_update(const api::orderman::Update&);
void update_book(const Book&);
void update_settlement(SecurityKey , Price ) ;
void update_ticker(SecurityKey, const TradeVector&);
void update_status(SecurityKey, co2::TradingStatus);
void advance_time(Duration, uint32_t steps=1);
@@ -104,6 +105,7 @@ private:
OrderKey order_key() const override;
OrderKey parent_order_key() const override;
const api::Security* lookup_security(SecurityKey) const override;
std::vector<api::Security const *> known_securities() const override;
bool is_attaching() const override;
ChildTemplate& make_child_template(const api::orderman::OrderDefinition& child_def, size_t reserve = 1) override;
TimeStamp get_time() const override;
@@ -112,6 +114,8 @@ private:
// IAlgoServices
void subscribe_book(SecurityKey)override;
void unsubscribe_book(SecurityKey) override;
void subscribe_settlement(SecurityKey)override;
void unsubscribe_settlement(SecurityKey) override;
void subscribe_child_change(OrderKey) override;
void unsubscribe_child_change(OrderKey) override;
void subscribe_timer() override;
@@ -120,6 +124,8 @@ private:
void unsubscribe_ticker(SecurityKey) override;
void subscribe_status(SecurityKey) override;
void unsubscribe_status(SecurityKey) override;
void subscribe_position() override;
void unsubscribe_position() override;
OrderKey launch_child(ChildTemplate &, Price, Quantity, bool) override;
OrderKey launch_child(api::orderman::OrderDefinition const &, Price,
@@ -151,4 +157,4 @@ struct AlgoTestDriver : public AlgoTestDriverBase
const Algo& algo() const { return _algo; }
};
}}}//namespace
}}}//namespace
@@ -13,6 +13,7 @@ class ISecurityDB
public:
virtual ~ISecurityDB() {};
virtual api::Security const *resolve(SecurityKey const&k) const = 0;
virtual std::vector<api::Security const*> get_securities() const = 0;
using SecurityMap = std::unordered_map<SecurityKey,api::Security const*> ;
};
@@ -13,6 +13,11 @@ struct MapSecurityDB : public securities::ISecurityDB
api::Security const *resolve(SecurityKey const&k) const override {
return _oracle.get_security(k);
}
std::vector<api::Security const*> get_securities() const override {
return _oracle.get_securities();
}
private:
md::SecurityOracle _oracle;
};
+1 -1
View File
@@ -8,7 +8,7 @@
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "/sparkbin/sparkd",
"program": "${workspaceFolder}/client_sdk_example",
"args": ["run"],
"stopAtEntry": false,
"cwd": "/sparkbin",
@@ -2,6 +2,7 @@
#include "spark/lib/domain_model/Security.h"
#include "lib/api/services/AdminService.client.pb.h"
#include "lib/api/services/PositionService.client.pb.h"
#include "lib/api/services/MarketDataService.client.pb.h"
#include <string>
#include <vector>
@@ -30,6 +31,17 @@ int main(int argc, char** argv) {
}
});
auto md_service = ctx.locate<bts::api::MarketDataService>();
auto sub2 =
md_service
.subscribe_securities()
.subscribe([](const auto& update) {
for (const auto& s : update.added()) {
std::cout << "Added security: " << s->id() << ": " << s->symbol() << std::endl;
}
});
std::this_thread::sleep_for(std::chrono::minutes(5));
return 0;
}
+2
View File
@@ -23,6 +23,8 @@ Ports {
value {
config_port: 9000
qbus_port: 9001
glbx_sim_fix_port: 9002
ice_sim_fix_port: 9003
}
}
+1 -1
View File
@@ -1 +1 @@
set(sources ../../externals/linux/include/google/protobuf/descriptor.proto;../../externals/linux/include/google/protobuf/timestamp.proto;../lib/api/apicore.proto;../lib/api/apigateway.proto;../lib/api/apioptions.proto;../lib/api/services/AdminService.proto;../lib/api/services/AlgoDefinitionService.proto;../lib/api/services/AlgoService.proto;../lib/api/services/ApiUnitTestService.proto;../lib/api/services/AuthService.proto;../lib/api/services/BasePositionService.proto;../lib/api/services/ErrorReportService.proto;../lib/api/services/HelloService.proto;../lib/api/services/MBOService.proto;../lib/api/services/MarginService.proto;../lib/api/services/MarketDataAuxiliaryService.proto;../lib/api/services/MarketDataRelay.proto;../lib/api/services/MarketDataService.proto;../lib/api/services/MoneyFileService.proto;../lib/api/services/OrderService.proto;../lib/api/services/OutOfProcessURLListener.proto;../lib/api/services/PersistenceService.proto;../lib/api/services/PositionService.proto;../lib/api/services/ShellDiagnosticService.proto;../lib/api/services/ShellLatencyService.proto;../lib/api/services/SyntheticSecurityService.proto;../lib/domain_model/Security.proto;../lib/md/protos/co2.proto;../lib/sparkproto/SparkProtos.proto;api/build.sh;api/node_modules/exit/test/fixtures/create-files.sh;api/node_modules/protobufjs/cli/node_modules/acorn-jsx/node_modules/acorn/bin/update_authors.sh;api/node_modules/watch/release.sh;api/quiet_build.sh;api/src/DynamicService.ts;api/src/ServiceContext.ts;api/src/bts_assert.ts;ui/src/Access.fs;ui/src/Accounts/ClipLimits.fs;ui/src/Accounts/ProductLimits.fs;ui/src/Accounts/SecurityLimits.fs;ui/src/Accounts/State.fs;ui/src/Accounts/Types.fs;ui/src/Accounts/View.fs;ui/src/App.fs;ui/src/Cmd.fs;ui/src/Controls/Button.fs;ui/src/Controls/DataGrid.fs;ui/src/Controls/Fields.fs;ui/src/Controls/Modal.fs;ui/src/Controls/ReactGrid.fs;ui/src/Controls/Table.fs;ui/src/Controls/nbsp.fs;ui/src/Global.fs;ui/src/Imports.fs;ui/src/Lens.fs;ui/src/Login/Login.fs;ui/src/Nav.fs;ui/src/State.fs;ui/src/Types.fs;ui/src/Users/State.fs;ui/src/Users/Types.fs;ui/src/Users/View.fs;ui/src/Util/EnumUtils.fs;ui/src/Util/JSMap.fs;ui/src/Util/StreamUtils.fs;ui/src/obj/Debug/netstandard2.0/ui.AssemblyInfo.fs)
set(sources ../../externals/linux/include/google/protobuf/descriptor.proto;../../externals/linux/include/google/protobuf/timestamp.proto;../lib/api/apicore.proto;../lib/api/apigateway.proto;../lib/api/apioptions.proto;../lib/api/services/AdminService.proto;../lib/api/services/AlgoDefinitionService.proto;../lib/api/services/AlgoService.proto;../lib/api/services/ApiUnitTestService.proto;../lib/api/services/AuthService.proto;../lib/api/services/BasePositionService.proto;../lib/api/services/ErrorReportService.proto;../lib/api/services/HelloService.proto;../lib/api/services/MBOService.proto;../lib/api/services/MarginService.proto;../lib/api/services/MarketDataAuxiliaryService.proto;../lib/api/services/MarketDataRelay.proto;../lib/api/services/MarketDataService.proto;../lib/api/services/MoneyFileService.proto;../lib/api/services/OrderService.proto;../lib/api/services/OutOfProcessURLListener.proto;../lib/api/services/PersistenceService.proto;../lib/api/services/PositionService.proto;../lib/api/services/ShellDiagnosticService.proto;../lib/api/services/ShellLatencyService.proto;../lib/api/services/SyntheticSecurityService.proto;../lib/domain_model/Security.proto;../lib/md/protos/co2.proto;../lib/sparkproto/SparkProtos.proto;api/build.sh;api/node_modules/exit/test/fixtures/create-files.sh;api/node_modules/protobufjs/cli/node_modules/acorn-jsx/node_modules/acorn/bin/update_authors.sh;api/node_modules/psl/scripts/ci-build.sh;api/node_modules/watch/release.sh;api/quiet_build.sh;api/src/DynamicService.ts;api/src/ServiceContext.ts;api/src/bts_assert.ts;ui/src/Access.fs;ui/src/Accounts/ClipLimits.fs;ui/src/Accounts/ProductLimits.fs;ui/src/Accounts/SecurityLimits.fs;ui/src/Accounts/State.fs;ui/src/Accounts/Types.fs;ui/src/Accounts/View.fs;ui/src/App.fs;ui/src/Cmd.fs;ui/src/Controls/Button.fs;ui/src/Controls/DataGrid.fs;ui/src/Controls/Fields.fs;ui/src/Controls/Modal.fs;ui/src/Controls/ReactGrid.fs;ui/src/Controls/Table.fs;ui/src/Controls/nbsp.fs;ui/src/Global.fs;ui/src/Imports.fs;ui/src/Lens.fs;ui/src/Login/Login.fs;ui/src/Nav.fs;ui/src/State.fs;ui/src/Types.fs;ui/src/Users/State.fs;ui/src/Users/Types.fs;ui/src/Users/View.fs;ui/src/Util/EnumUtils.fs;ui/src/Util/JSMap.fs;ui/src/Util/StreamUtils.fs;ui/src/obj/Debug/netstandard2.0/ui.AssemblyInfo.fs)