Compare commits

...
24 changed files with 300 additions and 137 deletions

No files matched your search

@@ -138,6 +138,7 @@ namespace mbts.spark {
public event Action invalidated;
#region Bound Properties
public string symbol => security.display_symbol();
public bool inside_bids => _inside_bids.value;
public bool inside_asks => _inside_asks.value;
public int position => _position.value;
@@ -423,4 +424,4 @@ namespace mbts.spark {
}
}
}
}
}
@@ -41,6 +41,7 @@ namespace mbts.spark {
const double working_orders_col_width = 105;
IMultiValueConverter price_converter = new ui.spark.converters.TickDisplayConverter();
add_col(Columns.Security, "Security", r => r.security.ToString(), nameof(Row.security), DefaultColumnWidths.Security, security_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.Symbol, DefaultColumnHeaders.Symbol, r => r.symbol, nameof(Row.symbol), DefaultColumnWidths.Symbol, symbol_filter, DataGridFilterKnownTemplates.DiscreteValues);
add_col(Columns.Volume, "Volume", r => r.volume_text, nameof(Row.volume), DefaultColumnWidths.Volume, volume_filter, DataGridFilterKnownTemplates.ULongMinMax);
add_col(Columns.LastSpd, "Spd", r => r.last_price_from_spread ? "S" : "", nameof(Row.last_price_from_spread), DefaultColumnWidths.Side, last_price_from_spread_filter, DataGridFilterKnownTemplates.Regex);
add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
@@ -134,6 +135,7 @@ namespace mbts.spark {
}
if (s.filters is FilterStates filters) {
security_filter.load_state(filters.security);
symbol_filter.load_state(filters.symbols);
bid_price_filter.load_state(filters.bid_price);
bid_merged_size_filter.load_state(filters.bid_merged_size);
bid_direct_size_filter.load_state(filters.bid_direct_size);
@@ -184,6 +186,7 @@ namespace mbts.spark {
public static IComparer<Row> get_column_comparer(Columns column) {
switch (column) {
case Columns.Security: return wrap(r => r.security);
case Columns.Symbol: return wrap(r => r.symbol);
case Columns.SecurityKey: return wrap(r => r.security.Id);
case Columns.Volume: return wrap(r => r.volume);
case Columns.Last: return wrap(r => r.last_price);
@@ -217,6 +220,7 @@ namespace mbts.spark {
#region Filters
public IDataGridColumnPersistentFilter<Row, string> security_filter { get; }
public IDataGridColumnPersistentFilter<Row, string[]> symbol_filter { get; } = DiscreteValuesFilterVM.Common.symbol<Row>(r => r.symbol, nameof(Row.symbol));
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> bid_price_filter { get; } = new DoubleMinMax<Row>(r => r.bid_price, nameof(Row.bid_price));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_merged_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_merged_size, nameof(Row.bid_merged_size));
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> bid_direct_size_filter { get; } = new UIntMinMax<Row>(r => r.bid_direct_size, nameof(Row.bid_direct_size));
@@ -252,6 +256,7 @@ namespace mbts.spark {
public FilterStates get_filter_states() {
return new FilterStates {
security = security_filter.get_state(),
symbols = symbol_filter.get_state(),
bid_price = bid_price_filter.get_state(),
bid_merged_size = bid_merged_size_filter.get_state(),
bid_direct_size = bid_direct_size_filter.get_state(),
@@ -10,6 +10,7 @@ namespace mbts.ui.spark {
partial class MarketViewView : UserControl {
public enum Columns {
Security,
Symbol,
Volume,
Last,
LastSpd,
@@ -49,4 +50,4 @@ namespace mbts.ui.spark {
}
class MarketViewRowView : DIYGrid.HighPerformanceRow<MarketViewVM.Row> { }
}
}
@@ -1,7 +1,6 @@
using bts.api;
using bts.proto;
using bts.settings;
using bts.view;
using mbts.persist;
using mbts.spark;
using mbts.spark.filters;
@@ -101,6 +100,7 @@ namespace bts.shell {
static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings symbol => new GridColumnSettings(col_id(Columns.Symbol), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol, is_visible: false);
public static GridColumnSettings volume => new GridColumnSettings(col_id(Columns.Volume), DefaultColumnHeaders.Volume, width: DefaultColumnWidths.Volume);
public static GridColumnSettings last => new GridColumnSettings(col_id(Columns.Last), DefaultColumnHeaders.Last, width: DefaultColumnWidths.Price);
public static GridColumnSettings last_from_spread => new GridColumnSettings(col_id(Columns.LastSpd), DefaultColumnHeaders.S, width: DefaultColumnWidths.Price, is_visible: false);
@@ -140,6 +140,7 @@ namespace bts.shell {
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return symbol;
yield return volume;
yield return last;
yield return last_from_spread;
@@ -232,6 +233,8 @@ namespace bts.shell {
public string last_price_from_spread;
[ProtoMember(28)]
public string mic_code;
[ProtoMember(29)]
public string[] symbols;
}
// migration rules
@@ -1,11 +1,13 @@
using mbts.spark.filters;
using bts.utils;
using Google.Protobuf;
using mbts.spark.filters;
using mbts.ui;
using ProtoBuf;
using System;
namespace bts.shell.time_and_sales {
[ProtoContract]
class FilterStates {
class FilterStates : IDeepCloneable<FilterStates> {
[ProtoMember(1)]
public TimeFilterTuple time;
[ProtoMember(2)]
@@ -20,12 +22,24 @@ namespace bts.shell.time_and_sales {
public string security_key;
[ProtoMember(7)]
public co2.AggressorSide[] aggressor_sides;
[ProtoMember(8)]
public string[] symbols;
public FilterStates Clone() =>
new FilterStates {
time = time.Clone(),
security = security?.Copy(),
price = price?.Clone(),
size = size?.Clone(),
symbols = ArrayHelper.safe_convert(symbols, sym => sym?.Copy())
};
}
static class TimeAndSalesFilterStatesExtensions {
public static Func<TimeAndSalesRowVM, bool> build_predicate(this FilterStates fs) {
var pb = new ColumnPredicateBuilder<TimeAndSalesRowVM>();
pb.add_regex(fs.security, r => r.security.ToString());
pb.add_discrete_values(fs.symbols, r => r.symbol);
pb.add_time_filter(fs.time, r => r.time);
pb.add_price(fs.price, r => r.price, r => r.security);
pb.add_numeric(fs.size, r => r.size);
@@ -1,74 +1,90 @@
using mbts.spark;
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System.Collections.Generic;
using System.Linq;
using static mbts.ui.controls.DIYGrid;
using Columns = bts.shell.time_and_sales.TimeAndSalesView.Columns;
namespace bts.shell.time_and_sales {
[ProtoContract]
class TimeAndSalesSettings {
[ProtoMember(1)]
public readonly SecurityCriteria security_criteria;
[ProtoMember(2)]
List<SortingCriteria> _sorting_criterion;
[ProtoMember(3)]
public FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
[ProtoMember(5)]
public SparkMenuDisplaySettings menu_display;
[ProtoMember(6)]
public bool use_defaults;
public TimeAndSalesSettings() { }
public TimeAndSalesSettings(SecurityCriteria security_criteria) :
this(security_criteria, new FilterStates(), null, null) { }
public TimeAndSalesSettings(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List<SortingCriteria> sorting_criterion = null) {
this.security_criteria = security_criteria;
this.filters = filters;
this.grid = grid;
_sorting_criterion = sorting_criterion;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public List<SortingCriteria> sorting_criterion => _sorting_criterion ?? (_sorting_criterion = DefaultColumns.default_sorting_criterion.ToList());
public static TimeAndSalesSettings Default(SecurityCriteria security_criteria) =>
new TimeAndSalesSettings(security_criteria, new FilterStates(), null, DefaultColumns.default_sorting_criterion.ToList());
public TimeAndSalesSettings with(SecurityCriteria security_criteria) =>
new TimeAndSalesSettings(security_criteria, filters, grid);
public static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.col_id(), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings time => new GridColumnSettings(Columns.Time.col_id(), DefaultColumnHeaders.Time, width: DefaultColumnWidths.TimeOfDay);
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.col_id(), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings size => new GridColumnSettings(Columns.Size.col_id(), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings aggressor => new GridColumnSettings(Columns.Aggressor.col_id(), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor);
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.col_id(), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(time.id, false);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return time;
yield return price;
yield return size;
yield return aggressor;
yield return security_key;
}
}
}
}
}
using bts.utils;
using mbts.spark;
using mbts.ui;
using mbts.ui.settings;
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using static mbts.ui.controls.DIYGrid;
using Columns = bts.shell.time_and_sales.TimeAndSalesView.Columns;
namespace bts.shell.time_and_sales {
[ProtoContract]
class TimeAndSalesSettings {
[ProtoMember(1)]
public readonly SecurityCriteria security_criteria;
[ProtoMember(2)]
List<SortingCriteria> _sorting_criterion;
[ProtoMember(3)]
public FilterStates filters;
[ProtoMember(4)]
public GridSettings grid;
[ProtoMember(5)]
public SparkMenuDisplaySettings menu_display;
[ProtoMember(6)]
public bool use_defaults;
[ProtoMember(7)]
string[] _symbols;
public TimeAndSalesSettings() { }
public TimeAndSalesSettings(SecurityCriteria security_criteria) :
this(security_criteria, new FilterStates(), null, null) { }
public TimeAndSalesSettings(SecurityCriteria security_criteria, FilterStates filters, GridSettings grid = default, List<SortingCriteria> sorting_criterion = null) {
this.security_criteria = security_criteria;
this.filters = filters;
this.grid = grid;
_sorting_criterion = sorting_criterion;
}
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
public List<SortingCriteria> sorting_criterion => _sorting_criterion ?? (_sorting_criterion = DefaultColumns.default_sorting_criterion.ToList());
public IReadOnlyList<string> symbols => _symbols ?? Array.Empty<string>();
public static TimeAndSalesSettings Default(SecurityCriteria security_criteria) =>
new TimeAndSalesSettings(security_criteria, new FilterStates(), null, DefaultColumns.default_sorting_criterion.ToList());
// todo: should prob clone new instances of each member;
// prob won't make a difference in practice, but it's weird that 2 instances of TimeAndSalesSettings
// share the same instances of internal members.
public TimeAndSalesSettings with(SecurityCriteria security_criteria) =>
new TimeAndSalesSettings(security_criteria, filters, grid.clone()) {
filters = filters?.Clone(),
grid = grid?.clone(),
menu_display = menu_display.Clone(),
use_defaults = use_defaults,
_symbols = ArrayHelper.safe_convert(_symbols, sym => sym?.Copy())
};
public static class DefaultColumns {
public static GridColumnSettings security => new GridColumnSettings(Columns.Security.col_id(), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
public static GridColumnSettings symbol => new GridColumnSettings(Columns.Symbol.col_id(), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol, is_visible: false);
public static GridColumnSettings time => new GridColumnSettings(Columns.Time.col_id(), DefaultColumnHeaders.Time, width: DefaultColumnWidths.TimeOfDay);
public static GridColumnSettings price => new GridColumnSettings(Columns.Price.col_id(), DefaultColumnHeaders.Price, width: DefaultColumnWidths.Price, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings size => new GridColumnSettings(Columns.Size.col_id(), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
public static GridColumnSettings aggressor => new GridColumnSettings(Columns.Aggressor.col_id(), DefaultColumnHeaders.Aggressor, width: DefaultColumnWidths.Aggressor);
public static GridColumnSettings security_key => new GridColumnSettings(Columns.SecurityKey.col_id(), DefaultColumnHeaders.SecurityKey, is_visible: false, width: DefaultColumnWidths.SecurityKey);
public static IEnumerable<SortingCriteria> default_sorting_criterion {
get {
yield return new SortingCriteria(time.id, false);
}
}
public static IEnumerable<GridColumnSettings> all {
get {
yield return security;
yield return symbol;
yield return time;
yield return price;
yield return size;
yield return aggressor;
yield return security_key;
}
}
}
}
}
@@ -39,6 +39,7 @@ namespace bts.shell.time_and_sales {
public uint trade_key { get; }
public uint security_key => security.Id;
public SecurityImmutable security { get; }
public string symbol => security.display_symbol();
public DateTime time => ((ulong)_trade.Time).lldatetime().ToLocalTime();
public double price => _trade.price();
public uint size => _trade.Size;
@@ -80,6 +80,13 @@ namespace bts.shell.time_and_sales {
DefaultColumnWidths.Security,
security_filter,
DataGridFilterKnownTemplates.Regex);
add_column(
TimeAndSalesView.Columns.Symbol,
r => r.symbol,
DefaultColumnHeaders.Symbol,
DefaultColumnWidths.Symbol,
symbol_filter,
DataGridFilterKnownTemplates.DiscreteValues);
add_column(
TimeAndSalesView.Columns.Time,
r => r.time.ToString("h:mm:ss tt"),
@@ -131,7 +138,10 @@ namespace bts.shell.time_and_sales {
return Disposable.Create(() => f.filter_changed -= _refresh_filter.execute);
}));
_sort_manager.active_sorting_changed += _refresh_sort.execute;
resources.Add(Disposable.Create(() => _sort_manager.active_sorting_changed -= _refresh_sort.execute));
resources.Add(Disposable.Create(() => {
_sort_manager.active_sorting_changed -= _refresh_sort.execute;
_filters.OfType<IDisposable>().DisposeAll();
}));
_dispose = Disposable.Create(resources.ToArray());
// local methods
void add_column(
@@ -182,6 +192,7 @@ namespace bts.shell.time_and_sales {
// filters
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, TimeFilterTuple> time_filter { get; } = new TimeFilterVM<TimeAndSalesRowVM>(r => r.time);
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, string> security_filter { get; } = new RegexFilterVM<TimeAndSalesRowVM>(r => r.security.ToString(), nameof(TimeAndSalesRowVM.security));
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, string[]> symbol_filter { get; } = DiscreteValuesFilterVM.Common.symbol<TimeAndSalesRowVM>(r => r.symbol, nameof(TimeAndSalesRowVM.symbol));
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, DoubleMinMaxState> price_filter { get; } = new DoubleMinMax<TimeAndSalesRowVM>(r => r.price);
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, UIntMinMaxState> size_filter { get; } = new UIntMinMax<TimeAndSalesRowVM>(r => r.size, nameof(TimeAndSalesRowVM.size));
public IDataGridColumnPersistentFilter<TimeAndSalesRowVM, AggressorSide[]> aggressor_filter { get; } = DiscreteValuesFilterVM.Create<TimeAndSalesRowVM, AggressorSide>(EnumHelper.get_values<AggressorSide>().ToArray(), r => r.aggressor_side);
@@ -259,6 +270,7 @@ namespace bts.shell.time_and_sales {
// load filters
time_filter.load_state(filters.time);
security_filter.load_state(filters.security);
symbol_filter.load_state(filters.symbols);
price_filter.load_state(filters.price);
size_filter.load_state(filters.size);
aggressor_filter.load_state(filters.aggressor_sides);
@@ -349,6 +361,7 @@ namespace bts.shell.time_and_sales {
case TimeAndSalesView.Columns.Aggressor: return wrap(r => r.aggressor_side);
case TimeAndSalesView.Columns.Time: return wrap(r => r.time);
case TimeAndSalesView.Columns.Security: return wrap(r => r.security);
case TimeAndSalesView.Columns.Symbol: return wrap(r => r.symbol);
case TimeAndSalesView.Columns.Price: return wrap(r => r.price);
case TimeAndSalesView.Columns.Size: return wrap(r => r.size);
case TimeAndSalesView.Columns.SecurityKey: return wrap(r => r.security_key);
@@ -373,6 +386,7 @@ namespace bts.shell.time_and_sales {
new FilterStates {
time = time_filter.get_state(),
security = security_filter.get_state(),
symbols = symbol_filter.get_state(),
price = price_filter.get_state(),
size = size_filter.get_state(),
security_key = security_key_filter.get_state(),
@@ -6,7 +6,7 @@ namespace bts.shell.time_and_sales {
/// Interaction logic for TimeAndSalesView.xaml
/// </summary>
partial class TimeAndSalesView : UserControl {
public enum Columns { Time, Security, Price, Size, SecurityKey, Aggressor, RowKey }
public enum Columns { Time, Security, Symbol, Price, Size, SecurityKey, Aggressor, RowKey }
public TimeAndSalesView() =>
InitializeComponent();
@@ -1,18 +1,20 @@
using bts.api;
using bts.spark;
using bts.settings;
using bts.shell.time_and_sales;
using bts.spark;
using mbts;
using mbts.co2;
using mbts.config;
using mbts.persist;
using mbts.spark;
using mbts.ui;
using mbts.ui.settings;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Columns = bts.shell.time_and_sales.TimeAndSalesView.Columns;
using mbts.config;
namespace bts.shell {
class TimeAndSalesWindowFactory : ShellWindowFactory<TimeAndSalesSettings> {
@@ -40,7 +42,7 @@ namespace bts.shell {
public override Task<IShellWindow> load(TimeAndSalesSettings state) =>
Task.FromResult<IShellWindow>(new TimeAndSalesWindow(this, _bus, _consumer, state, SparkCommands.PickTimeAndSales.icon,
new WindowSettingsManager<GridSettings>(_settings_hub, state.grid, s => s.time_and_sales_grid, (s, g) => s.with(time_and_sales:g)),
_settings_hub.create_time_and_sales_settings_manager(state.grid),
_time_and_sales_conf.can_pause_updates,
_security_auxiliary_model.get_display_quantity_divisor));
@@ -55,5 +57,12 @@ namespace bts.shell {
public TimeAndSalesConfig(bts.config.TimeAndSalesOptions options) =>
can_pause_updates = options.can_pause_updates;
}
// migration rules
[Export(typeof(ITimeAndSalesGridSettingsMigrationRule))]
class AddMissingColumnsMigrationRule : ITimeAndSalesGridSettingsMigrationRule {
public bool apply(GridSettings settings, out GridSettings new_settings) =>
settings.add_missing_columns(TimeAndSalesSettings.DefaultGridSettings.columns, out new_settings);
}
}
}
@@ -118,7 +118,15 @@ namespace bts.shell.pnl {
).FilterWithRaw(filter_src, PNLRowData.key_fn, null)
.OnDispatcher()
.Subscribe(_update),
_symbols_source(pnl_source).OnDispatcher().Subscribe(symbol_filter_items_observer),
// Note: the symbol filter items don't really need to be an IObservable
// (the symbol items used to be extracted from the IObservable pnl_source).
// It is only an IObservable now due to legacy; the filter was designed to observe incremental updates.
// This is can be easily changed if need be.
Observable.Return(
TableUpdate<string, string>.mk_empty(sym => sym).with_added(
SecurityHelper.get_all_display_symbols()))
.OnDispatcher()
.Subscribe(symbol_filter_items_observer),
Disposable.Create(() => {
foreach (var f in _all_filters)
f.filter_changed -= _refresh_filter.execute;
@@ -18,6 +18,7 @@ namespace bts.settings {
interface IPAndLGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface IPNLSettingsMigrationRule : ISettingsMigrationRule<PNLSettings> { }
interface ISyntheticManagementMigrationRule : ISettingsMigrationRule<GridSettings> { }
interface ITimeAndSalesGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
static class SettingsMigrationHelper {
public static bool apply<T>(T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, out T new_settings) {
@@ -26,7 +26,8 @@ namespace mbts.persist {
_fills_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.fills_grid, (us, gs) => us.with(provided_fills_gridsettings: gs)),
_mview_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.market_view_grid, (us, gs) => us.with(provided_market_view_gridsettings: gs)),
_pandl_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.pnl_grid, (us, gs) => us.with(provided_pnl_gridsettings: gs)),
_synth_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.synthetic_management_grid, (us, gs) => us.with(provided_synthetic_management_gridsettings: gs));
_synth_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.synthetic_management_grid, (us, gs) => us.with(provided_synthetic_management_gridsettings: gs)),
_time_and_sales_propi = ImmutableProperty.create<UserSettings, GridSettings>(us => us.time_and_sales_grid, (us, gs) => us.with(time_and_sales: gs));
static readonly IImmutableProperty<UserSettings, PNLSettings>
_pnl_propi = ImmutableProperty.create<UserSettings, PNLSettings>(us => us.pnl, (us, pnl) => us.with(pnl: pnl));
@@ -38,6 +39,7 @@ namespace mbts.persist {
readonly IEnumerable<IPAndLGridSettingsMigrationRule> _pandl_migration_rules;
readonly IEnumerable<IPNLSettingsMigrationRule> _pnl_migration_rules;
readonly IEnumerable<ISyntheticManagementMigrationRule> _synth_migration_rules;
readonly IEnumerable<ITimeAndSalesGridSettingsMigrationRule> _time_and_sales_migration_rules;
readonly IUserPersist _persist;
UserSettings _default_settings;
@@ -49,7 +51,8 @@ namespace mbts.persist {
[ImportMany]IEnumerable<IMarketViewGridSettingsMigrationRule> mview_migration_rules,
[ImportMany]IEnumerable<IPAndLGridSettingsMigrationRule> pandl_migration_rules,
[ImportMany]IEnumerable<IPNLSettingsMigrationRule> pnl_migration_rules,
[ImportMany]IEnumerable<ISyntheticManagementMigrationRule> synth_migration_rules) {
[ImportMany]IEnumerable<ISyntheticManagementMigrationRule> synth_migration_rules,
[ImportMany]IEnumerable<ITimeAndSalesGridSettingsMigrationRule> time_and_sales_migration_rules) {
//
_persist = persist;
_fills_migration_rules = fills_migration_rules ?? Enumerable.Empty<IFillsGridSettingsMigrationRule>();
@@ -57,6 +60,7 @@ namespace mbts.persist {
_pandl_migration_rules = pandl_migration_rules ?? Enumerable.Empty<IPAndLGridSettingsMigrationRule>();
_pnl_migration_rules = pnl_migration_rules ?? Enumerable.Empty<IPNLSettingsMigrationRule>();
_synth_migration_rules = synth_migration_rules ?? Enumerable.Empty<ISyntheticManagementMigrationRule>();
_time_and_sales_migration_rules = time_and_sales_migration_rules ?? Enumerable.Empty<ITimeAndSalesGridSettingsMigrationRule>();
init();
async void init() {
UserSettings s;
@@ -148,6 +152,12 @@ namespace mbts.persist {
_synth_migration_rules,
_synth_propi);
public IWindowSettingsManager<GridSettings> create_time_and_sales_settings_manager(GridSettings instance_settings) =>
_create_settings_manager(
instance_settings,
_time_and_sales_migration_rules,
_time_and_sales_propi);
public async Task save_default(UserSettings value) {
// notify observers before persisting so the changes are reflected immediately in the ui
lock (_default_settings_lock) {
@@ -402,8 +402,10 @@ namespace mbts.spark.filters {
public static IDataGridColumnPersistentFilter<TRow, Venue[]> exchange<TRow>(Func<TRow, Venue> get_row_exchange, string prop_name) =>
Create(EnumHelper.get_values_except(Venue.Unset).ToList(), get_row_exchange, prop_name);
// Symbol
public static IDataGridColumnPersistentFilter<TRow, string[]> symbol<TRow>(Func<TRow, string> get_row_symbol, string prop_name) =>
Create(co2.UserSymbol.get_all().Select(u => u.symbol).Distinct().ToList(), get_row_symbol, prop_name);
public static IDataGridColumnPersistentFilter<TRow, string[]> symbol<TRow>(Func<TRow, string> get_row_symbol, string prop_name, bool use_display_symbols = true) {
var symbols = use_display_symbols ? SecurityHelper.get_all_display_symbols() : co2.UserSymbol.get_all().Select(u => u.symbol);
return Create(symbols.Distinct().Ordered().ToList(), get_row_symbol, prop_name);
}
// Security Type
public static IDataGridColumnPersistentFilter<TRow, Security.ContractOneofCase[]> security_type<TRow>(Func<TRow, Security.ContractOneofCase> get_row_security_type, string prop_name) =>
Create(EnumHelper.get_values_except(
@@ -1,4 +1,5 @@
using bts.utils;
using Google.Protobuf;
using ProtoBuf;
using System;
@@ -9,7 +10,7 @@ namespace mbts.spark.filters {
}
[ProtoContract]
class DoubleStatus : INullableStructProto<double>, IEquatable<DoubleStatus> {
class DoubleStatus : INullableStructProto<double>, IDeepCloneable<DoubleStatus>, IEquatable<DoubleStatus> {
[ProtoMember(1)]
bool _active;
[ProtoMember(2)]
@@ -21,6 +22,12 @@ namespace mbts.spark.filters {
set { if (_active = value.HasValue && !double.IsNaN(value.Value)) _value = value.Value; }
}
public DoubleStatus Clone() =>
new DoubleStatus {
_active = _active,
_value = _value
};
public bool Equals(DoubleStatus other) =>
other != null &&
_active == other._active &&
@@ -32,20 +39,25 @@ namespace mbts.spark.filters {
}
[ProtoContract]
class DoubleMinMaxState : IMinMaxState<double>, IEquatable<DoubleMinMaxState> {
class DoubleMinMaxState : IMinMaxState<double>, IDeepCloneable<DoubleMinMaxState>, IEquatable<DoubleMinMaxState> {
[ProtoMember(1)]
readonly DoubleStatus _minimum;
[ProtoMember(2)]
readonly DoubleStatus _maximum;
public DoubleMinMaxState() {
_minimum = new DoubleStatus();
_maximum = new DoubleStatus();
public DoubleMinMaxState() : this(new DoubleStatus(), new DoubleStatus()) { }
DoubleMinMaxState(DoubleStatus minimum, DoubleStatus maximum) {
_minimum = minimum;
_maximum = maximum;
}
public INullableStructProto<double> minimum => _minimum;
public INullableStructProto<double> maximum => _maximum;
public DoubleMinMaxState Clone() =>
new DoubleMinMaxState(_minimum?.Clone(), _maximum?.Clone());
public bool Equals(DoubleMinMaxState other) =>
other != null &&
_minimum.safe_equals(other._minimum) &&
@@ -1,4 +1,5 @@
using bts.utils;
using Google.Protobuf;
using ProtoBuf;
using System;
@@ -9,7 +10,7 @@ namespace mbts.spark.filters {
}
[ProtoContract]
class UIntStatus : INullableStructProto<uint>, IEquatable<UIntStatus> {
class UIntStatus : INullableStructProto<uint>, IDeepCloneable<UIntStatus>, IEquatable<UIntStatus> {
[ProtoMember(1)]
bool _active;
[ProtoMember(2)]
@@ -21,6 +22,12 @@ namespace mbts.spark.filters {
set { if (_active = value.HasValue) _value = value.Value; }
}
public UIntStatus Clone() =>
new UIntStatus {
_active = _active,
_value = _value
};
public bool Equals(UIntStatus other) =>
other != null &&
_active == other._active &&
@@ -32,20 +39,25 @@ namespace mbts.spark.filters {
}
[ProtoContract]
class UIntMinMaxState : IMinMaxState<uint>, IEquatable<UIntMinMaxState> {
class UIntMinMaxState : IMinMaxState<uint>, IDeepCloneable<UIntMinMaxState>, IEquatable<UIntMinMaxState> {
[ProtoMember(1)]
readonly UIntStatus _minimum;
[ProtoMember(2)]
readonly UIntStatus _maximum;
public UIntMinMaxState() {
_minimum = new UIntStatus();
_maximum = new UIntStatus();
public UIntMinMaxState() : this(new UIntStatus(), new UIntStatus()) { }
UIntMinMaxState(UIntStatus minimum, UIntStatus maximum) {
_minimum = minimum;
_maximum = maximum;
}
public INullableStructProto<uint> minimum => _minimum;
public INullableStructProto<uint> maximum => _maximum;
public UIntMinMaxState Clone() =>
new UIntMinMaxState(_minimum?.Clone(), _maximum?.Clone());
public bool Equals(UIntMinMaxState other) =>
other != null &&
_minimum.safe_equals(other._minimum) &&
@@ -1,11 +1,12 @@
using mbts.ui.controls;
using Google.Protobuf;
using mbts.ui.controls;
using ProtoBuf;
using System;
using System.Globalization;
namespace mbts.ui {
[ProtoContract]
public struct TimeFilterTuple : IEquatable<TimeFilterTuple> {
public struct TimeFilterTuple : IDeepCloneable<TimeFilterTuple>, IEquatable<TimeFilterTuple> {
[ProtoMember(1)]
readonly TimeFilterMode _mode;
[ProtoMember(2)]
@@ -27,6 +28,8 @@ namespace mbts.ui {
_mode == TimeFilterMode.CMEOpen ||
(_mode == TimeFilterMode.Period && (_begin.result.HasValue || _end.result.HasValue));
public TimeFilterTuple Clone() =>
new TimeFilterTuple(_mode, _begin.Clone(), _end.Clone());
public bool Equals(TimeFilterTuple other) =>
other._mode == _mode && other._begin.Equals(_begin) && other._end.Equals(_end);
@@ -42,7 +45,7 @@ namespace mbts.ui {
}
[ProtoContract]
public struct TimeTextTuple : IEquatable<TimeTextTuple> {
public struct TimeTextTuple : IDeepCloneable<TimeTextTuple>, IEquatable<TimeTextTuple> {
[ProtoMember(1)]
readonly string _month_text;
[ProtoMember(2)]
@@ -86,6 +89,9 @@ namespace mbts.ui {
}
}
public TimeTextTuple Clone() =>
new TimeTextTuple(_month_text, _day_text, _year_text, _time_text);
public bool Equals(TimeTextTuple other) =>
other._month_text == month_text &&
other._day_text == _day_text &&
@@ -1,5 +1,6 @@
using bts.api;
using bts.utils;
using Google.Protobuf;
using ProtoBuf;
using System;
using System.Collections.Generic;
@@ -8,7 +9,7 @@ using System.Reactive.Linq;
namespace mbts.spark {
[ProtoContract]
class SecurityCriteria : IEquatable<SecurityCriteria> {
class SecurityCriteria : IDeepCloneable<SecurityCriteria>, IEquatable<SecurityCriteria> {
public SecurityCriteria()
{
clauses = new SecurityCriteriaClause[] { };
@@ -40,13 +41,16 @@ namespace mbts.spark {
internal static IObservable<IEnumerable<SecurityImmutable>> to_securities(SecurityCriteria security_criteria, IBus bus) =>
security_criteria == null ? Observable.Empty<IEnumerable<SecurityImmutable>>() : security_criteria.subscribe(bus);
public SecurityCriteria Clone() =>
new SecurityCriteria(ArrayHelper.convert(clauses, c => c.Clone()));
public bool Equals(SecurityCriteria other) => EqualityComparer.safe_sequence_equals(clauses, other?.clauses);
public override bool Equals(object obj) => Equals(obj as SecurityCriteria);
public override int GetHashCode() => EqualityComparer.safe_aggregate_hash(clauses);
}
[ProtoContract]
readonly struct SecurityCriteriaClause : IEquatable<SecurityCriteriaClause> {
readonly struct SecurityCriteriaClause : IDeepCloneable<SecurityCriteriaClause>, IEquatable<SecurityCriteriaClause> {
public SecurityCriteriaClause(
Venue[] exchanges = null,
SecurityType[] security_types = null,
@@ -86,6 +90,14 @@ namespace mbts.spark {
!symbols.is_null_or_empty() ||
!security_keys.is_null_or_empty();
public SecurityCriteriaClause Clone() =>
new SecurityCriteriaClause(
ArrayHelper.safe_clone(exchanges),
ArrayHelper.safe_clone(security_types),
ArrayHelper.safe_clone(symbols),
ArrayHelper.safe_clone(security_keys),
pretty_name == null ? null : string.Copy(pretty_name));
public override string ToString()
{
return pretty_name;
@@ -1,26 +1,30 @@
using bts.view;
using ProtoBuf;
using System;
using System.Windows.Controls;
namespace bts.shell {
[ProtoContract]
struct SparkMenuDisplaySettings : IEquatable<SparkMenuDisplaySettings> {
[ProtoMember(1)]
public readonly SparkMenuDisplayState display_state;
[ProtoMember(2)]
public readonly Dock dock;
public SparkMenuDisplaySettings(SparkMenuDisplayState display_state, Dock dock) {
this.display_state = display_state;
this.dock = dock;
}
public static SparkMenuDisplaySettings Default =>
new SparkMenuDisplaySettings(SparkMenuDisplayState.Collapsed, Dock.Left);
public bool Equals(SparkMenuDisplaySettings other) =>
display_state == other.display_state &&
dock == other.dock;
}
}
using bts.view;
using Google.Protobuf;
using ProtoBuf;
using System;
using System.Windows.Controls;
namespace bts.shell {
[ProtoContract]
struct SparkMenuDisplaySettings : IDeepCloneable<SparkMenuDisplaySettings>, IEquatable<SparkMenuDisplaySettings> {
[ProtoMember(1)]
public readonly SparkMenuDisplayState display_state;
[ProtoMember(2)]
public readonly Dock dock;
public SparkMenuDisplaySettings(SparkMenuDisplayState display_state, Dock dock) {
this.display_state = display_state;
this.dock = dock;
}
public static SparkMenuDisplaySettings Default =>
new SparkMenuDisplaySettings(SparkMenuDisplayState.Collapsed, Dock.Left);
public SparkMenuDisplaySettings Clone() =>
new SparkMenuDisplaySettings(display_state, dock);
public bool Equals(SparkMenuDisplaySettings other) =>
display_state == other.display_state &&
dock == other.dock;
}
}
@@ -1,4 +1,5 @@
using bts.utils;
using Google.Protobuf;
using ProtoBuf;
using System;
using System.ComponentModel;
@@ -6,7 +7,7 @@ using System.ComponentModel;
namespace mbts.ui {
[Serializable]
[ProtoContract]
class SortingCriteria : IEquatable<SortingCriteria> {
class SortingCriteria : IDeepCloneable<SortingCriteria>, IEquatable<SortingCriteria> {
public SortingCriteria() { }
public SortingCriteria(SortDescription sort_description) : this(sort_description.PropertyName, sort_description.Direction == ListSortDirection.Ascending) { }
public SortingCriteria(string col_name, bool is_ascending) {
@@ -19,6 +20,9 @@ namespace mbts.ui {
[ProtoMember(2)]
public bool is_ascending;
public SortingCriteria Clone() =>
new SortingCriteria(col_name?.Copy(), is_ascending);
public bool Equals(SortingCriteria other) =>
other != null &&
col_name == other.col_name &&
@@ -11,5 +11,8 @@ namespace bts.api {
default: return SecurityType.Unset;
}
}
public static string display_symbol(this SecurityImmutable security) =>
SecurityHelper.get_display_symbol(security);
}
}
@@ -1,10 +1,24 @@
using System;
using mbts.co2;
using System;
using System.Collections.Generic;
using System.Linq;
using static bts.api.Security;
namespace bts.api {
internal static class SecurityHelper {
public static IEnumerable<string> get_all_display_symbols() =>
get_all_display_symbols(UserSymbol.get_all().Select(usym => usym.symbol));
public static IEnumerable<string> get_all_display_symbols(IEnumerable<string> system_symbols) {
// todo: actual impl
return system_symbols;
}
public static string get_display_symbol(SecurityImmutable security) {
// todo: actual impl
return security.Symbol;
}
public static IEnumerable<FutureLeg> get_flat_future_legs(SecurityImmutable sec) {
switch (sec.ContractCase) {
case ContractOneofCase.Spread: return _get_flat_future_legs(sec.Spread.Legs, 1);
+12 -4
View File
@@ -1,4 +1,6 @@
using mbts.utils;
using bts.api.test;
using Google.Protobuf;
using mbts.utils;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -30,9 +32,6 @@ namespace bts.utils {
}
}
public static TResult[] safe_convert<T, TResult>(T[] array, Func<T, TResult> convert) =>
array == null ? Array.Empty<TResult>() : Array.ConvertAll(array, new Converter<T, TResult>(convert));
public static TResult[] convert<T, TResult>(ICollection<T> source, Func<T, TResult> convert) {
var array = new TResult[source.Count];
int i = 0;
@@ -41,12 +40,21 @@ namespace bts.utils {
return array;
}
public static T[] safe_deep_clone<T>(T[] array) where T : IDeepCloneable<T> =>
array == null ? null : Array.ConvertAll(array, v => v.Clone());
public static TResult[] safe_convert<T, TResult>(T[] array, Func<T, TResult> convert) =>
array == null ? Array.Empty<TResult>() : Array.ConvertAll(array, new Converter<T, TResult>(convert));
public static T[] clone<T>(T[] array) {
T[] rv = new T[array.Length];
Array.Copy(array, rv, array.Length);
return rv;
}
public static T[] safe_clone<T>(T[] array) =>
array == null ? Array.Empty<T>() : clone(array);
/// <summary>
/// Attemps to get an exact item count (to initialize the resultant array with an exact length, thus avoiding unneccessary allocations) by checking if the specified source implements <see cref="ICollection{T}"/>, <see cref="IReadOnlyCollection{T}"/>, or <see cref="ICollection"/>.
/// </summary>
@@ -16,5 +16,8 @@ namespace bts.utils {
public static bool SafeContains(this string str, string value) =>
str != null && str.Contains(value);
public static string Copy(this string str) =>
string.Copy(str);
}
}