Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e9a7d576e | ||
|
|
5e056dfff4 | ||
|
|
4029ccf8e6 | ||
|
|
22911d582b | ||
|
|
6ef7a2ccb5 | ||
|
|
737a07cecb | ||
|
|
1cbea3d1ee | ||
|
|
4dbedfa1f7 | ||
|
|
074f87dd37 | ||
|
|
6ee0ca9589 | ||
|
|
5745c13355 | ||
|
|
5f99edde0e | ||
|
|
d39bc5c73c | ||
|
|
9cb6d5ab88 | ||
|
|
278432a35b | ||
|
|
13c414f301 | ||
|
|
34f5265a42 | ||
|
|
83103e6dc4 | ||
|
|
add631b04d | ||
|
|
e08dc5dfba | ||
|
|
6a8e995c9c | ||
|
|
d7c460ac98 | ||
|
|
7871dc5e4f | ||
|
|
1aab1db4ca | ||
|
|
373187c091 | ||
|
|
423d127ee9 | ||
|
|
14f306d50d | ||
|
|
71fe863f1b | ||
|
|
ac47f0246f | ||
|
|
8cef60b5ab |
No files matched your search
@@ -0,0 +1,6 @@
|
||||
1.1.0
|
||||
- With this release we dropped wild cards from limits, so any user
|
||||
that has limits expressed in terms of wild cards will have to have
|
||||
those re-entered explicitly
|
||||
- With this release we add per user location and smp id tags. At least
|
||||
the location will have to be set through the admin UI for every user
|
||||
+9
-4
@@ -1,5 +1,10 @@
|
||||
1.1.0
|
||||
|
||||
Added self match protection, location and self match protection
|
||||
instructions tags, these will have to be set in the UI for the user
|
||||
for orders to be entered.
|
||||
- Added per user self match protection, location and self match protection FIX tag settings
|
||||
- Implemented a new "Trigger Order" algo
|
||||
- Corrected tick formatting for a number of products
|
||||
- Added the hability to recreate canceled orders
|
||||
- Added leg fills to facilitate futured based PnL
|
||||
- PnL report now has PnL columns calculated based on your start of day, current day, and total position
|
||||
- Start, Day and Total PnL columns are painted with a red or green background color based on if the pnl value is negative or positive respectively
|
||||
- Corrected cash per point values, used in pnl calculations, for a number of products
|
||||
- PnL report will now remember any customizations you make to filtering and/or sorting criteria
|
||||
@@ -55,7 +55,7 @@ namespace mbts.spark {
|
||||
})
|
||||
),
|
||||
SparkCommands.ShowTimeAndSales.complete(_vm.selected_securities_observable(), _ => true, secs => secs.Count > 0 ? new SecurityCriteria(secs) : _state.security_criteria),
|
||||
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count < 3, secs => new SecurityCriteria(secs))
|
||||
SparkCommands.ShowSyntheticCreate.complete(_vm.selected_securities_observable(), secs => secs.Count > 0 && secs.Count <= 24, secs => new SecurityCriteria(secs))
|
||||
};
|
||||
_subscriptions = _bus.subscribe(update_securities_tag).OnDispatcher().Subscribe(c =>
|
||||
_state_changed?.Invoke(_state = new MarketViewWindowFactory.StartupData(c, _vm.rows_view.GetSortingCriteria(), _vm.get_filter_states())));
|
||||
@@ -67,4 +67,4 @@ namespace mbts.spark {
|
||||
Commands.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,9 +72,9 @@
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.security_key_filter, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.security_key_filter.active, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_key_filter.open, Source={u:DirectReference grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource IntMinMaxTemplate}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
|
||||
/>
|
||||
</DataGrid.Columns>
|
||||
</c:DataGridEx>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -53,6 +53,8 @@ namespace bts.shell {
|
||||
public IntMinMaxState n_legs;
|
||||
[ProtoMember(5)]
|
||||
public UIntMinMaxState n_working_orders;
|
||||
[ProtoMember(6)]
|
||||
public string security_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,9 @@ namespace mbts.spark {
|
||||
description_filter.load_state(filters.description);
|
||||
n_legs_filter.load_state(filters.n_legs);
|
||||
n_working_orders_filter.load_state(filters.n_working_orders);
|
||||
security_key_filter.load_state(filters.security_key);
|
||||
}
|
||||
_dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter);
|
||||
_dispose = DataGridFilterHelper.hook_filters(rows_view, create_time_filter, name_filter, description_filter, n_legs_filter, n_working_orders_filter, security_key_filter);
|
||||
}
|
||||
|
||||
protected override Row row_factory((ExecutionStrategyImmutable? strategy, SecurityMD md) row_model) =>
|
||||
@@ -41,6 +42,7 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<Row, string> description_filter { get; } = new RegexFilterVM<Row>(r => r.description, nameof(Row.description));
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> n_legs_filter { get; } = new IntMinMax<Row>(r => r.n_legs, nameof(Row.n_legs));
|
||||
public IDataGridColumnPersistentFilter<Row, UIntMinMaxState> n_working_orders_filter { get; } = new UIntMinMax<Row>(r => r.n_working_orders, nameof(Row.n_legs));
|
||||
public IDataGridColumnPersistentFilter<Row, string> security_key_filter { get; } = new RegexFilterVM<Row>(r => r.security_key.ToString(), nameof(Row.security_key));
|
||||
|
||||
public FilterStates get_filter_states() =>
|
||||
new FilterStates {
|
||||
@@ -48,7 +50,8 @@ namespace mbts.spark {
|
||||
create_time = create_time_filter.get_state(),
|
||||
name = name_filter.get_state(),
|
||||
n_legs = n_legs_filter.get_state(),
|
||||
n_working_orders = n_working_orders_filter.get_state()
|
||||
n_working_orders = n_working_orders_filter.get_state(),
|
||||
security_key = security_key_filter.get_state()
|
||||
};
|
||||
#endregion
|
||||
|
||||
@@ -104,4 +107,4 @@ namespace mbts.spark {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
@@ -83,8 +84,8 @@ namespace bts.shell {
|
||||
public DoubleMinMaxState total_pnl;
|
||||
[ProtoMember(12)]
|
||||
public DoubleMinMaxState pnl_price;
|
||||
|
||||
|
||||
[ProtoMember(13)]
|
||||
public Fill.Types.FillKind[] fill_kind;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,10 +21,9 @@ namespace mbts.spark {
|
||||
public ExistingOrderTicketVM(OrdersModel model, IObservable<OrderRecordTuple> order_source, IObservable<int[]> calculator_buttons) {
|
||||
uint? key = null;
|
||||
var security_md_source = order_source.Where(ot => ot.security_md.HasValue).Select(ot => ot.security_md.Value);
|
||||
var security_source = security_md_source.Select(smd => smd.security);
|
||||
//
|
||||
_security = backing(nameof(security), security_source, SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_source.OnDispatcher(), order_source.Select(ot => new InlineOrderProperties(model, ot.order.Def)));
|
||||
_security = backing(nameof(security), security_md_source.Select(smd => smd.security), SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_md_source, order_source.Select(ot => new InlineOrderProperties(model, ot.order.Def)));
|
||||
_price = backing(nameof(price), double.NaN);
|
||||
size_calculator = new CalculatorLogic(null, calculator_buttons);
|
||||
_current_price = backing(nameof(current_price), order_source.Select(ort => ort.order.State.Price));
|
||||
|
||||
@@ -47,9 +47,8 @@ namespace mbts.spark {
|
||||
IObservable<InlineOrderTicketProperties> props_source,
|
||||
IObservable<int[]> calculator_buttons
|
||||
) {
|
||||
var security_source = security_md_source.Select(smd => smd.security);
|
||||
_security = backing(nameof(security), security_source, SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_source, props_source.Select(p => p.order_props));
|
||||
_security = backing(nameof(security), security_md_source.Select(smd => smd.security), SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
properties_vm = new OrderPropertiesEditorVM(model, security_md_source, props_source.Select(p => p.order_props));
|
||||
bbo = new BBOVM(security_md_source);
|
||||
send_order = new DelegateCommand<Side>(side => {
|
||||
bool price_valid = _validate_price(), size_valid = size_calculator.validate();
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace mbts.spark {
|
||||
class InlineOrderProperties {
|
||||
@@ -44,8 +43,9 @@ namespace mbts.spark {
|
||||
readonly SelectorProperty<IOrderTypeVM> _order_type;
|
||||
IDisposable _order_type_param_errors_sub;
|
||||
|
||||
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityImmutable> security_source, IObservable<InlineOrderProperties> order_properties) {
|
||||
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineOrderProperties> order_properties) {
|
||||
_model = model;
|
||||
var security_source = security_md_source.Select(smd => smd.security);
|
||||
_security = backing(nameof(security), security_source.OnDispatcher(), SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
// guarentee that "this.security" will always be set before observers are notified
|
||||
// this is important bc some observers will reference "this.security" in their callbacks
|
||||
@@ -75,7 +75,7 @@ namespace mbts.spark {
|
||||
_dispose = Disposable.Create(() => _order_type.Changed -= on_selected_order_type_changed);
|
||||
// local methods
|
||||
IOrderTypeVM create_order_type_vm(CustomOrderDefinitionImmutable cd) {
|
||||
var vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_source);
|
||||
var vm = OrderTypeVMFactory.create(cd.CustomCase, model, security_md_source);
|
||||
vm.load_from(cd);
|
||||
return vm;
|
||||
}
|
||||
@@ -136,6 +136,7 @@ namespace mbts.spark {
|
||||
protected override void Dispose(bool disposing) {
|
||||
base.Dispose(disposing);
|
||||
Disposable.dispose(ref _order_type_param_errors_sub);
|
||||
_order_type.items.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
class IcebergOrderVM : OrderTypeVMBase<IcebergOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -34,18 +34,18 @@ namespace mbts.spark {
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public IcebergOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineIcebergOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public IcebergOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineIcebergOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public IcebergOrderVM(OrdersModel model,
|
||||
IObservable<SecurityImmutable> security_source,
|
||||
IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineIcebergOrderProperties> inline_properties)
|
||||
: base(model) {
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override) {
|
||||
@@ -68,7 +68,7 @@ namespace mbts.spark {
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null) {
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
}
|
||||
return _child_order_choices;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace mbts.spark
|
||||
|
||||
class JoinOrderVM : OrderTypeVMBase<JoinOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -79,17 +79,17 @@ namespace mbts.spark
|
||||
}
|
||||
public int hedge_ticks { get; set; } = 1;
|
||||
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineJoinOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineJoinOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public JoinOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineJoinOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
@@ -157,7 +157,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace mbts.spark
|
||||
|
||||
class LeanOrderVM : OrderTypeVMBase<LeanOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -46,17 +46,17 @@ namespace mbts.spark
|
||||
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
|
||||
public uint max_chase_ticks { get; set; } = 10;
|
||||
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineLeanOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineLeanOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public LeanOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineLeanOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
@@ -104,7 +104,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using bts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
@@ -12,33 +13,30 @@ namespace mbts.spark {
|
||||
public static IOrderTypeVM market(OrdersModel model) => new MarketOrderVM(model);
|
||||
public static IOrderTypeVM limit(OrdersModel model) => new LimitOrderVM(model);
|
||||
public static IOrderTypeVM stop(OrdersModel model) => new StopOrderVM(model);
|
||||
public static IOrderTypeVM stop_limit(OrdersModel model) => new StopLimitOrderVM(model);
|
||||
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TimeBoxOrderVM(model, security_source);
|
||||
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TickOrderVM(model, security_source);
|
||||
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityImmutable> security_source) => new LeanOrderVM(model, security_source);
|
||||
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityImmutable> security_source) => new JoinOrderVM(model, security_source);
|
||||
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TopOrderVM(model, security_source);
|
||||
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TriggerOrderVM(model, security_source);
|
||||
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityImmutable> security_source) => new IcebergOrderVM(model, security_source);
|
||||
public static IOrderTypeVM stop_limit(OrdersModel model, IObservable<SecurityMD> security_md_source) => new StopLimitOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TimeBoxOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TickOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityMD> security_md_source) => new LeanOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityMD> security_md_source) => new JoinOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TopOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM trigger(OrdersModel model, IObservable<SecurityMD> security_md_source) => new TriggerOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityMD> security_md_source) => new IcebergOrderVM(model, security_md_source);
|
||||
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source) => new SpreaderOrderVM(model, security_source);
|
||||
|
||||
static Func<CustomOneofCase, IOrderTypeVM> _get_factory(OrdersModel orders_model, IObservable<SecurityImmutable> security_source) =>
|
||||
new Func<CustomOneofCase, IOrderTypeVM>(ot => create(ot, orders_model, security_source));
|
||||
|
||||
public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model, IObservable<SecurityImmutable> security_source) {
|
||||
public static IOrderTypeVM create(CustomOneofCase order_type, OrdersModel model, IObservable<SecurityMD> security_md_source) {
|
||||
switch (order_type) {
|
||||
case CustomOneofCase.Market: return market(model);
|
||||
case CustomOneofCase.Limit: return limit(model);
|
||||
case CustomOneofCase.Stop: return stop(model);
|
||||
case CustomOneofCase.StopLimit: return stop_limit(model);
|
||||
case CustomOneofCase.TimeBox: return time_box(model, security_source);
|
||||
case CustomOneofCase.Tick: return tick(model, security_source);
|
||||
case CustomOneofCase.Lean: return lean(model, security_source);
|
||||
case CustomOneofCase.Join: return join(model, security_source);
|
||||
case CustomOneofCase.Top: return top(model, security_source);
|
||||
case CustomOneofCase.Trigger: return trigger(model, security_source);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_source);
|
||||
case CustomOneofCase.Spreader: return spreader(model, security_source);
|
||||
case CustomOneofCase.StopLimit: return stop_limit(model, security_md_source);
|
||||
case CustomOneofCase.TimeBox: return time_box(model, security_md_source);
|
||||
case CustomOneofCase.Tick: return tick(model, security_md_source);
|
||||
case CustomOneofCase.Lean: return lean(model, security_md_source);
|
||||
case CustomOneofCase.Join: return join(model, security_md_source);
|
||||
case CustomOneofCase.Top: return top(model, security_md_source);
|
||||
case CustomOneofCase.Trigger: return trigger(model, security_md_source);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_md_source);
|
||||
case CustomOneofCase.Spreader: return spreader(model, security_md_source.Select(smd => smd.security));
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,61 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.utils;
|
||||
using mbts.co2;
|
||||
using mbts.ui;
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
private class StopLimitOrderVM : OrderTypeVMBase<StopLimitOrderDefinition> {
|
||||
double _stop_price = 0;
|
||||
class InlineStopLimitOrderProperties {
|
||||
readonly Func<InlineUserProperty<double>> _get_stop_price;
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model) : base(model) { }
|
||||
public static IObservable<InlineStopLimitOrderProperties> subscribe(IObservable<SecurityMD> security_md_source) {
|
||||
ulong time = 0;
|
||||
return security_md_source.Select(smd => new InlineStopLimitOrderProperties(() => {
|
||||
smd.md.get_bbo(ref time, out NativePriceSize bid, out NativePriceSize ask);
|
||||
return new InlineUserProperty<double>(bid.price);
|
||||
}));
|
||||
}
|
||||
|
||||
public InlineStopLimitOrderProperties(Func<InlineUserProperty<double>> get_stop_price) =>
|
||||
_get_stop_price = get_stop_price;
|
||||
|
||||
public InlineUserProperty<double> stop_price => _get_stop_price();
|
||||
}
|
||||
|
||||
private class StopLimitOrderVM : OrderTypeVMBase<StopLimitOrderDefinition>, IDisposable {
|
||||
UserProperty<double> _stop_price;
|
||||
InlineStopLimitOrderProperties _inline_props;
|
||||
IDisposable _dispose;
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source.Select(smd => smd.security.spec_or_default()), InlineStopLimitOrderProperties.subscribe(security_md_source)) { }
|
||||
|
||||
internal StopLimitOrderVM(OrdersModel model, IObservable<ContractSpecificationImmutable> spec_source, IObservable<InlineStopLimitOrderProperties> inline_properties) : base(model) {
|
||||
_stop_price = new UserProperty<double>(backing(nameof(stop_price), double.NaN));
|
||||
_dispose = Disposable.Create(
|
||||
spec_source.OnDispatcher().Subscribe(s => {
|
||||
spec = s;
|
||||
RaisePropertyChanged(nameof(spec));
|
||||
}), inline_properties.OnDispatcher().Subscribe(p => _inline_props = p));
|
||||
}
|
||||
|
||||
public override bool editable_properties_open {
|
||||
get => base.editable_properties_open;
|
||||
set {
|
||||
if (value) {
|
||||
if (_update_inline_props())
|
||||
_validate();
|
||||
}
|
||||
base.editable_properties_open = value;
|
||||
}
|
||||
}
|
||||
|
||||
public double stop_price {
|
||||
get => _stop_price;
|
||||
get => _stop_price.Value;
|
||||
set {
|
||||
_stop_price = value;
|
||||
_stop_price.Value = value;
|
||||
_validate();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +64,8 @@ namespace mbts.spark {
|
||||
SecurityDataHelper.Defaults.ContractSpecificationImmutable;
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value) {
|
||||
_update_inline_props();
|
||||
_validate();
|
||||
if (!HasErrors) {
|
||||
var sl = factory.stop_limit_order();
|
||||
sl.StopPrice = stop_price;
|
||||
@@ -28,9 +73,22 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => Disposable.dispose(ref _dispose);
|
||||
|
||||
// no need to constantly poll the md, just check the inline props when the popup opens, and right before sending an order
|
||||
// (if the user never looks at the popup, assume that the bid_price will be used)
|
||||
// TODO: add settings so the trigger-price can default to something relative to the md, e.g "bid-price" or "ask-price - nticks" or "best-bid-price with a minimum size of 10"
|
||||
bool _update_inline_props() {
|
||||
var inline_stop_price = _inline_props.stop_price;
|
||||
if (inline_stop_price.has_value && (inline_stop_price.force_override || !_stop_price.setter_called)) {
|
||||
_stop_price.bypass_setter(inline_stop_price.value);
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
void _validate() {
|
||||
using (validation())
|
||||
validation_assert(!DoubleHelper.IsNaNOrInfinity(_stop_price), string.Empty, nameof(stop_price));
|
||||
validation_assert(!DoubleHelper.IsNaNOrInfinity(stop_price), string.Empty, nameof(stop_price));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace mbts.spark
|
||||
|
||||
class TickOrderVM : OrderTypeVMBase<TickOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -55,17 +55,17 @@ namespace mbts.spark
|
||||
public uint max_chase_ticks { get; set; } = 10;
|
||||
public bool detach_on_toward { get; set; } = true;
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineTickOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTickOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public TickOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTickOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
@@ -131,7 +131,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace mbts.spark {
|
||||
|
||||
class TimeBoxOrderVM : OrderTypeVMBase<TimeBoxOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable {
|
||||
const double _start_immediately_delay_ms = 500;
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -40,18 +40,18 @@ namespace mbts.spark {
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineTimeBoxOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public TimeBoxOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTimeBoxOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TimeBoxOrderVM(OrdersModel model,
|
||||
IObservable<SecurityImmutable> security_source,
|
||||
IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTimeBoxOrderProperties> inline_properties)
|
||||
: base(model) {
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override) {
|
||||
@@ -94,7 +94,7 @@ namespace mbts.spark {
|
||||
public ICollection<IOrderTypeVM> child_order_types {
|
||||
get {
|
||||
if (_child_order_choices == null) {
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
}
|
||||
return _child_order_choices;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace mbts.spark
|
||||
|
||||
class TopOrderVM : OrderTypeVMBase<TopOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -77,18 +77,18 @@ namespace mbts.spark
|
||||
}
|
||||
public int hedge_ticks { get; set; } = 1;
|
||||
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineTopOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList())))
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTopOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList())))
|
||||
{ }
|
||||
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public TopOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTopOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p =>
|
||||
{
|
||||
@@ -157,7 +157,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace mbts.spark
|
||||
|
||||
class TriggerOrderVM : OrderTypeVMBase<TriggerOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
readonly IObservable<SecurityMD> _security_md_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
@@ -46,17 +46,17 @@ namespace mbts.spark
|
||||
public TriggerPriceType trigger_price_type { get; set; } = TriggerPriceType.Bid;
|
||||
public double order_price { get; set; } = double.NaN;
|
||||
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.Select(s =>
|
||||
new InlineTriggerOrderProperties(model.factory.default_order_type(s), model.factory.default_order_types(s).ToList()))) { }
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source)
|
||||
: this(model, security_md_source, security_md_source.Select(smd =>
|
||||
new InlineTriggerOrderProperties(model.factory.default_order_type(smd.security), model.factory.default_order_types(smd.security).ToList()))) { }
|
||||
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source,
|
||||
public TriggerOrderVM(OrdersModel model, IObservable<SecurityMD> security_md_source,
|
||||
IObservable<InlineTriggerOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_security_md_source = security_md_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.OnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
security_md_source.OnDispatcher()
|
||||
.Subscribe(smd => _security = smd.security),
|
||||
inline_properties.OnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
@@ -124,7 +124,7 @@ namespace mbts.spark
|
||||
get
|
||||
{
|
||||
if (_child_order_choices == null)
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
_child_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_md_source));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,9 @@ namespace mbts.ui.shell
|
||||
_recently_closed_windows.Push(() => { undoaction(); });
|
||||
}
|
||||
}
|
||||
_bookmark.save_bookmark(document.Model);
|
||||
if (document.HasCustomTitle) {
|
||||
_bookmark.save_bookmark(document.Model);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove_document(TabDocument document) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.co2;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -8,15 +7,15 @@ namespace mbts.spark {
|
||||
readonly OrdersModel _order_gateway;
|
||||
|
||||
public LadderModel(SecurityMD source, OrdersModel ordergw) {
|
||||
security = source.security;
|
||||
md = source.md;
|
||||
security_md = source;
|
||||
_order_gateway = ordergw;
|
||||
}
|
||||
|
||||
public enum CancelType { All, Bids, Asks }
|
||||
|
||||
public SecurityImmutable security { get; }
|
||||
public MDConsumerHandle md { get; }
|
||||
public SecurityMD security_md { get; }
|
||||
public SecurityImmutable security => security_md.security;
|
||||
public MDConsumerHandle md => security_md.md;
|
||||
public OrdersModel order_gateway => _order_gateway;
|
||||
|
||||
public Task cancel_orders(CancelType ct) {
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace mbts.spark {
|
||||
|
||||
_dispose = Disposable.Create(
|
||||
settings_command,
|
||||
order_properties_vm = new OrderPropertiesEditorVM(model.order_gateway, Observable.Return(security), _settings_manager.subscribe_current().Select(s => new InlineOrderProperties(model.order_gateway, s))),
|
||||
order_properties_vm = new OrderPropertiesEditorVM(model.order_gateway, Observable.Return(model.security_md), _settings_manager.subscribe_current().Select(s => new InlineOrderProperties(model.order_gateway, s))),
|
||||
size_calculator = new CalculatorLogic(0, _settings_manager.subscribe_current().Select(s => s.calculator_buttons)),
|
||||
bus.subscribe(SparkTags.LogActivity).OnDispatcher().Subscribe(status_update),
|
||||
model.order_gateway.working_orders.OnDispatcher().Subscribe(working => {
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Data;
|
||||
using static bts.shell.PAndLWindowFactory;
|
||||
using static bts.api.orderman.Fill.Types;
|
||||
|
||||
namespace mbts.spark {
|
||||
class PAndLVM : DisposableViewModelBase {
|
||||
@@ -58,6 +59,7 @@ namespace mbts.spark {
|
||||
total_net_position_filter.load_state(fs.total_net_pos);
|
||||
total_pnl_filter.load_state(fs.total_pnl);
|
||||
pnl_price_filter.load_state(fs.pnl_price);
|
||||
fill_kind_filter.load_state(fs.fill_kind);
|
||||
}
|
||||
// restore sort state or supply default value
|
||||
if (startup_data?.sort_criteria?.Any() == true) {
|
||||
@@ -89,7 +91,8 @@ namespace mbts.spark {
|
||||
total_pnl_filter,
|
||||
pnl_price_filter,
|
||||
weighted_average_buy_filter,
|
||||
weighted_average_sell_filter),
|
||||
weighted_average_sell_filter,
|
||||
fill_kind_filter),
|
||||
WorkLoop.Start("PAndL", _UPDATE_PERIOD, _calculate_pnl)
|
||||
);
|
||||
}
|
||||
@@ -194,6 +197,7 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> pnl_price_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.pnl_price, nameof(PnLSummaryRow.pnl_price));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_buy_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_buy, nameof(PnLSummaryRow.weighted_average_buy));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> weighted_average_sell_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.weighted_average_sell, nameof(PnLSummaryRow.weighted_average_sell));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, FillKind[]> fill_kind_filter { get; } = DiscreteValuesFilterVM.Create<PnLSummaryRow, FillKind>(EnumHelper.get_values<FillKind>().ToArray(), r => r.fill_kind, nameof(PnLSummaryRow.fill_kind));
|
||||
|
||||
public FilterStates get_filters_states() => new FilterStates {
|
||||
account_regex = account_filter.get_state(),
|
||||
@@ -207,7 +211,8 @@ namespace mbts.spark {
|
||||
day_pnl = day_pnl_filter.get_state(),
|
||||
total_net_pos = total_net_position_filter.get_state(),
|
||||
total_pnl = total_pnl_filter.get_state(),
|
||||
pnl_price = pnl_price_filter.get_state()
|
||||
pnl_price = pnl_price_filter.get_state(),
|
||||
fill_kind = fill_kind_filter.get_state()
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -64,6 +64,12 @@
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.security_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource RegexTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="fill_kind" Header="Fill Kind" Binding="{Binding fill_kind}" IsReadOnly="true"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.fill_kind_filter, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterActive="{Binding DataContext.fill_kind_filter.active, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterOpen="{Binding DataContext.fill_kind_filter.open, Source={uc:DirectReference _grid}}"
|
||||
c:DataGridFilterProperties.FilterTemplate="{StaticResource DiscreteValuesTemplate}"
|
||||
/>
|
||||
<DataGridTextColumn x:Name="start_net_pos" Header="Start Net Pos" Binding="{Binding start_net_position}" IsReadOnly="true"
|
||||
ElementStyle="{StaticResource RightAlignedColumn}"
|
||||
c:DataGridFilterProperties.Filter="{Binding DataContext.start_position_filter, Source={uc:DirectReference _grid}}"
|
||||
@@ -79,7 +85,7 @@
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="start_pnl"/>
|
||||
<Binding Path="security"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
<DataGridTextColumn.ElementStyle>
|
||||
@@ -111,7 +117,7 @@
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="day_pnl"/>
|
||||
<Binding Path="security"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
<DataGridTextColumn.ElementStyle>
|
||||
@@ -136,7 +142,7 @@
|
||||
<DataGridTextColumn.Binding>
|
||||
<MultiBinding Converter="{StaticResource TickDisplayConverter}">
|
||||
<Binding Path="total_pnl"/>
|
||||
<Binding Path="security"/>
|
||||
<Binding Path="security.ContractSpec"/>
|
||||
</MultiBinding>
|
||||
</DataGridTextColumn.Binding>
|
||||
<DataGridTextColumn.ElementStyle>
|
||||
|
||||
@@ -86,12 +86,16 @@ namespace mbts.spark {
|
||||
// Note that this isn't the acutal start of the trading day
|
||||
// but it's close enough for pnl purposes.
|
||||
// In reality, instruments have different schedules
|
||||
_start_of_trading_day_utc = DateTime.Now.Date.Add(start_of_day_ts).AddDays(-1).ToUniversalTime();
|
||||
var _start_of_trading_day_local = DateTime.Now.Date.Add(start_of_day_ts);
|
||||
if (_start_of_trading_day_local >= DateTime.Now) {
|
||||
_start_of_trading_day_local = _start_of_trading_day_local.AddDays(-1);
|
||||
}
|
||||
_start_of_trading_day_utc = _start_of_trading_day_local.ToUniversalTime();
|
||||
}
|
||||
|
||||
public IEnumerable<PnLSummaryRow> get_summary_position() {
|
||||
return (from pnl_item in _data
|
||||
group pnl_item by (pnl_item.account, pnl_item.exchange, pnl_item.symbol, pnl_item.security, pnl_item.sec_md) into grouping
|
||||
group pnl_item by (pnl_item.account, pnl_item.exchange, pnl_item.symbol, pnl_item.security, pnl_item.sec_md, pnl_item.fill_kind) into grouping
|
||||
/* let start_of_day = _get_start_of_day(grouping.Key.symbol) */
|
||||
select new PnLSummaryRow(_calc_type, _start_of_trading_day_utc, grouping.Key,
|
||||
grouping.Where(y => y.fill.Timestamp.ToDateTime() >= _start_of_trading_day_utc).Sum(y => (uint)y.buy_quantity),
|
||||
@@ -105,8 +109,11 @@ namespace mbts.spark {
|
||||
);
|
||||
}
|
||||
|
||||
public static bool get_driver(ref ulong bbo_time, ref ulong last_trade_time, SecurityMD sec_md, pnl_calc_type pnl_request, ref double driver_price) {
|
||||
// check with eric as I'm not sure if i was every querying md correctly
|
||||
public static bool get_driver(SecurityMD sec_md, pnl_calc_type pnl_request, ref double driver_price) {
|
||||
// we always want to pass in 0 to the md query so that we don't get back NaN if the market
|
||||
// data hasn't changed between query periods
|
||||
ulong bbo_time = 0;
|
||||
ulong last_trade_time = 0;
|
||||
if (!sec_md.md.get_bbo(ref bbo_time, out NativePriceSize bid, out NativePriceSize ask)) return false;
|
||||
if (!sec_md.md.get_last_trade(ref last_trade_time, out NativePriceSize last, out DateTime unused1, out ulong unused2)) return false;
|
||||
|
||||
@@ -188,7 +195,7 @@ namespace mbts.spark {
|
||||
if (pnl_request.HasFlag(pnl_calc_type.last)) { driver_price = last.price; return true; }
|
||||
// note that bid_or_ask is not considered here since this method only
|
||||
// supports returning a single driver price; see update_pnl
|
||||
if (pnl_request.HasFlag(pnl_calc_type.midpoint)) { driver_price = TickUtils.mid_tick(sec_md.security.ContractSpec, bid.price, ask.price); return true; }
|
||||
if (pnl_request.HasFlag(pnl_calc_type.midpoint)) { driver_price = (bid.price + ask.price) / 2.0; return true; }
|
||||
if (pnl_request.HasFlag(pnl_calc_type.open)) { driver_price = sec_md.md.open_price; return true; }
|
||||
if (pnl_request.HasFlag(pnl_calc_type.close)) { driver_price = sec_md.md.close_price; return true; }
|
||||
if (pnl_request.HasFlag(pnl_calc_type.settle)) { driver_price = sec_md.md.settlement_price; return true; }
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace mbts.spark {
|
||||
public SecurityImmutable? security => sec_md?.security;
|
||||
public double buy_quantity => (fill.Side == Side.Buy) ? fill.LastShares : 0;
|
||||
public double sell_quantity => (fill.Side == Side.Sell) ? fill.LastShares : 0;
|
||||
public Fill.Types.FillKind fill_kind => fill.FillKind;
|
||||
|
||||
public override string ToString() {
|
||||
return $"Fill: ( {account} {exchange} {security.ToString()} buy: {buy_quantity} sell: {sell_quantity} ) Security: {security.ToString()}";
|
||||
|
||||
@@ -13,19 +13,18 @@ namespace mbts.spark {
|
||||
|
||||
internal class PnLSummaryRow : ViewModelBase, IEquatable<PnLSummaryRow> {
|
||||
DateTime _start_of_day;
|
||||
(string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md) key;
|
||||
(string account, Venue? exchange, string symbol, SecurityImmutable? security, SecurityMD? sec_md, Fill.Types.FillKind fill_kind) key;
|
||||
int _start_net_position, _total_net_position;
|
||||
uint _day_long_quantity, _day_short_quantity;
|
||||
double _total_pnl, _sod_pnl, _start_pnl, _pnl_price, _weighted_average_buy, _weighted_average_sell;
|
||||
IEnumerable<FillImmutable> _fills;
|
||||
ulong _bbo_ref, _last_ref;
|
||||
double _driver;
|
||||
static readonly Dictionary<string, double> cpp_dict = config.Configuration.get<bts.config.CppConfig>().cpp_entry;
|
||||
|
||||
|
||||
public PnLSummaryRow(pnl_calc_type calc_type,
|
||||
DateTime start_of_day,
|
||||
(string, Venue?, string, SecurityImmutable?, SecurityMD?) key,
|
||||
(string, Venue?, string, SecurityImmutable?, SecurityMD?, Fill.Types.FillKind) key,
|
||||
uint day_buy_quant, uint day_sell_quant, int net_pos, int sod_pos, double wavg_buy, double wavg_sell, IEnumerable<FillImmutable> fills) {
|
||||
this._start_of_day = start_of_day;
|
||||
this.key = key;
|
||||
@@ -75,7 +74,8 @@ namespace mbts.spark {
|
||||
if (key.sec_md.HasValue) {
|
||||
double cpp = _get_cpp_for_security(key.security.Value);
|
||||
if (pnl_request.HasFlag(pnl_calc_type.bid_or_ask)) {
|
||||
key.sec_md.Value.md.get_bbo(ref _bbo_ref, out NativePriceSize bid, out NativePriceSize ask);
|
||||
ulong bbo_ref = 0;
|
||||
key.sec_md.Value.md.get_bbo(ref bbo_ref, out NativePriceSize bid, out NativePriceSize ask);
|
||||
|
||||
double summary_pnl = 0.0;
|
||||
double start_pnl_total = 0.0;
|
||||
@@ -94,7 +94,7 @@ namespace mbts.spark {
|
||||
start_pnl = start_pnl_total;
|
||||
pnl_price = Double.NaN;
|
||||
} else {
|
||||
if (PnLCalculator.get_driver(ref _bbo_ref, ref _last_ref, key.sec_md.Value, pnl_request, ref _driver)) {
|
||||
if (PnLCalculator.get_driver(key.sec_md.Value, pnl_request, ref _driver)) {
|
||||
double summary_pnl = 0.0;
|
||||
double start_pnl_total = 0.0;
|
||||
double day_pnl_total = 0.0;
|
||||
@@ -176,6 +176,8 @@ namespace mbts.spark {
|
||||
set => ignore();
|
||||
}
|
||||
|
||||
public Fill.Types.FillKind fill_kind => key.fill_kind;
|
||||
|
||||
|
||||
public int start_net_position {
|
||||
get => _start_net_position;
|
||||
@@ -224,7 +226,6 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int total_net_position {
|
||||
get => _total_net_position;
|
||||
set {
|
||||
|
||||
@@ -1114,7 +1114,7 @@ api::SecurityType AdminServiceUtils::security_type(const api::Security& security
|
||||
// boostrapping ////////////////////////////
|
||||
////////////////////////////////////////////
|
||||
|
||||
void add_product_position_limits(bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type)
|
||||
void add_product_position_limits(bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type, uint32_t limit)
|
||||
{
|
||||
std::vector<md::UserSymbol> symbols;
|
||||
md::get_system_md_symbols(venue, sec_type,&symbols);
|
||||
@@ -1124,12 +1124,12 @@ void add_product_position_limits(bts::api::admin::BTSAccount* to_account, api::V
|
||||
acct_a_prod_limit->mutable_product()->set_exchange(s.exchange());
|
||||
acct_a_prod_limit->mutable_product()->set_security_type(s.type());
|
||||
acct_a_prod_limit->mutable_product()->set_security_symbol(s.symbol());
|
||||
acct_a_prod_limit->set_long_position_limit(500000);
|
||||
acct_a_prod_limit->set_short_position_limit(500000);
|
||||
acct_a_prod_limit->set_long_position_limit(limit);
|
||||
acct_a_prod_limit->set_short_position_limit(limit);
|
||||
}
|
||||
}
|
||||
|
||||
void add_security_clip_sizes_to_account(bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type)
|
||||
void add_security_clip_sizes_to_account(bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type,uint32_t limit)
|
||||
{
|
||||
std::vector<md::UserSymbol> symbols;
|
||||
md::get_system_md_symbols(venue, sec_type,&symbols);
|
||||
@@ -1139,21 +1139,31 @@ void add_security_clip_sizes_to_account(bts::api::admin::BTSAccount* to_account,
|
||||
acct_a_clip_size->mutable_security()->set_exchange(s.exchange());
|
||||
acct_a_clip_size->mutable_security()->set_security_type(s.type());
|
||||
acct_a_clip_size->mutable_security()->set_security_symbol(s.symbol());
|
||||
acct_a_clip_size->set_clip_size(50);
|
||||
acct_a_clip_size->set_clip_size(limit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void add_security_position_limits (bts::api::admin::BTSAccount* to_account, api::Venue venue, api::SecurityType sec_type)
|
||||
{
|
||||
auto acct_a1_sec_limit = to_account->add_security_position_limits();
|
||||
acct_a1_sec_limit->mutable_security()->set_exchange(venue);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_type(sec_type);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_symbol("*");
|
||||
acct_a1_sec_limit->set_long_position_limit (10000);
|
||||
acct_a1_sec_limit->set_short_position_limit (10000);
|
||||
}
|
||||
|
||||
void AdminServiceUtils::bootstrap_data (bts::api::admin::AdminData& data){
|
||||
data.set_id(1);
|
||||
bts::api::admin::AdminServiceOperationResult result;
|
||||
bts::api::admin::AdminServiceCreateAccountResult account_result;
|
||||
bts::api::admin::AdminServiceCreateEntityResult user_result;
|
||||
// adding accounts
|
||||
auto parent_account = create_account(data, 0, std::string("acc a"), std::string("description"), true, account_result);
|
||||
auto account_1 = create_account(data, parent_account->id(), std::string("acc a1"), std::string("description"), true, account_result); account_1->set_is_active(true);
|
||||
auto account_2 = create_account(data, parent_account->id(), std::string("acc a2"), std::string("description"), true, account_result); account_2->set_is_active(true);
|
||||
auto account_3 = create_account(data, parent_account->id(), std::string("acc a3"), std::string("description"), true, account_result); account_3->set_is_active(true);
|
||||
create_account(data, parent_account->id(), std::string("acc a4"), std::string("description"), true, account_result);
|
||||
auto parent_account = create_account(data, 0, std::string("ACC A"), std::string("description"), true, account_result);
|
||||
auto account_1 = create_account(data, parent_account->id(), std::string("ACC A1"), std::string("description"), true, account_result); account_1->set_is_active(true);
|
||||
auto account_2 = create_account(data, parent_account->id(), std::string("ACC A2"), std::string("description"), true, account_result); account_2->set_is_active(true);
|
||||
auto account_3 = create_account(data, parent_account->id(), std::string("ACC A3"), std::string("description"), true, account_result); account_3->set_is_active(true);
|
||||
create_account(data, parent_account->id(), std::string("ACC A4"), std::string("description"), true, account_result);
|
||||
// add group
|
||||
auto def_group = create_group(data, 0, std::string("default"), result);
|
||||
// adding users to the group
|
||||
@@ -1225,65 +1235,36 @@ void AdminServiceUtils::bootstrap_data (bts::api::admin::AdminData& data){
|
||||
data.mutable_accounts(j)->mutable_connections()->add_exchange_connection_names(conn_name);
|
||||
}
|
||||
|
||||
// add prod limit for acc a
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD);
|
||||
// add prod limit for ACC A
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE,50000);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD,50000);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE,50000);
|
||||
add_product_position_limits(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD,50000);
|
||||
|
||||
// security clip sizes for ACC A
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE,50);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD,50);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE,50);
|
||||
add_security_clip_sizes_to_account(parent_account, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD,50);
|
||||
|
||||
// add sec limit for ACC A1
|
||||
// cme
|
||||
auto acct_a1_sec_limit = account_1->add_security_position_limits();
|
||||
acct_a1_sec_limit->mutable_security()->set_exchange(api::Venue::VENUE_GLBX);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_symbol("*");
|
||||
acct_a1_sec_limit->set_long_position_limit (10000);
|
||||
acct_a1_sec_limit->set_short_position_limit (10000);
|
||||
acct_a1_sec_limit = account_1->add_security_position_limits();
|
||||
// sim
|
||||
acct_a1_sec_limit = account_1->add_security_position_limits();
|
||||
acct_a1_sec_limit->mutable_security()->set_exchange(api::Venue::VENUE_SIM);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_sec_limit->mutable_security()->set_security_symbol("*");
|
||||
acct_a1_sec_limit->set_long_position_limit (10000);
|
||||
acct_a1_sec_limit->set_short_position_limit (10000);
|
||||
acct_a1_sec_limit = account_1->add_security_position_limits();
|
||||
add_security_position_limits(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
add_security_position_limits(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD);
|
||||
add_security_position_limits(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
add_security_position_limits(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD);
|
||||
|
||||
// add prod limit for ACC A1
|
||||
// cme
|
||||
auto acct_a1_prod_limit = account_1->add_product_position_limits();
|
||||
acct_a1_prod_limit->mutable_product()->set_exchange(api::Venue::VENUE_GLBX);
|
||||
acct_a1_prod_limit->mutable_product()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_prod_limit->mutable_product()->set_security_symbol("*");
|
||||
acct_a1_prod_limit->set_long_position_limit(1000000);
|
||||
acct_a1_prod_limit->set_short_position_limit(1000000);
|
||||
// sim
|
||||
acct_a1_prod_limit = account_1->add_product_position_limits();
|
||||
acct_a1_prod_limit->mutable_product()->set_exchange(api::Venue::VENUE_SIM);
|
||||
acct_a1_prod_limit->mutable_product()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_prod_limit->mutable_product()->set_security_symbol("*");
|
||||
acct_a1_prod_limit->set_long_position_limit(1000000);
|
||||
acct_a1_prod_limit->set_short_position_limit(1000000);
|
||||
add_product_position_limits(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE,100000);
|
||||
add_product_position_limits(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD,100000);
|
||||
add_product_position_limits(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE,100000);
|
||||
add_product_position_limits(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD,100000);
|
||||
|
||||
// security clip sizes for ACC A1
|
||||
// cme
|
||||
auto acct_a1_clip_size = account_1->mutable_security_clip_limits()->add_limit();
|
||||
acct_a1_clip_size->mutable_security()->set_exchange(api::Venue::VENUE_GLBX);
|
||||
acct_a1_clip_size->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_clip_size->mutable_security()->set_security_symbol("*");
|
||||
acct_a1_clip_size->set_clip_size(100);
|
||||
// sim
|
||||
acct_a1_clip_size = account_1->mutable_security_clip_limits()->add_limit();
|
||||
acct_a1_clip_size->mutable_security()->set_exchange(api::Venue::VENUE_SIM);
|
||||
acct_a1_clip_size->mutable_security()->set_security_type(api::SecurityType::SECURITYTYPE_FUTURE);
|
||||
acct_a1_clip_size->mutable_security()->set_security_symbol("*");
|
||||
acct_a1_clip_size->set_clip_size(100);
|
||||
add_security_clip_sizes_to_account(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_FUTURE,100);
|
||||
add_security_clip_sizes_to_account(account_1, api::Venue::VENUE_GLBX, api::SecurityType::SECURITYTYPE_SPREAD,100);
|
||||
add_security_clip_sizes_to_account(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_FUTURE,100);
|
||||
add_security_clip_sizes_to_account(account_1, api::Venue::VENUE_SIM, api::SecurityType::SECURITYTYPE_SPREAD,100);
|
||||
}
|
||||
bool AdminServiceUtils::has_id (uint32_t id){return id>0;}
|
||||
bool AdminServiceUtils::is_symbol_set (const std::string& symbol){
|
||||
|
||||
@@ -526,6 +526,9 @@ void ExchangeSimApplication::on_depth(
|
||||
|
||||
if (sec.has_synthetic())
|
||||
return ;
|
||||
|
||||
if (!me->do_merge_with_md)
|
||||
return;
|
||||
|
||||
auto it = me->md_consumer_recs.find(sec.glbx().security_desc());
|
||||
bts_assert(it != me->md_consumer_recs.end());
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/api/public/ApiUtils.h"
|
||||
#include "lib/api/core/ApiClientInterop.h"
|
||||
#include "lib/api/services/UserService.client.pb.h"
|
||||
#include "lib/api/services/AdminService.client.pb.h"
|
||||
#include "lib/utils/bts_assert.h"
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
@@ -123,7 +124,7 @@ ApiGatewaySession::~ApiGatewaySession(){}
|
||||
|
||||
void ApiGatewaySession::handle(const api::gateway::GatewayRequest& request, const Responder& reply) {
|
||||
GatewayResponse response;
|
||||
LOG(Api,Info, "Gateway Request: {}", api::toJSON(request));
|
||||
LOG(Api,Diag, "Gateway Request: {}", api::toJSON(request));
|
||||
switch(request.request_case()) {
|
||||
case GatewayRequest::kLogin:
|
||||
_me->handle_login(request.login(), reply);
|
||||
@@ -138,4 +139,32 @@ void ApiGatewaySession::handle(const api::gateway::GatewayRequest& request, cons
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_server_info(const api::ServiceContext& ctx) {
|
||||
using namespace api::admin;
|
||||
using std::endl;
|
||||
api::VoidMessage vod;
|
||||
AdminService admin(ctx.locate<api::admin::AdminService>());
|
||||
auto response = admin.get_admin_data(vod, 100);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "{" << endl;
|
||||
|
||||
if (response.success()) {
|
||||
ss << " \"users\": [" << endl;
|
||||
for (const auto& user : response.result()->users()) {
|
||||
ss << " ";
|
||||
if (&user != &response.result()->users()[0]) ss << ",";
|
||||
ss << "{ ";
|
||||
ss << "\"name\": \"" << user.name() << "\", ";
|
||||
ss << "\"operator_id\": \"" << user.user_data().exchange_user_data().cme_data().operator_id() << "\"";
|
||||
ss << "}" << endl;
|
||||
}
|
||||
ss << " ]" << endl;
|
||||
}
|
||||
|
||||
ss << "}" << endl;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -20,6 +20,10 @@ class ApiGatewaySession {
|
||||
~ApiGatewaySession();
|
||||
|
||||
void handle(const api::gateway::GatewayRequest& request, const Responder&);
|
||||
|
||||
};
|
||||
|
||||
std::string get_server_info(const api::ServiceContext&);
|
||||
|
||||
|
||||
}}
|
||||
@@ -156,6 +156,34 @@ namespace http {
|
||||
_mtimer->observe((utils::TimeStamp::now() - start).microseconds() / 1000.0);
|
||||
}
|
||||
|
||||
bool require_authentication(connection_ptr& con, const std::string& username, const std::string& password) {
|
||||
auto auth = con->get_request_header("Authorization");
|
||||
if (!auth.empty() && auth.size() >= 7 && boost::iequals(auth.substr(0, 6), "Basic ")) {
|
||||
auto b64 = auth.substr(6);
|
||||
auto decoded = websocketpp::base64_decode(b64);
|
||||
auto colon = decoded.find(':');
|
||||
if (colon != std::string::npos) {
|
||||
auto user = decoded.substr(0, colon);
|
||||
auto pass = decoded.substr(colon+1);
|
||||
if (user == username && pass == password) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed
|
||||
con->append_header("WWW-Authenticate", "Basic realm=\"BTS\"");
|
||||
con->set_status(websocketpp::http::status_code::unauthorized);
|
||||
con->set_body("Not Authorized");
|
||||
return false;
|
||||
}
|
||||
|
||||
void check_auth_and_return_info(connection_ptr& con) {
|
||||
if (require_authentication(con, "bts", "bbb835af-b1ea-42d3-8be1-fd9d5555530d")) {
|
||||
auto horrible_json = get_server_info(_ctx);
|
||||
con->set_body(horrible_json);
|
||||
con->set_status(websocketpp::http::status_code::ok);
|
||||
}
|
||||
}
|
||||
|
||||
void directory_index(connection_ptr& con, const std::string& resource, const boost::filesystem::path& path) {
|
||||
// directory listing
|
||||
std::string body = "<!doctype html>\n";
|
||||
@@ -256,6 +284,9 @@ namespace http {
|
||||
else if (boost::iequals(resource, "/metrics")) {
|
||||
return_metrics(con);
|
||||
}
|
||||
else if (boost::iequals(resource, "/server_info")) {
|
||||
check_auth_and_return_info(con);
|
||||
}
|
||||
else if (boost::iequals(resource, "/bts_installer.exe") && boost::filesystem::is_regular_file(_www+"/bts_installer.exe")) {
|
||||
return_bts_installer(con);
|
||||
}
|
||||
|
||||
@@ -176,10 +176,12 @@ let applyUpdate (field:UserField) (user:BTSUser) =
|
||||
| Name n -> { user with name = n }
|
||||
| Password p -> { user with password = p }
|
||||
| IsActive ia -> { user with isActive = ia }
|
||||
| OperatorID oid -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with operatorId = oid }}}}
|
||||
| ATSOperatorID atsoid -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with atsOperatorId = atsoid }}}}
|
||||
| CMEOperatorID oid -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with operatorId = oid }}}}
|
||||
| CMEATSOperatorID atsoid -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with atsOperatorId = atsoid }}}}
|
||||
| SimOperatorID oid -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with simData = { user.userData.exchangeUserData.simData with operatorId = oid }}}}
|
||||
| SMPID smpid -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with smpId = smpid }}}}
|
||||
| Location loc -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with location = loc }}}}
|
||||
| CMELocation loc -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with location = loc }}}}
|
||||
| SimLocation loc -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with simData = { user.userData.exchangeUserData.simData with location = loc }}}}
|
||||
| SMPInstruction smpi -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with smpInstruction = smpi }}}}
|
||||
| MaxOrderRatePerConnection morpc -> { user with userData = { user.userData with exchangeUserData = { user.userData.exchangeUserData with cmeData = { user.userData.exchangeUserData.cmeData with maxOrderRatePerConnection = morpc }}}}
|
||||
| MiscTags map ->
|
||||
|
||||
@@ -13,10 +13,12 @@ type UserField =
|
||||
| Name of string
|
||||
| Password of string
|
||||
| IsActive of bool
|
||||
| OperatorID of string
|
||||
| ATSOperatorID of string
|
||||
| CMEOperatorID of string
|
||||
| CMEATSOperatorID of string
|
||||
| SimOperatorID of string
|
||||
| SMPID of string
|
||||
| Location of string
|
||||
| CMELocation of string
|
||||
| SimLocation of string
|
||||
| SMPInstruction of string
|
||||
| MaxOrderRatePerConnection of string
|
||||
| MiscTags of JSMap<string>
|
||||
|
||||
@@ -146,19 +146,28 @@ let mdEntitlementsTabView (model:Model) (dispatch:Msg->Unit) =
|
||||
|
||||
let orderRoutingTabView (model:Model) (dispatch:Msg->Unit) =
|
||||
let cmeData = model.scratchUser.userData.exchangeUserData.cmeData
|
||||
let simData = model.scratchUser.userData.exchangeUserData.simData
|
||||
div []
|
||||
[
|
||||
h2 [Class "is-size-3"] [ str "CME" ]
|
||||
div []
|
||||
[
|
||||
Fields.text "Operator ID" cmeData.operatorId [] (OperatorID >> UpdateField >> dispatch)
|
||||
Fields.text "Operator ID" cmeData.operatorId [] (CMEOperatorID >> UpdateField >> dispatch)
|
||||
//Fields.text "ATS Operator ID" cmeData.atsOperatorId [] (ATSOperatorID >> UpdateField >> dispatch)
|
||||
Fields.text "SMP ID" cmeData.smpId [] (SMPID >> UpdateField >> dispatch)
|
||||
Fields.mapSelect "SMP Instruction" State.SMPInstructionMap None (Some cmeData.smpInstruction) (SMPInstruction >> UpdateField >> dispatch)
|
||||
Fields.text "Location" cmeData.location [] (Location >> UpdateField >> dispatch)
|
||||
Fields.text "Location" cmeData.location [] (CMELocation >> UpdateField >> dispatch)
|
||||
//Fields.text "Max Order Rate Per Connection" cmeData.maxOrderRatePerConnection [] (MaxOrderRatePerConnection >> UpdateField >> dispatch)
|
||||
//Fields.map "Misc Tag Mapping" cmeData.miscTags [] (MiscTags >> UpdateField >> dispatch)
|
||||
]
|
||||
br []
|
||||
h2 [Class "is-size-3"] [ str "Sim" ]
|
||||
div []
|
||||
[
|
||||
Fields.text "Operator ID" simData.operatorId [] (SimOperatorID >> UpdateField >> dispatch)
|
||||
Fields.text "Location" simData.location [] (SimLocation >> UpdateField >> dispatch)
|
||||
|
||||
]
|
||||
]
|
||||
|
||||
let userControls (model:Model) (dispatch:Msg->Unit) =
|
||||
|
||||
+1
-6
@@ -1,12 +1,7 @@
|
||||
This test verifies that security position limits are enforced correctly. (Position limits are
|
||||
enforced (per account) at the product level and optionally at the security level)
|
||||
|
||||
0. Clear up your orders and fills so we don't have any lingering positions
|
||||
(run rm -r orders and rm -r fills from /sparkdata/data/databases). Restart your sparkd
|
||||
|
||||
1. Pick a future, lets say ESU8 and an account, lets say ACC A1.
|
||||
|
||||
2. In the Spark Admin, select the default account for your user, and enter big clip limits so that
|
||||
enter big clip limits so that
|
||||
clip limits don't interfere with this test.
|
||||
|
||||
3. Go to the Position limits and remove all entries at both the product and security level
|
||||
@@ -15,7 +15,7 @@ e. Disable all Quote, Pre-Hedge, Post-Hedge Parameters for both legs
|
||||
|
||||
2. Make sure there is no market for these contracts. If there is, cancel those orders.
|
||||
|
||||
3. For the first leg (non quoting) enter 1 lot bid orders from 18.230 to 18.250 at 0.05 intervals, and 1 lot ask orders from 18.300 to 18.320.
|
||||
3. For the first leg (non quoting) enter 1 lot bid orders from 18.230 to 18.250 at 0.005 intervals, and 1 lot ask orders from 18.300 to 18.320.
|
||||
Make sure these are reflected in the ladder market data (when the ladder has no market, we will need to enter the first order via an Order Ticket)
|
||||
|
||||
4. For the second leg (quoting leg) enter a 5 lot bid order at 18.5 and a 5 lot sell order at 18.525. Make sure both the markets show up.
|
||||
@@ -1,24 +1,21 @@
|
||||
This test verifies that clip limits are followed, and rule precedence works correctly
|
||||
|
||||
1. Pick a future, lets say SIZ1.
|
||||
Use the SIM exchange for all steps.
|
||||
|
||||
2. In the Spark Admin, go to accounts and select the default account for your user (let say ACC A1).
|
||||
Go to Position Limits and enter large values for the Product Limits and remove all rows for the Security Limits
|
||||
0. Pick a future, let's say CLX8, and veryify you can enter orders for it
|
||||
|
||||
4. Now go to Clip Limits and delete all entries.
|
||||
1. Pick another future, lets say BZQ8.
|
||||
|
||||
2. In the Spark Admin, go to accounts and select the default account for your user (let say ACC A).
|
||||
|
||||
4. Now go to Limits, Clip Limits and delete the BZ future rows in the SIM exchange
|
||||
|
||||
5. Enter an order and it should fail with a message that should tell exactly why. Make sure the orderbook and the
|
||||
ladder show the message.
|
||||
|
||||
6. Now enter a Clip Limit for SIZ1.
|
||||
6. Now enter a Clip Limit for BZ.
|
||||
Enter a Clip Size of 10. Make sure orders > 10 are rejected and < 10 are allowed.
|
||||
|
||||
7. Now enter an order for another future, lets say SIN1. Enter a 1 lot and it should be rejected.
|
||||
7. Delete the BZ spread clip limits row
|
||||
|
||||
8. Enter another rule, GLBX, FUTURES, All, All and enter a clip limit of 100.
|
||||
|
||||
9. Now try the SIN1 order again. Verify it does the right thing for the clip limit you entered in 8.
|
||||
|
||||
10. Verify that SIZ1 is still controlled by a clip size of 10.
|
||||
|
||||
11. Enter a future spread order. It should be rejected since we don't have any clip limits for futures.
|
||||
8. Verify you can no longer enter BZ spread trades, but you can still enter BZ future trades
|
||||
@@ -1,19 +1,21 @@
|
||||
This test verifies that product position limits are enforced correctly. (Position limits are
|
||||
enforced at both the security and account level)
|
||||
|
||||
0. Clear up your orders and fills so we don't have any lingering positions
|
||||
(run rm -r orders and rm -r fills from /sparkdata/data/databases). Restart your sparkd
|
||||
Use SIM for all steps of the test
|
||||
|
||||
0. Clear up your orders, fills and admin db so we don't have any lingering positions
|
||||
(run rm -r orders fills admin from /sparkdata/data/databases). Restart your sparkd
|
||||
|
||||
1. Pick 2 futures, lets say SIU8 and SIZ8
|
||||
|
||||
2. In the Spark Admin, select the default account for your user (lets say ACC A1)
|
||||
and enter big clip limits so that clip limits don't interfere with this test.
|
||||
2. In the Spark Admin, select the default account for your user (lets say ACC A)
|
||||
and enter a big clip, say 500000, so that clip limits don't interfere with this test.
|
||||
|
||||
3. Go to the Position limits and remove all entries at both the product and security level
|
||||
|
||||
4. Enter an SIU8 order. It should be rejected. Make sure the rejection message makes sense.
|
||||
|
||||
5. Enter a product level rule for GLBX, Future, SI, 60, 120.
|
||||
5. Enter a product level rule for SIM, Future, SI, 60, 120.
|
||||
|
||||
6. Send 3 10 lot buys for both the futures. Make sure they go through.
|
||||
|
||||
|
||||
@@ -18,19 +18,3 @@ fills). Cancel all the orders.
|
||||
4. Verify that the limits set in 2. work for spreads. Cancel all the orders.
|
||||
|
||||
5. Make these product limits 1,000,000 to make them irrelevant
|
||||
|
||||
ii:
|
||||
|
||||
6. For security limits enter long 10 and short 20 for SIU8 and SIZ8 futures respectively and 50 and 60 for
|
||||
the SIU8-SIZ8 spread.
|
||||
|
||||
7. Enter a 5 lot buy for SIU8-SIZ8. This 5 should count toward SIU8 long limit (which is 10). Enter
|
||||
a 5 lot SIU8 buy, which should be allowed but a subsequent 1 lot buy should be rejected. Similiary, the
|
||||
spreader order should count toward the SIZ8 short limit of 20. Enter a 15 lot sell on SIZ8, and a subsequent
|
||||
1 lot should be rejected.
|
||||
|
||||
8. Cancel all orders
|
||||
|
||||
iii:
|
||||
|
||||
Enter a 11 lot spread order. It should be rejected because of the 10 lot limit on SIU8.
|
||||
@@ -13,7 +13,7 @@ makes sense.
|
||||
ladder dropdown and uncheck "Is Active").
|
||||
|
||||
6. Verify that orders going into that account are rejected and that the rejection message
|
||||
is clear.
|
||||
is clear .
|
||||
|
||||
7. Verify that the account's "Is Active" is correctly applying to that account only, i.e. you
|
||||
can still enter orders into other accounts
|
||||
@@ -13,4 +13,8 @@ thresh then cancel the order else do nothing
|
||||
- Verify that orders cancel if they end up working at an inside market with less than threshold size
|
||||
|
||||
Goto Label l and test a sell
|
||||
|
||||
|
||||
Now do this test again with "Enable Hedge" checked and hedge ticks set to 1, hedge type set to limit
|
||||
|
||||
This should modify the behavior such that the algorithm will send a limit order on the opposite
|
||||
side of the market, one tick away from fill price in a quantity equivalent to the amount of the fill
|
||||
@@ -1,7 +1,7 @@
|
||||
0. This needs sim market data **not** to be merged with replay (or cert), so we can create our own markets.
|
||||
|
||||
1. Bring up a ladder, lets say in SIN1 (does not matter which security)
|
||||
2. Enter a 5 lot limit order at 20.00 (if there are no markets use the M View and the ticket to enter the order)
|
||||
2. Enter a 5 lot limit order at 20.00 (buy) (if there are no markets use the M View and the ticket to enter the order)
|
||||
3. In the dropdown select Trigger. For the trigger details, enter the following :
|
||||
a. Trigger Price Type : Bid
|
||||
b. Order Price : 19.975
|
||||
|
||||
@@ -5,5 +5,3 @@ Go to Help -> About
|
||||
|
||||
Marvel at our About Box
|
||||
|
||||
Ask Michael to insert a link to our website
|
||||
|
||||
@@ -13,9 +13,9 @@ JetFIX {
|
||||
connection {
|
||||
# Exchange simulator connection
|
||||
name: "sim"
|
||||
platform: GLBX
|
||||
socket_connect_host_ip: "127.0.0.1"
|
||||
socket_connect_port: @Ports.exchange_sim_fix_port
|
||||
platform: SIM
|
||||
socket_connect_host_ip: "127.0.0.1"
|
||||
socket_connect_port: @Ports.exchange_sim_fix_port
|
||||
sender_compid: "SPARK" # this is the FIX session name
|
||||
sender_subid: "BTS"
|
||||
target_compid: "BTS"
|
||||
@@ -24,11 +24,11 @@ JetFIX {
|
||||
start_time: "07:52:00"
|
||||
raw_data: "1JE9X"
|
||||
use_cme_secure_logon: false
|
||||
seconds_to_pause : 1
|
||||
seconds_to_pause : 1
|
||||
|
||||
# max of 3 order operations (creates or updates only) for each 5 second
|
||||
max_order_msgs_per_window: 3
|
||||
window_duration_msecs: 5000
|
||||
max_order_msgs_per_window: 3
|
||||
window_duration_msecs: 5000
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ Type some stuff in the description box and a fake phone number, check include sc
|
||||
Click send.
|
||||
|
||||
Verify that a folder was created on the server under ~/sparkdata/data/error_reports/ with the information you typed in.
|
||||
If you left the screenshot checkmark checked copy the screenshot.jpeg to ~/www/ and look at it. Make sure it is your desktop not mine.
|
||||
If you left the screenshot checkmark checked copy the screenshot.jpeg to ~/sparkdata/www/ and view it from http://sparkvm:8000/files/
|
||||
|
||||
|
||||
@@ -11,3 +11,28 @@ Test modification: Change price, commit, change size, commit, change price and s
|
||||
Test fill: Cross an order, make sure fill is accurately reflected
|
||||
If you tested cancellation and modification through the side-bar, now do the same through the context menu
|
||||
Test sorting
|
||||
|
||||
|
||||
Recreate Testing
|
||||
----------------
|
||||
After the above, you should have a variety of order statuses in your order book.
|
||||
|
||||
Right click on a Completed order
|
||||
The Recreate menu item should remain disabled
|
||||
Left click and drag over over a series of orders, at least one of them being in Canceled status
|
||||
The Recreate menu item should now be available
|
||||
Click Recreate from the tool well
|
||||
Orders should be submitted for all top-level orders (no children of any kind) that have a status of cancelled.
|
||||
The Order should be placed at the same price as the original order
|
||||
The order should be placed with a Qty of (Qty - Filled Qty)
|
||||
Left click on a cancelled child order of a parent-child order type
|
||||
The Recreate menu item should remain disabled
|
||||
Left click on a cancelled parent order of a parent-child order type
|
||||
Click Recreate from the tool well
|
||||
The original order heirarchy will again be working
|
||||
Orders should be placed at the same price as their respective original order
|
||||
The top parent order should be placed with a Qty of (Qty - Filled Qty)
|
||||
* Note, child orders will *NOT* be recreated with a Qty of (Qty - Filled Qty) at this time since their intermediate fill status is unknown to their parent.
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ order for SIN9 since the SIF9 hedge leg still doesnt have a market. Enter a 1 lo
|
||||
Re-enter the SIK9 order, 10 lots at 20.200 and the quoting order should appear as well.
|
||||
|
||||
5. Now lift the quoting leg ask with a 1 lot buy order at the same price. Make sure hedges are sent (and filled)
|
||||
for all the 3 hedging legs. SIH9 should have no market now and hence we should have no quoting order.
|
||||
for all the 3 hedging legs. SIF9 should have no market now and hence we should have no quoting order.
|
||||
|
||||
6. Enter a 10 lot sell order at 20.000 for SIF9. This should result in a 9 lot quoting order.
|
||||
|
||||
|
||||
Reference in new issue
Block a user