Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48f56e76d4 | ||
|
|
05e6c40a57 |
No files matched your search
@@ -39,7 +39,6 @@
|
||||
|
||||
<Message Importance="High" Text="Generating service %(Proto3.FileName)" />
|
||||
<Exec Command="
|
||||
set PATH=$(spark_build_python_path);%path% &
|
||||
set PYTHONPATH=$(Externals)\python &
|
||||
$(protoc) ^
|
||||
%(Proto3.FullPath) ^
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<DataGridTextColumn x:Key="{x:Static usp:ExecutionStrategyEditor.AddColumnPlaceholderKey}" x:Shared="False" IsReadOnly="True">
|
||||
<DataGridTextColumn x:Key="AddColumnPlaceholder" x:Shared="False" IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<c:PopupControl
|
||||
ShowExpanderArrow="False" PopupPlacement="Right" Header="Add Leg" Padding="6,0"
|
||||
@@ -308,7 +308,7 @@
|
||||
</c:PopupControl>
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.RemovableColumnHeaderTemplateKey}">
|
||||
<DataTemplate x:Key="RemovableColumnHeaderTemplate">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -321,9 +321,7 @@
|
||||
<TextBlock Grid.Column="1" Margin="4,0,0,0" Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.NonRemovableColumnHeaderTemplateKey}">
|
||||
<TextBlock Text="{Binding Converter={StaticResource SecurityTextConverter}}"/>
|
||||
</DataTemplate>
|
||||
|
||||
<ControlTemplate x:Key="ExecutionStrategyEditorTemplate" TargetType="usp:ExecutionStrategyEditor">
|
||||
<Border
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
|
||||
@@ -25,11 +25,7 @@ namespace mbts.ui.spark {
|
||||
/// <summary>
|
||||
/// Interaction logic for ExecutionStrategyEditor.xaml
|
||||
/// </summary>
|
||||
partial class ExecutionStrategyEditor : Control {
|
||||
public const string
|
||||
RemovableColumnHeaderTemplateKey = "RemovableColumnHeaderTemplate",
|
||||
NonRemovableColumnHeaderTemplateKey = "ReadOnlyColumnHeaderTemplate",
|
||||
AddColumnPlaceholderKey = "AddColumnPlaceholder";
|
||||
partial class ExecutionStrategyEditor : Control {
|
||||
static ExecutionStrategyEditor() =>
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExecutionStrategyEditor), new FrameworkPropertyMetadata(typeof(ExecutionStrategyEditor)));
|
||||
|
||||
@@ -200,7 +196,7 @@ namespace mbts.ui.spark {
|
||||
if (dg != null) {
|
||||
var legs = LegTuples;
|
||||
dg.Columns.SetItems(legs.Select(_column_factory));
|
||||
if (IsNew) dg.Columns.Add((DataGridColumn)FindResource(AddColumnPlaceholderKey));
|
||||
if (IsNew) dg.Columns.Add((DataGridColumn)FindResource("AddColumnPlaceholder"));
|
||||
_dg_line_adorner = AdornerHelper.attach_continuously_on_loaded(() => new ExecutionStrategyGridLineAdorner(dg), dg);
|
||||
_legs_collection_changed_sub = (legs as INotifyCollectionChanged)?.Subscribe((s, e) => dg.Columns.ReflectChange(legs, _column_factory, e));
|
||||
}
|
||||
@@ -208,7 +204,7 @@ namespace mbts.ui.spark {
|
||||
|
||||
DataGridColumn _column_factory(LegSpreaderParamsTuple l) {
|
||||
var col = new DataGridColumnInternal(l.leg.Security, ParameterCellTemplateConverter, ParameterCellEditingTemplateConverter) { Width = 75, };
|
||||
col.HeaderTemplate = (DataTemplate)FindResource(IsNew ? RemovableColumnHeaderTemplateKey : NonRemovableColumnHeaderTemplateKey);
|
||||
if (IsNew) col.HeaderTemplate = (DataTemplate)FindResource("RemovableColumnHeaderTemplate");
|
||||
DataGridEx.SetRightBorder(col, new DataGridExVerticalLine(false, true, Colors.DarkGray, 1));
|
||||
return col;
|
||||
}
|
||||
@@ -244,7 +240,7 @@ namespace mbts.ui.spark {
|
||||
FrameworkElement _generate_element(IValueConverter cell_template_converter) {
|
||||
var rv = new ContentControl();
|
||||
rv.SetBinding(ContentControl.ContentProperty, new Binding());
|
||||
rv.SetBinding(ContentControl.ContentTemplateProperty, new Binding(nameof(ICell.data_type)) { Converter = cell_template_converter });
|
||||
rv.SetBinding(ContentControl.ContentTemplateProperty, new Binding("data_type") { Converter = cell_template_converter });
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,6 @@ namespace mbts.ui.spark {
|
||||
public DataTemplate StopLimitOrderTemplate { get; set; }
|
||||
public DataTemplate StopMarketOrderTemplate { get; set; }
|
||||
public DataTemplate TimeBoxOrderTemplate { get; set; }
|
||||
public DataTemplate TickOrderTemplate { get; set; }
|
||||
public DataTemplate LeanOrderTemplate { get; set; }
|
||||
public DataTemplate JoinOrderTemplate { get; set; }
|
||||
public DataTemplate TopOrderTemplate { get; set; }
|
||||
public DataTemplate IcebergOrderTemplate { get; set; }
|
||||
public DataTemplate SpreaderTemplate { get; set; }
|
||||
public DataTemplate BTSStopOrderTemplate { get; set; }
|
||||
@@ -37,10 +33,6 @@ namespace mbts.ui.spark {
|
||||
case CustomOrderDefinition.CustomOneofCase.Stop: return StopMarketOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.StopLimit: return StopLimitOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.TimeBox: return TimeBoxOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Tick: return TickOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Lean: return LeanOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: return JoinOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: return TopOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Iceberg: return IcebergOrderTemplate;
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: return SpreaderTemplate;
|
||||
}
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineJoinOrderProperties
|
||||
{
|
||||
public InlineJoinOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
|
||||
var hedgetype = CustomOneofCase.Lean;
|
||||
if(!child_order_types.Contains(hedgetype))
|
||||
hedgetype = CustomOneofCase.Limit;
|
||||
|
||||
//ExceptionAssert.InvalidOperation.That(child_order_types.Contains(hedgetype), $"Hedge order type {hedgetype} is not in hedge_order_types");
|
||||
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
|
||||
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
|
||||
join_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
|
||||
enable_hedge = new InlineUserProperty<bool>(false, false);
|
||||
hedge_ticks = new InlineUserProperty<int>(1, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
|
||||
|
||||
|
||||
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
|
||||
public readonly InlineUserProperty<bts.spark.QuantityMeasure> join_threshold;
|
||||
|
||||
public readonly InlineUserProperty<bool> enable_hedge;
|
||||
public readonly InlineUserProperty<int> hedge_ticks;
|
||||
|
||||
}
|
||||
|
||||
class JoinOrderVM : OrderTypeVMBase<JoinOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineJoinOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
|
||||
IOrderTypeVM _hedge_order;
|
||||
IDisposable _hedge_order_errors_sub;
|
||||
|
||||
public uint tick_offset_on_deploy { get; set; } = 0;
|
||||
public bts.spark.QuantityMeasure join_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
|
||||
bool _enable_hedge = false;
|
||||
public bool enable_hedge
|
||||
{
|
||||
get { return _enable_hedge; }
|
||||
set
|
||||
{
|
||||
_enable_hedge = value;
|
||||
RaisePropertyChanged(nameof(enable_hedge));
|
||||
}
|
||||
}
|
||||
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<SecurityImmutable> security_source,
|
||||
IObservable<InlineJoinOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.ObserveOnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
inline_properties.ObserveOnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
{
|
||||
_order_types.SetItems(p.child_order_types.value);
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
|
||||
{
|
||||
_select_hedge_order(p.selected_hedge_order_type.value);
|
||||
}
|
||||
if (p.tick_offset_on_deploy.force_override)
|
||||
{
|
||||
tick_offset_on_deploy = p.tick_offset_on_deploy.value;
|
||||
RaisePropertyChanged(nameof(tick_offset_on_deploy));
|
||||
}
|
||||
if (p.join_threshold.force_override)
|
||||
{
|
||||
join_threshold = p.join_threshold.value;
|
||||
RaisePropertyChanged(nameof(join_threshold));
|
||||
}
|
||||
if (p.enable_hedge.force_override)
|
||||
{
|
||||
enable_hedge = p.enable_hedge.value;
|
||||
RaisePropertyChanged(nameof(enable_hedge));
|
||||
}
|
||||
if (p.hedge_ticks.force_override)
|
||||
{
|
||||
hedge_ticks = p.hedge_ticks.value;
|
||||
RaisePropertyChanged(nameof(hedge_ticks));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
);
|
||||
_validate();
|
||||
}
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value)
|
||||
{
|
||||
if (_child_order == null)
|
||||
_select_child_order(model.factory.default_order_type(_security.Value, true));
|
||||
if (_hedge_order == null && enable_hedge)
|
||||
_select_hedge_order(model.factory.default_order_type(_security.Value, true));
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.join_order();
|
||||
i.TickOffsetOnDeploy = tick_offset_on_deploy;
|
||||
i.JoinThreshold = join_threshold;
|
||||
i.EnableHedge = enable_hedge;
|
||||
i.HedgeTicks = hedge_ticks;
|
||||
child_order.assign_to(i.Child);
|
||||
if(enable_hedge && hedge_order != null)
|
||||
hedge_order.assign_to(i.Hedge);
|
||||
value.Join = i;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<IOrderTypeVM> hedge_order_types
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _child_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _child_order_errors_sub);
|
||||
_child_order = value;
|
||||
_child_order?.init();
|
||||
if (_child_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM hedge_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _hedge_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _hedge_order_errors_sub);
|
||||
_hedge_order = value;
|
||||
_hedge_order?.init();
|
||||
if (_hedge_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_hedge_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(hedge_order));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposable.dispose(ref _dispose);
|
||||
if (_child_order_choices != null)
|
||||
_child_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
if (_hedge_order_choices != null)
|
||||
_hedge_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
|
||||
public override void init()
|
||||
{
|
||||
if (_child_order == null && _security.HasValue)
|
||||
_select_child_order();
|
||||
if (_hedge_order == null && _security.HasValue && enable_hedge)
|
||||
_select_hedge_order();
|
||||
_validate();
|
||||
}
|
||||
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
void _select_child_order(CustomOneofCase ot)
|
||||
{
|
||||
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_child_order == null)
|
||||
_child_order = child_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
|
||||
void _select_hedge_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
|
||||
_inline_props.selected_hedge_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_hedge_order(ot);
|
||||
}
|
||||
|
||||
void _select_hedge_order(CustomOneofCase ot)
|
||||
{
|
||||
_hedge_order = hedge_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_hedge_order == null)
|
||||
_hedge_order = hedge_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(hedge_order));
|
||||
}
|
||||
|
||||
|
||||
void _validate()
|
||||
{
|
||||
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
|
||||
set_error(nameof(child_order), string.Empty, true);
|
||||
else clear_error(nameof(child_order));
|
||||
|
||||
if ( (_hedge_order == null && enable_hedge) || (_hedge_order is INotifyDataErrorInfo hdei && hdei.HasErrors))
|
||||
set_error(nameof(hedge_order), string.Empty, true);
|
||||
else clear_error(nameof(hedge_order));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineLeanOrderProperties
|
||||
{
|
||||
public InlineLeanOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(10, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
|
||||
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
|
||||
public readonly InlineUserProperty<uint> max_chase_ticks;
|
||||
}
|
||||
|
||||
class LeanOrderVM : OrderTypeVMBase<LeanOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineLeanOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
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<SecurityImmutable> security_source,
|
||||
IObservable<InlineLeanOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.ObserveOnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
inline_properties.ObserveOnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
{
|
||||
_order_types.SetItems(p.child_order_types.value);
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (p.cross_threshold.force_override)
|
||||
{
|
||||
cross_threshold = p.cross_threshold.value;
|
||||
RaisePropertyChanged(nameof(cross_threshold));
|
||||
}
|
||||
if (p.max_chase_ticks.force_override)
|
||||
{
|
||||
max_chase_ticks = p.max_chase_ticks.value;
|
||||
RaisePropertyChanged(nameof(max_chase_ticks));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
);
|
||||
_validate();
|
||||
}
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value)
|
||||
{
|
||||
if (_child_order == null)
|
||||
_select_child_order(model.factory.default_order_type(_security.Value, true));
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.lean_order();
|
||||
i.CrossThreshold = cross_threshold;
|
||||
i.MaxChaseTicks = max_chase_ticks;
|
||||
child_order.assign_to(i.Child);
|
||||
value.Lean = i;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _child_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _child_order_errors_sub);
|
||||
_child_order = value;
|
||||
_child_order?.init();
|
||||
if (_child_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposable.dispose(ref _dispose);
|
||||
if (_child_order_choices != null)
|
||||
_child_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
|
||||
public override void init()
|
||||
{
|
||||
if (_child_order == null && _security.HasValue)
|
||||
_select_child_order();
|
||||
_validate();
|
||||
}
|
||||
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
void _select_child_order(CustomOneofCase ot)
|
||||
{
|
||||
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_child_order == null)
|
||||
_child_order = child_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
|
||||
void _validate()
|
||||
{
|
||||
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
|
||||
set_error(nameof(child_order), string.Empty, true);
|
||||
else clear_error(nameof(child_order));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,6 @@ namespace mbts.spark {
|
||||
public static IOrderTypeVM stop(OrdersModel model) => new StopOrderVM(model);
|
||||
public static IOrderTypeVM stop_limit(OrdersModel model) => new StopLimitOrderVM(model);
|
||||
public static IOrderTypeVM time_box(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TimeBoxOrderVM(model, security_source);
|
||||
public static IOrderTypeVM tick(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TickOrderVM(model, security_source);
|
||||
public static IOrderTypeVM lean(OrdersModel model, IObservable<SecurityImmutable> security_source) => new LeanOrderVM(model, security_source);
|
||||
public static IOrderTypeVM join(OrdersModel model, IObservable<SecurityImmutable> security_source) => new JoinOrderVM(model, security_source);
|
||||
public static IOrderTypeVM top(OrdersModel model, IObservable<SecurityImmutable> security_source) => new TopOrderVM(model, security_source);
|
||||
public static IOrderTypeVM iceberg(OrdersModel model, IObservable<SecurityImmutable> security_source) => new IcebergOrderVM(model, security_source);
|
||||
public static IOrderTypeVM spreader(OrdersModel model, IObservable<SecurityImmutable> security_source) => new SpreaderOrderVM(model, security_source);
|
||||
|
||||
@@ -31,10 +27,6 @@ namespace mbts.spark {
|
||||
case CustomOneofCase.Stop: return stop(model);
|
||||
case CustomOneofCase.StopLimit: return stop_limit(model);
|
||||
case CustomOneofCase.TimeBox: return time_box(model, security_source);
|
||||
case CustomOneofCase.Tick: return tick(model, security_source);
|
||||
case CustomOneofCase.Lean: return lean(model, security_source);
|
||||
case CustomOneofCase.Join: return join(model, security_source);
|
||||
case CustomOneofCase.Top: return top(model, security_source);
|
||||
case CustomOneofCase.Iceberg: return iceberg(model, security_source);
|
||||
case CustomOneofCase.Spreader: return spreader(model, security_source);
|
||||
default: throw new NotImplementedException();
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineTickOrderProperties
|
||||
{
|
||||
public InlineTickOrderProperties (CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
tick_offset_on_deploy = new InlineUserProperty<uint>(0, false);
|
||||
cross_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1}, false);
|
||||
cross_ticks = new InlineUserProperty<uint>(0, false);
|
||||
max_chase_ticks = new InlineUserProperty<uint>(10, false);
|
||||
detach_on_toward = new InlineUserProperty<bool>(true, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
|
||||
public readonly InlineUserProperty<uint> tick_offset_on_deploy;
|
||||
public readonly InlineUserProperty<bts.spark.QuantityMeasure> cross_threshold;
|
||||
public readonly InlineUserProperty<uint> cross_ticks;
|
||||
public readonly InlineUserProperty<uint> max_chase_ticks;
|
||||
public readonly InlineUserProperty<bool> detach_on_toward;
|
||||
}
|
||||
|
||||
class TickOrderVM : OrderTypeVMBase<TickOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineTickOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
public uint tick_offset_on_deploy { get; set; } = 0;
|
||||
public bts.spark.QuantityMeasure cross_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
|
||||
public uint cross_ticks { get; set; } = 0;
|
||||
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<SecurityImmutable> security_source,
|
||||
IObservable<InlineTickOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.ObserveOnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
inline_properties.ObserveOnDispatcher()
|
||||
.Subscribe(p => {
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
{
|
||||
_order_types.SetItems(p.child_order_types.value);
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (p.tick_offset_on_deploy.force_override)
|
||||
{
|
||||
tick_offset_on_deploy = p.tick_offset_on_deploy.value;
|
||||
RaisePropertyChanged(nameof(tick_offset_on_deploy));
|
||||
}
|
||||
if (p.cross_threshold.force_override)
|
||||
{
|
||||
cross_threshold = p.cross_threshold.value;
|
||||
RaisePropertyChanged(nameof(cross_threshold));
|
||||
}
|
||||
if (p.cross_ticks.force_override)
|
||||
{
|
||||
cross_ticks = p.cross_ticks.value;
|
||||
RaisePropertyChanged(nameof(cross_ticks));
|
||||
}
|
||||
if (p.max_chase_ticks.force_override)
|
||||
{
|
||||
max_chase_ticks = p.max_chase_ticks.value;
|
||||
RaisePropertyChanged(nameof(max_chase_ticks));
|
||||
}
|
||||
if (p.detach_on_toward.force_override)
|
||||
{
|
||||
detach_on_toward = p.detach_on_toward.value;
|
||||
RaisePropertyChanged(nameof(detach_on_toward));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
);
|
||||
_validate();
|
||||
}
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value)
|
||||
{
|
||||
if (_child_order == null)
|
||||
_select_child_order(model.factory.default_order_type(_security.Value, true));
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.tick_order();
|
||||
i.TickOffsetOnDeploy = tick_offset_on_deploy;
|
||||
i.CrossThreshold = cross_threshold;
|
||||
i.CrossTicks = cross_ticks;
|
||||
i.MaxChaseTicks = max_chase_ticks;
|
||||
i.DetachOnToward = detach_on_toward;
|
||||
child_order.assign_to(i.Child);
|
||||
value.Tick = i;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _child_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _child_order_errors_sub);
|
||||
_child_order = value;
|
||||
_child_order?.init();
|
||||
if (_child_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposable.dispose(ref _dispose);
|
||||
if (_child_order_choices != null)
|
||||
_child_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
|
||||
public override void init()
|
||||
{
|
||||
if (_child_order == null && _security.HasValue)
|
||||
_select_child_order();
|
||||
_validate();
|
||||
}
|
||||
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
void _select_child_order(CustomOneofCase ot)
|
||||
{
|
||||
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_child_order == null)
|
||||
_child_order = child_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
|
||||
void _validate()
|
||||
{
|
||||
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
|
||||
set_error(nameof(child_order), string.Empty, true);
|
||||
else clear_error(nameof(child_order));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using static bts.api.orderman.CustomOrderDefinition;
|
||||
|
||||
namespace mbts.spark
|
||||
{
|
||||
static partial class OrderTypeVMFactory
|
||||
{
|
||||
class InlineTopOrderProperties
|
||||
{
|
||||
public InlineTopOrderProperties(CustomOneofCase selected_child_order_type, IReadOnlyCollection<CustomOneofCase> child_order_types)
|
||||
{
|
||||
ExceptionAssert.InvalidOperation.That(child_order_types.Contains(selected_child_order_type), $"Child order type {selected_child_order_type} is not in child_order_types");
|
||||
this.selected_child_order_type = new InlineUserProperty<CustomOneofCase>(selected_child_order_type, true);
|
||||
this.child_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
|
||||
var hedgetype = CustomOneofCase.Lean;
|
||||
if(!child_order_types.Contains(hedgetype))
|
||||
hedgetype = CustomOneofCase.Limit;
|
||||
this.selected_hedge_order_type = new InlineUserProperty<CustomOneofCase>(hedgetype, true);
|
||||
this.hedge_order_types = new InlineUserProperty<IReadOnlyCollection<CustomOneofCase>>(child_order_types, true);
|
||||
|
||||
watch_level_tick_offset = new InlineUserProperty<uint>(0, false);
|
||||
top_threshold = new InlineUserProperty<bts.spark.QuantityMeasure>(
|
||||
new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 }, false);
|
||||
enable_hedge = new InlineUserProperty<bool>(false, false);
|
||||
hedge_ticks = new InlineUserProperty<int>(1, false);
|
||||
}
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_child_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> child_order_types;
|
||||
public readonly InlineUserProperty<CustomOneofCase> selected_hedge_order_type;
|
||||
public readonly InlineUserProperty<IReadOnlyCollection<CustomOneofCase>> hedge_order_types;
|
||||
|
||||
|
||||
public readonly InlineUserProperty<uint> watch_level_tick_offset;
|
||||
public readonly InlineUserProperty<bts.spark.QuantityMeasure> top_threshold;
|
||||
|
||||
public readonly InlineUserProperty<bool> enable_hedge;
|
||||
public readonly InlineUserProperty<int> hedge_ticks;
|
||||
|
||||
}
|
||||
|
||||
class TopOrderVM : OrderTypeVMBase<TopOrderDefinition>, IHierarchicalOrderTypeVM, IDisposable
|
||||
{
|
||||
readonly IObservable<SecurityImmutable> _security_source;
|
||||
SecurityImmutable? _security;
|
||||
ObservableCollection<CustomOneofCase> _order_types = new ObservableCollection<CustomOneofCase>();
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _child_order_choices;
|
||||
InlineTopOrderProperties _inline_props;
|
||||
IOrderTypeVM _child_order;
|
||||
IDisposable _dispose, _child_order_errors_sub;
|
||||
|
||||
ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase> _hedge_order_choices;
|
||||
IOrderTypeVM _hedge_order;
|
||||
IDisposable _hedge_order_errors_sub;
|
||||
|
||||
public uint watch_level_tick_offset { get; set; } = 0;
|
||||
public bts.spark.QuantityMeasure top_threshold { get; set; } = new bts.spark.QuantityMeasure() { Ratio = 1.0, Absolute = 1 };
|
||||
bool _enable_hedge = false;
|
||||
public bool enable_hedge
|
||||
{
|
||||
get { return _enable_hedge; }
|
||||
set
|
||||
{
|
||||
_enable_hedge = value;
|
||||
RaisePropertyChanged(nameof(enable_hedge));
|
||||
}
|
||||
}
|
||||
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<SecurityImmutable> security_source,
|
||||
IObservable<InlineTopOrderProperties> inline_properties) : base(model)
|
||||
{
|
||||
_security_source = security_source;
|
||||
_dispose = Disposable.Create(
|
||||
security_source.ObserveOnDispatcher()
|
||||
.Subscribe(s => _security = s),
|
||||
inline_properties.ObserveOnDispatcher()
|
||||
.Subscribe(p =>
|
||||
{
|
||||
if (_order_types.Count == 0 || p.child_order_types.force_override)
|
||||
{
|
||||
_order_types.SetItems(p.child_order_types.value);
|
||||
}
|
||||
if (_child_order_choices != null && (_child_order == null || p.selected_child_order_type.force_override))
|
||||
{
|
||||
_select_child_order(p.selected_child_order_type.value);
|
||||
}
|
||||
if (_hedge_order_choices != null && (_hedge_order == null || p.selected_hedge_order_type.force_override))
|
||||
{
|
||||
_select_hedge_order(p.selected_hedge_order_type.value);
|
||||
}
|
||||
if (p.watch_level_tick_offset.force_override)
|
||||
{
|
||||
watch_level_tick_offset = p.watch_level_tick_offset.value;
|
||||
RaisePropertyChanged(nameof(watch_level_tick_offset));
|
||||
}
|
||||
if (p.top_threshold.force_override)
|
||||
{
|
||||
top_threshold = p.top_threshold.value;
|
||||
RaisePropertyChanged(nameof(top_threshold));
|
||||
}
|
||||
if (p.enable_hedge.force_override)
|
||||
{
|
||||
enable_hedge = p.enable_hedge.value;
|
||||
RaisePropertyChanged(nameof(enable_hedge));
|
||||
}
|
||||
if (p.hedge_ticks.force_override)
|
||||
{
|
||||
hedge_ticks = p.hedge_ticks.value;
|
||||
RaisePropertyChanged(nameof(hedge_ticks));
|
||||
}
|
||||
_inline_props = p;
|
||||
_validate();
|
||||
})
|
||||
);
|
||||
_validate();
|
||||
}
|
||||
|
||||
public override void assign_to(CustomOrderDefinition value)
|
||||
{
|
||||
if (_child_order == null)
|
||||
_select_child_order(model.factory.default_order_type(_security.Value, true));
|
||||
if (_hedge_order == null && enable_hedge)
|
||||
_select_hedge_order(model.factory.default_order_type(_security.Value, true));
|
||||
_validate();
|
||||
if (!HasErrors)
|
||||
{
|
||||
var i = factory.top_order();
|
||||
i.WatchLevelTickOffset = watch_level_tick_offset;
|
||||
i.TopThreshold = top_threshold;
|
||||
i.EnableHedge = enable_hedge;
|
||||
i.HedgeTicks = hedge_ticks;
|
||||
child_order.assign_to(i.Child);
|
||||
if (enable_hedge && hedge_order != null)
|
||||
hedge_order.assign_to(i.Hedge);
|
||||
value.Top = i;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return _child_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<IOrderTypeVM> hedge_order_types
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_hedge_order_choices == null)
|
||||
_hedge_order_choices = new ObservableAutoWrapper<IOrderTypeVM, CustomOneofCase>(_order_types, ot => create(ot, model, _security_source));
|
||||
return _hedge_order_choices;
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM child_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _child_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _child_order_errors_sub);
|
||||
_child_order = value;
|
||||
_child_order?.init();
|
||||
if (_child_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_child_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
}
|
||||
|
||||
public IOrderTypeVM hedge_order
|
||||
{
|
||||
get
|
||||
{
|
||||
init();
|
||||
return _hedge_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
Disposable.dispose(ref _hedge_order_errors_sub);
|
||||
_hedge_order = value;
|
||||
_hedge_order?.init();
|
||||
if (_hedge_order is INotifyDataErrorInfo ndei)
|
||||
{
|
||||
ndei.ErrorsChanged += on_errors_changed;
|
||||
void on_errors_changed(object s, DataErrorsChangedEventArgs e) =>
|
||||
_validate();
|
||||
_hedge_order_errors_sub = Disposable.Create(() => ndei.ErrorsChanged -= on_errors_changed);
|
||||
}
|
||||
_validate();
|
||||
RaisePropertyChanged(nameof(hedge_order));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposable.dispose(ref _dispose);
|
||||
if (_child_order_choices != null)
|
||||
_child_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
if (_hedge_order_choices != null)
|
||||
_hedge_order_choices.OfType<IDisposable>().DisposeAll();
|
||||
}
|
||||
|
||||
public override void init()
|
||||
{
|
||||
if (_child_order == null && _security.HasValue)
|
||||
_select_child_order();
|
||||
if (_hedge_order == null && _security.HasValue && enable_hedge)
|
||||
_select_hedge_order();
|
||||
_validate();
|
||||
}
|
||||
|
||||
void _select_child_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_child_order_type.has_value ?
|
||||
_inline_props.selected_child_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_child_order(ot);
|
||||
}
|
||||
|
||||
void _select_child_order(CustomOneofCase ot)
|
||||
{
|
||||
_child_order = child_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_child_order == null)
|
||||
_child_order = child_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(child_order));
|
||||
}
|
||||
|
||||
void _select_hedge_order()
|
||||
{
|
||||
CustomOneofCase ot = _inline_props != null && _inline_props.selected_hedge_order_type.has_value ?
|
||||
_inline_props.selected_hedge_order_type.value :
|
||||
model.factory.default_order_type(_security.Value, true);
|
||||
_select_hedge_order(ot);
|
||||
}
|
||||
|
||||
void _select_hedge_order(CustomOneofCase ot)
|
||||
{
|
||||
_hedge_order = hedge_order_types.FirstOrDefault(vm => vm.order_type == ot);
|
||||
if (_hedge_order == null)
|
||||
_hedge_order = hedge_order_types.FirstOrDefault();
|
||||
RaisePropertyChanged(nameof(hedge_order));
|
||||
}
|
||||
|
||||
|
||||
void _validate()
|
||||
{
|
||||
if (_child_order == null || (_child_order is INotifyDataErrorInfo ndei && ndei.HasErrors))
|
||||
set_error(nameof(child_order), string.Empty, true);
|
||||
else clear_error(nameof(child_order));
|
||||
|
||||
if ((_hedge_order == null && enable_hedge) || (_hedge_order is INotifyDataErrorInfo hdei && hdei.HasErrors))
|
||||
set_error(nameof(hedge_order), string.Empty, true);
|
||||
else clear_error(nameof(hedge_order));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,10 +199,10 @@ namespace mbts.ui {
|
||||
public static BTSColor straddle_theovsmarket_positive { get { return BTSCoreColors.green_light; } }
|
||||
|
||||
// Order Types
|
||||
public static BTSColor tick_order { get { return BTSColors.purple.rgb_color();}}
|
||||
public static BTSColor lean_order { get { return BTSColors.purple.rgb_color();}}
|
||||
public static BTSColor join_order { get { return BTSColors.purple.rgb_color();}}
|
||||
public static BTSColor top_order { get { return BTSColors.purple.rgb_color();}}
|
||||
public static BTSColor tick_order { get { return new BTSColor(31, 4, 57);}}
|
||||
public static BTSColor lean_order { get { return new BTSColor(110, 77, 143);}}
|
||||
public static BTSColor join_order { get { return new BTSColor(15, 1, 29);}}
|
||||
public static BTSColor leader_order { get { return new BTSColor(40, 9, 72);}}
|
||||
}
|
||||
|
||||
public static class AW2 {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding Mode, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
/>
|
||||
<Grid x:Name="PeriodRoot" Grid.Row="1" Margin="24,6,0,0" IsEnabled="False">
|
||||
<Grid x:Name="PeriodRoot" Grid.Row="1" Margin="0,6,0,0" IsEnabled="False">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="4"/>
|
||||
|
||||
@@ -73,9 +73,8 @@ namespace mbts.ui.controls {
|
||||
public enum TimeFilterMode {
|
||||
Anytime,
|
||||
Today,
|
||||
[Description("Period:")]
|
||||
Period,
|
||||
[Description("Since Open")]
|
||||
[Description("Glbx Open")]
|
||||
CMEOpen
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
<ResourceDictionary Source="/view/Controls/PriceEntryControl.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Popup/PopupControl.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Time/TimeSpanControl.xaml"/>
|
||||
<ResourceDictionary Source="/view/Controls/Order/QuantityMeasureControl.xaml"/>
|
||||
<ResourceDictionary Source="/Modules/Spreader/ExecutionStrategyEditor.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<Style TargetType="uag:OrderDetailsPopupControl">
|
||||
@@ -156,186 +155,6 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TimeBoxOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.TickOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
|
||||
<TextBlock Grid.Row="3" VerticalAlignment="Center">Cross Ticks</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="3" Grid.Column="2" Value="{Binding cross_ticks}"/>
|
||||
<TextBlock Grid.Row="5" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="5" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
|
||||
<CheckBox Grid.Row="7" Grid.Column="2" VerticalAlignment="Center" Margin="0,0,0,0" IsChecked="{Binding detach_on_toward}">Detach on Toward</CheckBox>
|
||||
<TextBlock Grid.Row="9" VerticalAlignment="Center">Initial Tick Offset</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="9" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
|
||||
<TextBlock Grid.Row="11">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="11" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TickOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.LeanOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Cross Thresh</TextBlock>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding cross_threshold}"/>
|
||||
<TextBlock Grid.Row="3" VerticalAlignment="Center">Max Chase Ticks</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="3" Grid.Column="2" Value="{Binding max_chase_ticks}"/>
|
||||
<TextBlock Grid.Row="5">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="5" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.LeanOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.JoinOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Join Thresh</TextBlock>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding join_threshold}"/>
|
||||
<CheckBox Grid.Row="3" Grid.Column="2" VerticalAlignment="Center" Margin="0,0,0,0" IsChecked="{Binding enable_hedge}">Enable Hedge</CheckBox>
|
||||
<TextBlock Grid.Row="5" VerticalAlignment="Center" IsEnabled="{Binding enable_hedge}" >Hedge Ticks</TextBlock>
|
||||
<c:TickUpDownInt Grid.Row="5" Grid.Column="2" IsEnabled="{Binding enable_hedge}" Value="{Binding hedge_ticks}"/>
|
||||
<TextBlock Grid.Row="7">Hedge</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="7" Grid.Column="2"
|
||||
IsEnabled="{Binding enable_hedge}"
|
||||
IsOpen="{Binding hedge_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding hedge_order_types}"
|
||||
SelectedItem="{Binding hedge_order}"
|
||||
HasEditableProperties="{Binding hedge_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
<TextBlock Grid.Row="9" VerticalAlignment="Center">Initial Tick Offset</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="9" Grid.Column="2" Value="{Binding tick_offset_on_deploy}"/>
|
||||
<TextBlock Grid.Row="11">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="11" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.JoinOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.TopOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="4" />
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="12"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Row="1" VerticalAlignment="Center">Top Thresh</TextBlock>
|
||||
<uag:QuantityMeasureControl Grid.Row="1" Grid.Column="2" QuantityMeasure="{Binding top_threshold}"/>
|
||||
<CheckBox Grid.Row="3" Grid.Column="2" VerticalAlignment="Center" Margin="0,0,0,0" IsChecked="{Binding enable_hedge}">Enable Hedge</CheckBox>
|
||||
<TextBlock Grid.Row="5" VerticalAlignment="Center" IsEnabled="{Binding enable_hedge}" >Hedge Ticks</TextBlock>
|
||||
<c:TickUpDownInt Grid.Row="5" Grid.Column="2" IsEnabled="{Binding enable_hedge}" Value="{Binding hedge_ticks}"/>
|
||||
<TextBlock Grid.Row="7">Hedge</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="7" Grid.Column="2"
|
||||
IsEnabled="{Binding enable_hedge}"
|
||||
IsOpen="{Binding hedge_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding hedge_order_types}"
|
||||
SelectedItem="{Binding hedge_order}"
|
||||
HasEditableProperties="{Binding hedge_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
<TextBlock Grid.Row="9" VerticalAlignment="Center">Watch Levels</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="9" Grid.Column="2" Value="{Binding watch_level_tick_offset}"/>
|
||||
<TextBlock Grid.Row="11">Child</TextBlock>
|
||||
<uag:OrderDetailsPopupControl
|
||||
Grid.Row="11" Grid.Column="2"
|
||||
IsOpen="{Binding child_order.editable_properties_open, FallbackValue=False}"
|
||||
ItemsSource="{Binding child_order_types}"
|
||||
SelectedItem="{Binding child_order}"
|
||||
HasEditableProperties="{Binding child_order.has_editable_properties, FallbackValue=False}"
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.TopOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.IcebergOrderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@@ -361,7 +180,7 @@
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.IcebergOrderTemplate>
|
||||
</usp:OrderModificationTemplateSelector.IcebergOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.SpreaderTemplate>
|
||||
<DataTemplate>
|
||||
<usp:ExecutionStrategyEditor
|
||||
|
||||
@@ -13,10 +13,9 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
class LadderOrderVM : DisposableViewModelBase {
|
||||
class LadderOrderVM : DisposableViewModelBase, IEquatable<LadderOrderVM> {
|
||||
#region Colors
|
||||
static readonly Color _colorbg = Colors.White, _darkbg = Color.FromRgb(0xbf, 0xbf, 0xbf);
|
||||
static readonly Brush _color_bg_brush = _colorbg.rgb_color().rgb_brush();
|
||||
static Brush mkbrush(Color color, Color endcolor, double pct_complete) {
|
||||
GradientStopCollection stops = new GradientStopCollection();
|
||||
stops.Add(new GradientStop(color, 0.0));
|
||||
@@ -35,8 +34,7 @@ namespace mbts.spark {
|
||||
readonly Property<FontFamily> _font_family;
|
||||
readonly Property<FontWeight> _font_weight;
|
||||
readonly Property<double> _font_size;
|
||||
readonly Property<Brush> _foreground, _background, _hoverbackground, _border;
|
||||
readonly Property<string> _summary, _description;
|
||||
readonly Property<Brush> _foreground;
|
||||
IObservable<LadderSettings.BadgeStyle> _bs;
|
||||
LadderSettings.BadgeStyle _current_badge_style;
|
||||
int _n_active_price_change_ops = 0;
|
||||
@@ -58,11 +56,6 @@ namespace mbts.spark {
|
||||
_font_weight = backing(nameof(font_weight), parent.settings, get_font_weight, get_font_weight(fs));
|
||||
_font_size = backing(nameof(font_size), parent.settings, get_font_size, get_font_size(fs));
|
||||
_foreground = backing(nameof(foreground), parent.settings, get_foreground, get_foreground(fs));
|
||||
_background = backing(nameof(background), _color_bg_brush);
|
||||
_hoverbackground = backing(nameof(hoverbackground), _color_bg_brush);
|
||||
_border = backing(nameof(border), _color_bg_brush);
|
||||
_summary = backing(nameof(summary), string.Empty);
|
||||
_description = backing(nameof(description), string.Empty);
|
||||
FontFamily get_font_family(LadderSettings s) => new FontFamily(s.orders_column.font_family);
|
||||
FontWeight get_font_weight(LadderSettings s) => ui.utility.text.TextUtils.from_open_type_weight_safe(s.orders_column.font_weight);
|
||||
double get_font_size(LadderSettings s) => s.orders_column.font_size;
|
||||
@@ -75,12 +68,13 @@ namespace mbts.spark {
|
||||
})
|
||||
);
|
||||
|
||||
//
|
||||
add_order(o);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void update() {
|
||||
void _init() {
|
||||
// update brushes
|
||||
Color color;
|
||||
switch (side) {
|
||||
@@ -100,9 +94,6 @@ namespace mbts.spark {
|
||||
working_quantity = _parent_and_child_not_sharing_slot().Select(order => order.working_quantity()).Sum();
|
||||
quantity = _parent_and_child_not_sharing_slot().Select(order => order.State.Quantity).Sum();
|
||||
|
||||
summary = _orders.Values.Select(order => order.short_description(_parent.security.ContractSpec)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
description = _orders.Values.Select(order => order.full_description(_parent.security)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
|
||||
_set_badge_text();
|
||||
}
|
||||
|
||||
@@ -110,6 +101,7 @@ namespace mbts.spark {
|
||||
if (_orders.Count > 0) {
|
||||
switch (_current_badge_style) {
|
||||
case LadderSettings.BadgeStyle.Alternate:
|
||||
//text = $"{_buy_sell_indicator}:{filled}
|
||||
top_line = $"{_buy_sell_indicator}:{filled}";
|
||||
bottom_line = $"W:{ working_quantity}";
|
||||
break;
|
||||
@@ -122,10 +114,7 @@ namespace mbts.spark {
|
||||
|
||||
public void add_order(OrderRecordImmutable new_order) {
|
||||
_orders[new_order.Key] = new_order;
|
||||
}
|
||||
|
||||
public void remove_order(uint order_key) {
|
||||
_orders.Remove(order_key);
|
||||
_init();
|
||||
}
|
||||
|
||||
//public void update(OrderRecordImmutable neworder) {
|
||||
@@ -138,7 +127,6 @@ namespace mbts.spark {
|
||||
public uint security_key => _parent.security.Id;
|
||||
public double price { get => double.IsNaN(_phantom_price) ? _real_price : _phantom_price; }
|
||||
public double real_price => _real_price;
|
||||
|
||||
public Side side {
|
||||
get {
|
||||
var distinct_sides = _orders.Values.Select(o => o.side()).Distinct();
|
||||
@@ -198,6 +186,8 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public string summary => _orders.Values.Select(order => order.short_description(_parent.security.ContractSpec)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
public string description => _orders.Values.Select(order => order.full_description(_parent.security)).Aggregate((s1, s2) => $"{s1}{Environment.NewLine}{Environment.NewLine}{s2}");
|
||||
public bool has_parent => _orders.Values.Any( order => order.has_parent());
|
||||
public bool has_phantom_price => !double.IsNaN(_phantom_price);
|
||||
|
||||
@@ -214,11 +204,10 @@ namespace mbts.spark {
|
||||
public FontWeight font_weight => _font_weight.Value;
|
||||
public double font_size => _font_size.Value;
|
||||
public Brush foreground => _foreground.Value;
|
||||
public Brush background { get => _background.Value; private set => _background.Value = value; }
|
||||
public Brush hoverbackground { get => _hoverbackground.Value; private set => _hoverbackground.Value = value; }
|
||||
public Brush border { get => _border.Value; private set => _border.Value = value; }
|
||||
public string summary { get => _summary.Value; private set => _summary.Value = value; }
|
||||
public string description { get => _description.Value; private set => _description.Value = value; }
|
||||
public Brush background { get; private set; }
|
||||
public Brush hoverbackground { get; private set; }
|
||||
public Brush header_brush => _orders.Values.FirstOrDefault().Def.Side == Side.Buy ? BTSColors.MarketData.bid : BTSColors.MarketData.ask;
|
||||
public Brush border { get; private set; }
|
||||
public IEnumerable<OrderRecordImmutable> orders => _orders.Values;
|
||||
|
||||
void _cancel_order() {
|
||||
@@ -250,8 +239,40 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return $"Price: {this.price} Order Count: {this.orders.Count()}";
|
||||
|
||||
public override bool Equals(object obj) => this.safe_equals(obj as LadderOrderVM, _non_null_equals);
|
||||
public override int GetHashCode() {
|
||||
// can't use aggregate_hash since OrderRecordImmutable is not a reference type
|
||||
unchecked {
|
||||
int hash = 19;
|
||||
foreach (var o in _orders.Values) {
|
||||
hash = hash * 31 + o.GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
public bool Equals(LadderOrderVM other) => this.safe_equals(other, _non_null_equals);
|
||||
static bool _non_null_equals(LadderOrderVM x, LadderOrderVM y) {
|
||||
// null checks already taken care of safe_equals
|
||||
// same object ?
|
||||
if (object.ReferenceEquals(x,y)) return true;
|
||||
|
||||
// if the Dicts have different count, we have different objects
|
||||
if (x._orders.Count != y._orders.Count) return false;
|
||||
|
||||
// are the keys the same ?
|
||||
foreach(uint kx in x._orders.Keys) {
|
||||
if (!y._orders.ContainsKey(kx))
|
||||
return false;
|
||||
}
|
||||
|
||||
// I don't think we have to look at the Dictionary values
|
||||
// since the keys originate from the OrderRecordImmutable.key
|
||||
//foreach (uint kx in x._orders.Keys) {
|
||||
// if (!x._orders[kx].Equals(y._orders[kx)
|
||||
// return false;
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ namespace mbts.spark {
|
||||
readonly Property<Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
|
||||
IWindowSettingsManager<LadderSettings> _settings_manager;
|
||||
ulong _misc_time = 0;
|
||||
Dictionary<double, LadderOrderVM> _slot_price_vs_orders = new Dictionary<double, LadderOrderVM>();
|
||||
|
||||
public LadderVM(IBus bus, LadderModel model, IWindowSettingsManager<LadderSettings> settings_manager) {
|
||||
_bus = bus;
|
||||
@@ -63,77 +62,46 @@ namespace mbts.spark {
|
||||
return dialog;
|
||||
}, -500, -500);
|
||||
|
||||
var under_orders = new ObservableCollection<OrderRecordImmutable>();
|
||||
orders = new ObservableCollection<LadderOrderVM>();
|
||||
//new ObservableAutoWrapper<LadderOrderVM, OrderRecordImmutable>(under_orders, rec => new LadderOrderVM(rec, this, _bus, _settings_manager.subscribe_current().Select(s => s.badge_style)));
|
||||
|
||||
_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))),
|
||||
size_calculator = new CalculatorLogic(0, _settings_manager.subscribe_current().Select(s => s.calculator_buttons)),
|
||||
bus.subscribe(SparkTags.LogActivity).ObserveOnDispatcher().Subscribe(status_update),
|
||||
// need to subscribe to all orders, and cache them, because parent orders don't necessarily have the same security
|
||||
// and OrderVM needs access to parent orders, if there are any
|
||||
model.order_gateway.working_orders.ObserveOnDispatcher().Subscribe(working => {
|
||||
under_orders.sync_with(working.Values.Where(o => o.Def.SecurityKey == security.Id && o.is_non_terminal()));
|
||||
// Here we manage N orders to 1 price slot in the ladder
|
||||
|
||||
Dictionary<double, HashSet<uint>> orders_used_in_this_update = new Dictionary<double, HashSet<uint>>();
|
||||
var ladders_orders = working.Values.Where(o => o.Def.SecurityKey == security.Id);
|
||||
foreach (var wo in ladders_orders) {
|
||||
var slot_price_target = wo.price();
|
||||
var order_type = wo.order_type();
|
||||
var filled = wo.filled();
|
||||
// there is likely a smarter way to do this than creating this anew each time
|
||||
// need to do something about stop and stop limits in here
|
||||
var slot_price_vs_orders = new Dictionary<double, LadderOrderVM>();
|
||||
foreach(var uo in under_orders) {
|
||||
var slot_price_target = uo.price();
|
||||
var order_type = uo.order_type();
|
||||
var filled = uo.filled();
|
||||
if (order_type == CustomOrderDefinition.CustomOneofCase.Stop) {
|
||||
// if the stop order has been submitted but hasn't been triggered
|
||||
// we draw the badge at the stop/trigger price, else the price price
|
||||
if (filled == 0) { slot_price_target = wo.stop_price(); }
|
||||
if (filled == 0) { slot_price_target = uo.stop_price(); }
|
||||
} else if (order_type == CustomOrderDefinition.CustomOneofCase.StopLimit) {
|
||||
// consistent behavior as Stop order above.
|
||||
if (filled == 0) { slot_price_target = wo.stop_limit_price(); }
|
||||
if (filled == 0) { slot_price_target = uo.stop_limit_price(); }
|
||||
}
|
||||
|
||||
if (_slot_price_vs_orders.TryGetValue(slot_price_target, out LadderOrderVM slot_vm)) {
|
||||
slot_vm.add_order(wo);
|
||||
slot_price_vs_orders.TryGetValue(slot_price_target, out LadderOrderVM slot_vm);
|
||||
if (slot_vm != null) {
|
||||
slot_vm.add_order(uo);
|
||||
} else {
|
||||
slot_vm = new LadderOrderVM(wo, slot_price_target, this, _bus, _settings_manager.subscribe_current().Select(s => s.badge_style));
|
||||
_slot_price_vs_orders[slot_price_target] = slot_vm;
|
||||
slot_vm = new LadderOrderVM(uo, slot_price_target, this, _bus, _settings_manager.subscribe_current().Select(s => s.badge_style));
|
||||
}
|
||||
|
||||
// keep track of slot prices vs orders; we use this below to remove orders from the LadderOrderVMs
|
||||
// that are not found in this callback run
|
||||
if (!orders_used_in_this_update.TryGetValue(slot_price_target, out HashSet<uint> orders_at_level)) {
|
||||
orders_at_level = new HashSet<uint>();
|
||||
}
|
||||
orders_at_level.Add(wo.Key);
|
||||
orders_used_in_this_update[slot_price_target] = orders_at_level;
|
||||
|
||||
slot_price_vs_orders[slot_price_target] = slot_vm;
|
||||
}
|
||||
|
||||
// remove any orders from the lovm that aren't in here
|
||||
foreach(var kvp in orders_used_in_this_update) {
|
||||
var slot_vm = _slot_price_vs_orders[kvp.Key];
|
||||
foreach(var existing_order_key in slot_vm.orders.Select(o => o.Key).ToList()) {
|
||||
if (!kvp.Value.Contains(existing_order_key)) {
|
||||
slot_vm.remove_order(existing_order_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update dirty brushes and text
|
||||
foreach (var lovm in _slot_price_vs_orders.Values) {
|
||||
lovm.update();
|
||||
}
|
||||
|
||||
// complete removal of slots that were not updated
|
||||
// orders_used_in_this_update.Keys are the price levels in the update
|
||||
foreach (var slot_price in _slot_price_vs_orders.Keys.ToList()) {
|
||||
if (!orders_used_in_this_update.Keys.Contains(slot_price)) {
|
||||
_slot_price_vs_orders.TryGetValue(slot_price, out LadderOrderVM removal_candidate);
|
||||
if (removal_candidate != null) {
|
||||
removal_candidate.Dispose();
|
||||
}
|
||||
_slot_price_vs_orders.Remove(slot_price);
|
||||
}
|
||||
}
|
||||
|
||||
orders.sync_with(_slot_price_vs_orders.Values);
|
||||
orders.sync_with(slot_price_vs_orders.Values);
|
||||
orders_cache = working;
|
||||
}),
|
||||
WorkLoop.Start($"Ladder '{security}'", _tick)
|
||||
);
|
||||
@@ -184,6 +152,7 @@ namespace mbts.spark {
|
||||
public Brush ask_foreground => _ask_foreground.Value;
|
||||
public DialogCommand settings_command { get; }
|
||||
internal LadderModel model => _model;
|
||||
internal IReadOnlyDictionary<uint, OrderRecordImmutable> orders_cache { get; private set; } = new Dictionary<uint, OrderRecordImmutable>();
|
||||
internal IObservable<LadderSettings> settings => _settings_manager.subscribe_current();
|
||||
|
||||
public LadderSettings get_save_state() {
|
||||
|
||||
@@ -227,41 +227,6 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="SettingsButton" TargetType="Button" BasedOn="{StaticResource LadderMenuButton}">
|
||||
<Setter Property="Content">
|
||||
<Setter.Value>
|
||||
<TextBlock VerticalAlignment="Center">Settings</TextBlock>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="Border"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
BorderBrush="{mbts:BTSColor UI.Button.normal_border}"
|
||||
Background="{mbts:BTSColor UI.Button.normal_background}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
SnapsToDevicePixels="True"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="0,0,0,12">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RecognizesAccessKey="True"
|
||||
Name="button_content"
|
||||
/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Border" Property="Background" Value="{mbts:BTSColor UI.Button.mouseover_background}" />
|
||||
<Setter TargetName="Border" Property="BorderBrush" Value="{mbts:BTSColor UI.Button.mouseover_border}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<AdornerDecorator>
|
||||
@@ -406,7 +371,16 @@
|
||||
<Button Command="{Binding cancel_command}" CommandParameter="Asks" Style="{StaticResource CancelSells}"/>
|
||||
<Button Command="{Binding cancel_command}" CommandParameter="All" Style="{StaticResource CancelAll}"/>
|
||||
</StackPanel>
|
||||
<Button Grid.Row="1" HorizontalContentAlignment="Stretch" Command="{Binding settings_command}" Style="{StaticResource SettingsButton}" />
|
||||
<Button Grid.Row="1" HorizontalContentAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,12,5,16" Command="{Binding settings_command}" Padding="4,0" Style="{StaticResource LadderMenuButton}" ToolTip="Settings">
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Height="15" SnapsToDevicePixels="True" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Source="{x:Static mbts:Icons.ViewSettings}" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="6,2">Settings</TextBlock>
|
||||
</Grid>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Border Grid.Column="2" HorizontalAlignment="Stretch">
|
||||
|
||||
@@ -258,6 +258,7 @@ namespace mbts.spark {
|
||||
RaisePropertyChanged(p);
|
||||
foreach (var child_row in _child_rows)
|
||||
child_row.on_parent_updated();
|
||||
_row_passes_filter = _book_vm.columns.filter(this);
|
||||
row_passes_filter = _book_vm.columns.filter(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,6 @@ namespace mbts.spark.utilities {
|
||||
CustomOrderDefinition.CustomOneofCase.StopLimit,
|
||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
||||
CustomOrderDefinition.CustomOneofCase.TimeBox,
|
||||
CustomOrderDefinition.CustomOneofCase.Tick,
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
}, _spreader_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Spreader,
|
||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
||||
@@ -192,30 +188,6 @@ namespace mbts.spark.utilities {
|
||||
Start = Timestamp.FromDateTime(DateTime.UtcNow),
|
||||
Stop = Timestamp.FromDateTime(DateTime.UtcNow)
|
||||
};
|
||||
public TickOrderDefinition tick_order() => new TickOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
CrossThreshold = new QuantityMeasure(),
|
||||
};
|
||||
public LeanOrderDefinition lean_order() => new LeanOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
CrossThreshold = new QuantityMeasure(),
|
||||
};
|
||||
public JoinOrderDefinition join_order() => new JoinOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
JoinThreshold = new QuantityMeasure(),
|
||||
Hedge = custom_order(),
|
||||
};
|
||||
public TopOrderDefinition top_order() => new TopOrderDefinition
|
||||
{
|
||||
Child = custom_order(),
|
||||
TopThreshold = new QuantityMeasure(),
|
||||
Hedge = custom_order(),
|
||||
};
|
||||
|
||||
|
||||
public SpreaderOrderDefinition spreader_order() => new SpreaderOrderDefinition { ExecutionStrategy = new ExecutionStrategy() };
|
||||
|
||||
public CustomOrderDefinition custom_order(CustomOrderDefinition.CustomOneofCase order_type) {
|
||||
@@ -227,10 +199,6 @@ namespace mbts.spark.utilities {
|
||||
case CustomOrderDefinition.CustomOneofCase.StopLimit: rv.StopLimit = stop_limit_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Iceberg: rv.Iceberg = iceberg_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.TimeBox: rv.TimeBox = time_box_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Tick: rv.Tick = tick_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Lean: rv.Lean = lean_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: rv.Join = join_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: rv.Top = top_order(); break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: rv.Spreader = spreader_order(); break;
|
||||
}
|
||||
return rv;
|
||||
@@ -242,14 +210,10 @@ namespace mbts.spark.utilities {
|
||||
switch (order_type) {
|
||||
case CustomOrderDefinition.CustomOneofCase.Market: return new OrderMetadata { KeyBinding = "M", Color = BTSColors.yellow.rgb_color(), NoProps = true };
|
||||
case CustomOrderDefinition.CustomOneofCase.Limit: return new OrderMetadata { KeyBinding = "L", Color = BTSColors.green.rgb_color(), NoProps = true };
|
||||
case CustomOrderDefinition.CustomOneofCase.Stop: return new OrderMetadata { KeyBinding = "S", Color = BTSColors.red_orange.rgb_color(), NoProps = true };
|
||||
case CustomOrderDefinition.CustomOneofCase.Stop: return new OrderMetadata { KeyBinding = "S", Color = BTSColors.orange.rgb_color(), NoProps = true };
|
||||
case CustomOrderDefinition.CustomOneofCase.StopLimit: return new OrderMetadata { KeyBinding = "SL", Color = BTSColors.pale_green.rgb_color() };
|
||||
case CustomOrderDefinition.CustomOneofCase.Iceberg: return new OrderMetadata { KeyBinding = "I", Color = BTSColors.pale_blue.rgb_color() };
|
||||
case CustomOrderDefinition.CustomOneofCase.TimeBox: return new OrderMetadata { KeyBinding = "TB", Color = BTSColors.purple.rgb_color() };
|
||||
case CustomOrderDefinition.CustomOneofCase.Tick: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.tick_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Lean: return new OrderMetadata { KeyBinding = "L", Color = BTSColors.Orders.lean_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
|
||||
case CustomOrderDefinition.CustomOneofCase.Spreader: goto default;
|
||||
case CustomOrderDefinition.CustomOneofCase.None: goto default;
|
||||
default: return _default_order_metadata;
|
||||
@@ -259,14 +223,10 @@ namespace mbts.spark.utilities {
|
||||
switch (order_type) {
|
||||
case OrderState.CustomOneofCase.Market: return new OrderMetadata { KeyBinding = "M", Color = BTSColors.yellow.rgb_color(), NoProps = true };
|
||||
case OrderState.CustomOneofCase.Limit: return new OrderMetadata { KeyBinding = "L", Color = BTSColors.green.rgb_color(), NoProps = true };
|
||||
case OrderState.CustomOneofCase.Stop: return new OrderMetadata { KeyBinding = "S", Color = BTSColors.red_orange.rgb_color() };
|
||||
case OrderState.CustomOneofCase.Stop: return new OrderMetadata { KeyBinding = "S", Color = BTSColors.orange.rgb_color() };
|
||||
case OrderState.CustomOneofCase.StopLimit: return new OrderMetadata { KeyBinding = "SL", Color = BTSColors.pale_green.rgb_color() };
|
||||
case OrderState.CustomOneofCase.Iceberg: return new OrderMetadata { KeyBinding = "I", Color = BTSColors.pale_blue.rgb_color() };
|
||||
case OrderState.CustomOneofCase.TimeBox: return new OrderMetadata { KeyBinding = "TB", Color = BTSColors.purple.rgb_color() };
|
||||
case OrderState.CustomOneofCase.Tick: return new OrderMetadata { KeyBinding = "T", Color = BTSColors.Orders.tick_order };
|
||||
case OrderState.CustomOneofCase.Lean: return new OrderMetadata { KeyBinding = "L", Color = BTSColors.Orders.lean_order };
|
||||
case OrderState.CustomOneofCase.Join: return new OrderMetadata { KeyBinding = "J", Color = BTSColors.Orders.join_order };
|
||||
case OrderState.CustomOneofCase.Top: return new OrderMetadata { KeyBinding = "D", Color = BTSColors.Orders.top_order };
|
||||
case OrderState.CustomOneofCase.Spreader: goto default;
|
||||
case OrderState.CustomOneofCase.None: goto default;
|
||||
default: return _default_order_metadata;
|
||||
@@ -298,10 +258,6 @@ namespace mbts.spark.utilities {
|
||||
case nameof(IcebergOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Iceberg;
|
||||
case nameof(SpreaderOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Spreader;
|
||||
case nameof(TimeBoxOrderDefinition): return CustomOrderDefinition.CustomOneofCase.TimeBox;
|
||||
case nameof(TickOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Tick;
|
||||
case nameof(LeanOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Lean;
|
||||
case nameof(JoinOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Join;
|
||||
case nameof(TopOrderDefinition): return CustomOrderDefinition.CustomOneofCase.Top;
|
||||
default: throw new ArgumentException($"Unable to match '{order_type_def?.Descriptor.Name}' to an order definition type");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ namespace sparkcmd
|
||||
if (args.FirstOrDefault() == "usertest") {
|
||||
user_test();
|
||||
}
|
||||
else if (args.FirstOrDefault() == "service") {
|
||||
new ServiceDriver().run(args.Skip(1));
|
||||
}
|
||||
else if (args.FirstOrDefault() == "synthetic") {
|
||||
synthetic_test();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using bts.api.admin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace sparkcmd
|
||||
{
|
||||
class ServiceDriver
|
||||
{
|
||||
|
||||
string menu(string prompt, params string[] options) {
|
||||
while (true) {
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(prompt);
|
||||
for (int x = 0; x < options.Length; ++x) {
|
||||
Console.WriteLine($"{x}. {options[x]}");
|
||||
}
|
||||
var k = Console.ReadKey();
|
||||
Console.WriteLine();
|
||||
if (Int32.TryParse(k.KeyChar.ToString(), out int c) &&
|
||||
c >= 0 && c < options.Length) {
|
||||
return options[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string prompt(string prompt, int indent = 0) {
|
||||
Console.Write(new String(' ', indent));
|
||||
Console.Write(prompt);
|
||||
return Console.ReadLine();
|
||||
}
|
||||
|
||||
T prompt2<T>(string msg, Func<string,T> andthen) {
|
||||
while(true) {
|
||||
var str = prompt(msg);
|
||||
try {
|
||||
return andthen(str);
|
||||
}catch(Exception e) {
|
||||
Console.WriteLine(e);
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T json_value_prompt<T>() {
|
||||
T value = default;
|
||||
do {
|
||||
var serialized = prompt($"Enter {typeof(T).Name} as json: ");
|
||||
serialized = serialized.Trim(' ', '\t', '\r', '\n');
|
||||
if (serialized[0] != '{') serialized = "{" + serialized;
|
||||
if (serialized.Last() != '}') serialized += "}";
|
||||
try {
|
||||
value = JsonConvert.DeserializeObject<T>(serialized);
|
||||
} catch(Exception e) { Console.WriteLine(e); }
|
||||
} while(value == null);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void run(IEnumerable<string> args) {
|
||||
// input service name
|
||||
//
|
||||
var service_name = args.FirstOrDefault() ?? prompt("Service Name: ");
|
||||
var service_type = Type.GetType(service_name);
|
||||
// List methods
|
||||
var service_methods = service_type.GetMethods().Select(md => md.Name).ToArray();
|
||||
menu("Which method do you want to call?", service_methods);
|
||||
|
||||
|
||||
// var ps = json_value_prompt<SecurityScope>();
|
||||
// Console.WriteLine(ps.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
from __future__ import print_function
|
||||
import os, sys
|
||||
from collections import namedtuple
|
||||
|
||||
@@ -31,8 +30,8 @@ import apioptions_pb2
|
||||
def loadTemplate(lang, name):
|
||||
path = os.path.join("templates", lang, name)
|
||||
text = open(path).read()
|
||||
#utext = unicode(text, "utf-8")
|
||||
return compiler.compile(text)
|
||||
utext = unicode(text, "utf-8")
|
||||
return compiler.compile(utext)
|
||||
|
||||
def option(options, key, default=None):
|
||||
if options.HasExtension(key):
|
||||
@@ -308,7 +307,7 @@ if __name__ == "__main__":
|
||||
if DEBUG:
|
||||
data = open("data", mode='rb').read()
|
||||
else:
|
||||
data = sys.stdin.buffer.read()
|
||||
data = sys.stdin.read()
|
||||
request = plugin.CodeGeneratorRequest()
|
||||
# open("data", 'wb').write(data)
|
||||
request.ParseFromString(data)
|
||||
@@ -317,8 +316,8 @@ if __name__ == "__main__":
|
||||
|
||||
if DEBUG:
|
||||
for f in response.file:
|
||||
print(f.content)
|
||||
print f.content
|
||||
else:
|
||||
output = response.SerializeToString()
|
||||
sys.stdout.buffer.write(output)
|
||||
sys.stdout.write(output)
|
||||
|
||||
@@ -64,13 +64,9 @@ message CustomOrderDefinition
|
||||
IcebergOrderDefinition iceberg = 15;
|
||||
SpreaderOrderDefinition spreader = 16;
|
||||
TimeBoxOrderDefinition time_box = 17;
|
||||
TickOrderDefinition tick = 18;
|
||||
LeanOrderDefinition lean = 19;
|
||||
JoinOrderDefinition join = 20;
|
||||
TopOrderDefinition top = 21;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Custom order definitions
|
||||
message MarketOrderDefinition {}
|
||||
message LimitOrderDefinition {}
|
||||
@@ -80,11 +76,6 @@ message IcebergOrderDefinition {double show_quantity = 1; CustomOrderDefinition
|
||||
message SpreaderOrderDefinition {api.Security security = 1; spark.ExecutionStrategy execution_strategy = 2;}
|
||||
message TimeBoxOrderDefinition {google.protobuf.Timestamp start = 1; google.protobuf.Timestamp stop = 2;CustomOrderDefinition child = 3;}
|
||||
|
||||
message TickOrderDefinition {uint32 tick_offset_on_deploy = 1; spark.QuantityMeasure cross_threshold = 2; uint32 cross_ticks = 3; uint32 max_chase_ticks = 4; bool detach_on_toward = 5; CustomOrderDefinition child = 6; }
|
||||
message LeanOrderDefinition {spark.QuantityMeasure cross_threshold = 1; uint32 max_chase_ticks = 2; CustomOrderDefinition child = 3; }
|
||||
message JoinOrderDefinition {uint32 tick_offset_on_deploy = 1; spark.QuantityMeasure join_threshold = 2; bool enable_hedge = 3; CustomOrderDefinition hedge = 4; int32 hedge_ticks = 5; CustomOrderDefinition child = 6;}
|
||||
message TopOrderDefinition {uint32 watch_level_tick_offset = 1; spark.QuantityMeasure top_threshold = 2; bool enable_hedge = 3; CustomOrderDefinition hedge = 4; int32 hedge_ticks = 5; CustomOrderDefinition child = 6;}
|
||||
|
||||
message OrderRecord {
|
||||
uint32 key = 1 [(bts.api.key)=true];
|
||||
uint32 parent_key = 2;
|
||||
@@ -115,10 +106,6 @@ message OrderState {
|
||||
IcebergOrderState iceberg = 13;
|
||||
SpreaderOrderState spreader = 14;
|
||||
TimeBoxOrderState time_box = 15;
|
||||
TickOrderState tick = 17;
|
||||
LeanOrderState lean = 18;
|
||||
JoinOrderState join = 19;
|
||||
TopOrderState top = 20;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,10 +158,6 @@ message StopLimitOrderState {NativeOrderState native_state = 1;}
|
||||
message IcebergOrderState {}
|
||||
message SpreaderOrderState {SpreaderChildKind child_kind = 1;}
|
||||
message TimeBoxOrderState {}
|
||||
message TickOrderState {}
|
||||
message LeanOrderState {}
|
||||
message JoinOrderState {}
|
||||
message TopOrderState {}
|
||||
|
||||
enum OrderStatus {
|
||||
|
||||
@@ -216,6 +199,8 @@ enum TimeInForce {
|
||||
TIME_IN_FORCE_UNDEFINED = 0;
|
||||
TIME_IN_FORCE_DAY = 48;
|
||||
TIME_IN_FORCE_GTC = 49;
|
||||
TIME_IN_FORCE_OPG = 50;
|
||||
TIME_IN_FORCE_IOC = 51;
|
||||
}
|
||||
|
||||
enum ManualOrderIndicator {
|
||||
|
||||
@@ -77,7 +77,7 @@ message JetFIX {
|
||||
|
||||
optional string trading_session_id = 330 [default=""];
|
||||
optional int32 udf_support_indicator = 340 [default=1];
|
||||
optional bool use_cme_secure_logon = 341 [default=true];
|
||||
optional bool use_cme_secure_logon = 341 [default=false];
|
||||
optional bool auto_cert_mode = 342 [default=false];
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ void ExchangeSimApplication::onMessage(const FIX42::NewOrderSingle &message,
|
||||
message.get(ordType);
|
||||
if (ordType == FIX::OrdType_LIMIT)
|
||||
message.get(price);
|
||||
|
||||
|
||||
message.get(orderQty);
|
||||
message.getFieldIfSet(timeInForce);
|
||||
|
||||
@@ -206,7 +206,7 @@ void ExchangeSimApplication::onMessage(const FIX42::NewOrderSingle &message,
|
||||
auto &matcher = me->matchers[sec_desc];
|
||||
if (matcher.get_order(clOrdID))
|
||||
throw std::logic_error("Duplicate order");
|
||||
|
||||
|
||||
ex_sim::Order order;
|
||||
order.set_clordid(clOrdID);
|
||||
order.set_sec_desc(sec_desc);
|
||||
@@ -227,7 +227,7 @@ void ExchangeSimApplication::onMessage(const FIX42::NewOrderSingle &message,
|
||||
else
|
||||
throw new std::logic_error("No sell market");
|
||||
} else {
|
||||
|
||||
|
||||
if (book.bids.size())
|
||||
order.set_price(book.bids.begin()->first.value());
|
||||
else
|
||||
@@ -283,11 +283,9 @@ void ExchangeSimApplication::onMessage(
|
||||
try {
|
||||
auto &matcher = me->matchers[sec_desc];
|
||||
auto order = matcher.get_order(clOrdID);
|
||||
if (!order) {
|
||||
LOG(ExSim, Info, "Receive cancel/replace for unknown order {}",
|
||||
clOrdID);
|
||||
if (!order)
|
||||
throw std::logic_error("Unknown order");
|
||||
} else {
|
||||
else {
|
||||
auto order_cpy = *order;
|
||||
order_cpy.set_price(price);
|
||||
order_cpy.set_quantity(orderQty);
|
||||
@@ -320,11 +318,9 @@ void ExchangeSimApplication::onMessage(const FIX42::OrderCancelRequest &message,
|
||||
try {
|
||||
auto &matcher = me->matchers[sec_desc];
|
||||
auto order = matcher.get_order(origClOrdID);
|
||||
if (!order) {
|
||||
LOG(ExSim, Info, "Receive cancel for unknown order {}",
|
||||
origClOrdID.getString());
|
||||
if (!order)
|
||||
throw std::logic_error("Unknown order");
|
||||
} else {
|
||||
else {
|
||||
auto order_cpy = *order;
|
||||
order_cpy.set_open_quantity(0);
|
||||
updateOrder(order_cpy, FIX::OrdStatus_CANCELED);
|
||||
@@ -443,32 +439,20 @@ void ExchangeSimApplication::update_md(std::string const &sec_desc) {
|
||||
|
||||
auto &handle = it->second;
|
||||
auto &matcher = me->matchers[sec_desc];
|
||||
auto order_book = matcher.get_order_book("SYNTH");
|
||||
order_book.set_sec_desc(sec_desc);
|
||||
persist_book(order_book);
|
||||
|
||||
auto book = matcher.get_book();
|
||||
|
||||
auto price_factor = pow(10, handle.security().display_factor);
|
||||
|
||||
const auto cmax = md::COCONUT_MAX_DEPTH;
|
||||
std::array<md::PriceSize, cmax> bids_temp = {}, asks_temp = {};
|
||||
auto bids_end = book.bids.size() > cmax
|
||||
? std::next(book.bids.begin(), cmax)
|
||||
: book.bids.end();
|
||||
std::transform(book.bids.begin(), bids_end, bids_temp.begin(),
|
||||
[&](const auto &bs) {
|
||||
return md::PriceSize::from_double<7>(
|
||||
bs.first.value() * price_factor, bs.second.value());
|
||||
});
|
||||
auto asks_end = book.asks.size() > cmax
|
||||
? std::next(book.asks.begin(), cmax)
|
||||
: book.asks.end();
|
||||
std::transform(book.asks.begin(), asks_end, asks_temp.begin(),
|
||||
[&](const auto &bs) {
|
||||
return md::PriceSize::from_double<7>(
|
||||
bs.first.value() * price_factor, bs.second.value());
|
||||
});
|
||||
std::array<md::PriceSize,cmax> bids_temp = { }, asks_temp = { };
|
||||
auto bids_end = book.bids.size() > cmax ?
|
||||
std::next(book.bids.begin(), cmax) : book.bids.end();
|
||||
std::transform(book.bids.begin(), bids_end, bids_temp.begin(),
|
||||
[&](const auto& bs) { return md::PriceSize::from_double<7>(bs.first.value()*price_factor, bs.second.value()); });
|
||||
auto asks_end = book.asks.size() > cmax ?
|
||||
std::next(book.asks.begin(), cmax) : book.asks.end();
|
||||
std::transform(book.asks.begin(), asks_end, asks_temp.begin(),
|
||||
[&](const auto& bs) { return md::PriceSize::from_double<7>(bs.first.value()*price_factor, bs.second.value()); });
|
||||
handle.update_book(utils::TimeStamp::now(), bids_temp, asks_temp);
|
||||
|
||||
auto new_trades = matcher.get_new_trades();
|
||||
@@ -604,8 +588,7 @@ void ExchangeSimApplication::merge_with_md_book(std::string const &sec_desc,
|
||||
order_book.set_sec_desc(sec_desc);
|
||||
persist_book(order_book);
|
||||
auto sz = matcher.size();
|
||||
LOG(ExSim, Diag, "Book size: {} {} {} {}", sec_desc, std::get<0>(sz),
|
||||
std::get<1>(sz), std::get<2>(sz));
|
||||
LOG(ExSim,Diag,"Book size: {} {} {} {}",sec_desc,std::get<0>(sz),std::get<1>(sz),std::get<2>(sz));
|
||||
}
|
||||
|
||||
void ExchangeSimApplication::persist_book(OrderBook const &b) {
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
// risk limit checks
|
||||
////////////////////
|
||||
Limiter::LimiterQueryResult can_send_new_order(api::Side side, Quantity quantity) const{
|
||||
Limiter::LimiterQueryResult result;result.ok=true;
|
||||
Limiter::LimiterQueryResult result;
|
||||
if (quantity.value() > _clip_size){
|
||||
result.ok = false;
|
||||
result.error_msg = fmt::format("Security Clip Size (Max {} Qty {})", _clip_size, quantity.value());
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
#include "server/sparkd/orderman/orders/spreader_orders/SpreaderOrder.h"
|
||||
#include "server/sparkd/orderman/orders/NativeOrder.h"
|
||||
#include "server/sparkd/orderman/orders/TimeBoxOrder.h"
|
||||
#include "server/sparkd/orderman/orders/book_algos/TickOrder.h"
|
||||
#include "server/sparkd/orderman/orders/book_algos/LeanOrder.h"
|
||||
#include "server/sparkd/orderman/orders/book_algos/JoinOrder.h"
|
||||
#include "server/sparkd/orderman/orders/book_algos/TopOrder.h"
|
||||
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
@@ -124,18 +120,6 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
case CustomOrderDefinition::kTimeBox:
|
||||
order_ptr.reset (new orderman::TimeBoxOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kTick:
|
||||
order_ptr.reset (new orderman::TickOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kLean:
|
||||
order_ptr.reset (new orderman::LeanOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kJoin:
|
||||
order_ptr.reset (new orderman::JoinOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::kTop:
|
||||
order_ptr.reset (new orderman::TopOrder());
|
||||
break;
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
bts_assert(false);
|
||||
break;
|
||||
|
||||
@@ -78,7 +78,10 @@ void NativeOrder::on_init_new(OrderDefinition const &def,
|
||||
case CustomOrderDefinition::kStopLimit:
|
||||
me->state.mutable_stop_limit();
|
||||
break;
|
||||
default:
|
||||
case CustomOrderDefinition::kIceberg:
|
||||
case CustomOrderDefinition::kSpreader:
|
||||
case CustomOrderDefinition::kTimeBox:
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
@@ -340,7 +343,10 @@ void NativeOrder::on_activate(Price p, Quantity q,
|
||||
case CustomOrderDefinition::kStop:
|
||||
case CustomOrderDefinition::kMarket:
|
||||
break;
|
||||
default:
|
||||
case CustomOrderDefinition::kIceberg:
|
||||
case CustomOrderDefinition::kSpreader:
|
||||
case CustomOrderDefinition::kTimeBox:
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
@@ -823,7 +829,10 @@ void NativeOrder::init_fix() {
|
||||
me->update_price_bm = update_encoding.add<fix::Price>(0, price_sz());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case CustomOrderDefinition::kIceberg:
|
||||
case CustomOrderDefinition::kSpreader:
|
||||
case CustomOrderDefinition::kTimeBox:
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
@@ -966,7 +975,10 @@ NativeOrder::get_native_state(api::orderman::OrderState &st) {
|
||||
return *st.mutable_stop()->mutable_native_state();
|
||||
case OrderState::kStopLimit:
|
||||
return *st.mutable_stop_limit()->mutable_native_state();
|
||||
default:
|
||||
case OrderState::kIceberg:
|
||||
case OrderState::kSpreader:
|
||||
case OrderState::kTimeBox:
|
||||
case OrderState::CUSTOM_NOT_SET:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,442 +0,0 @@
|
||||
#include "BookAlgo.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct BookAlgo::PImpl
|
||||
{
|
||||
PImpl() : services(nullptr), fake_detached_child(false),
|
||||
supports_hedge(false)
|
||||
{
|
||||
}
|
||||
|
||||
OrderServices* services;
|
||||
|
||||
api::orderman::OrderDefinition def;
|
||||
api::orderman::OrderState state;
|
||||
|
||||
OrderBuilder child_builder;
|
||||
ChildOrder child;
|
||||
api::orderman::OrderState child_state;
|
||||
bool fake_detached_child;
|
||||
|
||||
bool supports_hedge;
|
||||
OrderBuilder hedge_builder;
|
||||
std::vector<ChildOrder> hedges;
|
||||
using HedgeStateMap = boost::container::flat_map<OrderKey, api::orderman::OrderState>;
|
||||
HedgeStateMap hedges_state;
|
||||
|
||||
double tick_size;
|
||||
};
|
||||
|
||||
BookAlgo::BookAlgo() : me(new PImpl())
|
||||
{
|
||||
}
|
||||
|
||||
BookAlgo::~BookAlgo() {}
|
||||
|
||||
|
||||
|
||||
const api::orderman::OrderDefinition& BookAlgo::definition() const
|
||||
{
|
||||
return me->def;
|
||||
}
|
||||
|
||||
const api::orderman::OrderState& BookAlgo::this_state() const
|
||||
{
|
||||
return me->state;
|
||||
}
|
||||
|
||||
void BookAlgo::set_price(double p)
|
||||
{
|
||||
me->state.set_price(p);
|
||||
}
|
||||
|
||||
void BookAlgo::set_quantity(uint32_t s)
|
||||
{
|
||||
me->state.set_quantity(s);
|
||||
}
|
||||
|
||||
const api::orderman::OrderState& BookAlgo::child_state() const
|
||||
{
|
||||
return me->child_state;
|
||||
}
|
||||
|
||||
void BookAlgo::set_child_price(double p)
|
||||
{
|
||||
me->child_state.set_price(p);
|
||||
}
|
||||
|
||||
void BookAlgo::set_child_quantity(uint32_t s)
|
||||
{
|
||||
me->child_state.set_quantity(s);
|
||||
}
|
||||
|
||||
void BookAlgo::activate_child(Price p, Quantity q)
|
||||
{
|
||||
me->child.activate(p, q);
|
||||
}
|
||||
|
||||
void BookAlgo::update_child(Price p, Quantity q)
|
||||
{
|
||||
me->child.update(p, q);
|
||||
}
|
||||
|
||||
void BookAlgo::cancel_child()
|
||||
{
|
||||
me->child.cancel();
|
||||
}
|
||||
|
||||
void BookAlgo::build_and_send_hedge(Price p, Quantity q)
|
||||
{
|
||||
auto hedge = me->hedge_builder.build();
|
||||
me->hedges.push_back(std::move(hedge));
|
||||
me->hedges.back().activate(p, q);
|
||||
}
|
||||
|
||||
double BookAlgo::tick_size() const
|
||||
{
|
||||
return me->tick_size;
|
||||
}
|
||||
|
||||
void BookAlgo::on_init_new(const api::orderman::OrderDefinition& def,
|
||||
const PreActivationOrderServices &services)
|
||||
{
|
||||
me->services = nullptr;
|
||||
me->def = def;
|
||||
|
||||
me->child_state.set_status(ORDER_STATUS_INACTIVE);
|
||||
OrderDefinition child_def = def;
|
||||
setup_child(def, services, me->child_state, &child_def);
|
||||
me->child_builder = OrderBuilder(key(), child_def, services, 1);
|
||||
|
||||
OrderDefinition hedge_def = def;
|
||||
me->supports_hedge = setup_hedge(def, services, &hedge_def);
|
||||
if(me->supports_hedge)
|
||||
me->hedge_builder = OrderBuilder(key(), hedge_def, services, 4);
|
||||
|
||||
const bts::api::Security* sec = services.sec_db.resolve(SecurityKey(def.security_key()));
|
||||
if(sec)
|
||||
{
|
||||
me->tick_size = sec->contract_spec().low_tick();
|
||||
transition(ORDER_STATUS_INACTIVE);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto msg = fmt::format("Unknown security: {}", def.security_key());
|
||||
me->state.set_last_msg(msg);
|
||||
transition(ORDER_STATUS_REJECTED_BY_SPARK, msg);
|
||||
}
|
||||
}
|
||||
|
||||
void BookAlgo::on_init_recovered(const api::orderman::OrderRecord& rec,
|
||||
const PreActivationOrderServices &)
|
||||
{
|
||||
auto const &def = rec.def();
|
||||
me->def = def;
|
||||
me->state = rec.state();
|
||||
switch (me->state.status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
auto msg = "System restarted with active BookAlgo";
|
||||
me->state.set_last_msg(msg);
|
||||
transition(ORDER_STATUS_ERROR, msg);
|
||||
break;
|
||||
}
|
||||
// terminal states
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
break;
|
||||
|
||||
// impossible to reach states for this order type
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BookAlgo::on_activate(Price p, Quantity q, OrderServices& services)
|
||||
{
|
||||
bts_assert(!me->services);
|
||||
me->services = &services;
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
me->child_builder.activate(services);
|
||||
if(me->supports_hedge)
|
||||
me->hedge_builder.activate(services);
|
||||
|
||||
me->child = me->child_builder.build();
|
||||
set_price(p.value());
|
||||
set_quantity(q.value());
|
||||
services.child_updates.sub(key(),
|
||||
[this](auto const &os) { this->on_child_order_update(os); });
|
||||
services.book_updates.sub(SecurityKey(me->def.security_key()),
|
||||
[this](auto const &b) { this->on_book_update(b); });
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
break;
|
||||
|
||||
// terminal states
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
break;
|
||||
|
||||
// impossible to reach states at this stage (on recovery from disk
|
||||
// we would have transitioned to error if not already in a terminal
|
||||
// state)
|
||||
case ORDER_STATUS_WORKING:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::on_update(Price p, Quantity q)
|
||||
{
|
||||
LOG(OrderMan, Info, "Updating BookAlgo {}", key().value());
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
set_price(p.value());
|
||||
set_quantity(q.value());
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
set_price(p.value());
|
||||
set_quantity(q.value());
|
||||
if(!me->fake_detached_child)
|
||||
me->child.update(p,q);
|
||||
break;
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
// we launched our child but we are not yet able
|
||||
// to update it
|
||||
transition(this_state().status(), "Cannot update order");
|
||||
break;
|
||||
|
||||
// if the child was in any of these states we couldn't be
|
||||
// in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
transition(this_state().status(), "Cannot update order");
|
||||
break;
|
||||
|
||||
// these are all unreacheable for this order type at this point
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::on_crack() {}
|
||||
|
||||
void BookAlgo::on_cancel()
|
||||
{
|
||||
LOG(OrderMan, Info, "Canceling BookAlgo Order {}", key().value());
|
||||
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
if(!me->fake_detached_child)
|
||||
me->child.cancel();
|
||||
for(auto& i : me->hedges)
|
||||
i.crack();
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
break;
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
// child is running but it cannot be canceled at this point
|
||||
transition(this_state().status(), "Cannot cancel order");
|
||||
break;
|
||||
|
||||
// child cannot be in any of these if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
transition(this_state().status(), "Cannot cancel order");
|
||||
break;
|
||||
|
||||
// these are unreacheable for this order type
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::transition(api::orderman::OrderStatus new_status, std::string msg)
|
||||
{
|
||||
auto const &from = OrderStatus_Name(me->state.status());
|
||||
auto const &to = OrderStatus_Name(new_status);
|
||||
LOG(OrderMan, Info, "Transitioning BookAlgo {} from {} to {}. Msg: {}", key().value(), from, to, msg);
|
||||
me->state.set_status(new_status);
|
||||
}
|
||||
|
||||
void BookAlgo::on_child_order_update(const OrderStateUpdateEvent& ev)
|
||||
{
|
||||
if(me->fake_detached_child)
|
||||
return;
|
||||
if(ev.key != me->child.key())
|
||||
me->hedges_state[ev.key] = ev.state;
|
||||
else
|
||||
{
|
||||
api::orderman::OrderState orig_state = child_state();
|
||||
me->child_state = ev.state;
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (ev.state.status())
|
||||
{
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
if(me->supports_hedge)
|
||||
send_hedge_if_new_fills(orig_state, ev);
|
||||
break;
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
transition(ORDER_STATUS_COMPLETED);
|
||||
if(me->supports_hedge)
|
||||
send_hedge_if_new_fills(orig_state, ev);
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
transition(ORDER_STATUS_ERROR, "Unexpected child order state");
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::report_state()
|
||||
{
|
||||
auto merged_state = me->state;
|
||||
merged_state.set_filled_quantity(child_state().filled_quantity());
|
||||
merged_state.set_last_price(child_state().last_price());
|
||||
merged_state.set_avg_price(child_state().avg_price());
|
||||
me->services->report_state(merged_state);
|
||||
}
|
||||
|
||||
void BookAlgo::payup(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() + me->tick_size * cross_ticks;
|
||||
me->child.update(Price(new_price), Quantity(this_state().quantity()));
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::paydown(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() - me->tick_size * cross_ticks;
|
||||
me->child.update(Price(new_price), Quantity(this_state().quantity()));
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::detach(bts::api::orderman::OrderStatus status_after_detach)
|
||||
{
|
||||
me->fake_detached_child = true;
|
||||
me->child.crack();
|
||||
transition(status_after_detach);
|
||||
report_state();
|
||||
}
|
||||
|
||||
void BookAlgo::on_book_update(const bts::orderman::BookUpdateEvent&) {}
|
||||
|
||||
bool BookAlgo::setup_hedge(const api::orderman::OrderDefinition&, const PreActivationOrderServices&, api::orderman::OrderDefinition*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void BookAlgo::send_hedge_if_new_fills(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
|
||||
{
|
||||
UNUSED(orig_state);UNUSED(new_state);
|
||||
// uint32_t qty = new_state.state.filled_quantity() - orig_state.filled_quantity();
|
||||
// UNUSED(qty);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "server/sparkd/orderman/orders/Order.h"
|
||||
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "lib/api/services/OrderService.server.pb.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//Book algo is a useful base class for implementing order algorithms that do things base on the on_book method
|
||||
|
||||
class BookAlgo : public Order
|
||||
{
|
||||
public:
|
||||
BookAlgo();
|
||||
virtual ~BookAlgo();
|
||||
const api::orderman::OrderDefinition& definition() const override;
|
||||
void on_init_new(const api::orderman::OrderDefinition&,
|
||||
const PreActivationOrderServices&) override;
|
||||
void on_init_recovered(const api::orderman::OrderRecord&,
|
||||
const PreActivationOrderServices &) override;
|
||||
void on_activate(Price, Quantity, OrderServices&) override;
|
||||
void on_update(Price, Quantity) override;
|
||||
void on_crack() override;
|
||||
void on_cancel() override;
|
||||
|
||||
protected:
|
||||
virtual void setup_child(const api::orderman::OrderDefinition& parent, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) = 0;
|
||||
virtual bool setup_hedge(const api::orderman::OrderDefinition& parent, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition*);
|
||||
virtual void send_hedge_if_new_fills(const api::orderman::OrderState&, const OrderStateUpdateEvent&);
|
||||
|
||||
virtual void on_child_order_update(const OrderStateUpdateEvent&);
|
||||
virtual void on_book_update(const bts::orderman::BookUpdateEvent&);
|
||||
|
||||
virtual void transition(api::orderman::OrderStatus new_state, std::string = "");
|
||||
virtual void report_state();
|
||||
|
||||
virtual void payup(const md::PriceSize&, uint32_t cross_ticks = 0);
|
||||
virtual void paydown(const md::PriceSize&, uint32_t cross_ticks = 0);
|
||||
void detach(bts::api::orderman::OrderStatus status_after_detach = bts::api::orderman::ORDER_STATUS_CANCELED);
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
protected:
|
||||
const api::orderman::OrderState& this_state() const;
|
||||
void set_price(double);
|
||||
void set_quantity(uint32_t);
|
||||
|
||||
const api::orderman::OrderState& child_state() const;
|
||||
void set_child_price(double);
|
||||
void set_child_quantity(uint32_t);
|
||||
void activate_child(Price p, Quantity q);
|
||||
void update_child(Price, Quantity);
|
||||
void cancel_child();
|
||||
|
||||
void build_and_send_hedge(Price, Quantity);
|
||||
double tick_size() const;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,185 +0,0 @@
|
||||
#include "JoinOrder.h"
|
||||
|
||||
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "server/sparkd/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct JoinOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
uint32_t tick_offset_on_deploy;
|
||||
spark::QuantityMeasure join_threshold;
|
||||
bool enable_hedge;
|
||||
int hedge_ticks;
|
||||
OrderDefinition hedge_def;
|
||||
|
||||
md::PriceSize opposite_at_inception;
|
||||
bool did_join;
|
||||
};
|
||||
|
||||
|
||||
JoinOrder::JoinOrder() : me(new PImpl()) {}
|
||||
|
||||
JoinOrder::~JoinOrder() {}
|
||||
|
||||
void JoinOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_join();
|
||||
auto const &join_def = this_def.custom_def().join();
|
||||
*out_child->mutable_custom_def() = join_def.child();
|
||||
|
||||
me->tick_offset_on_deploy = join_def.tick_offset_on_deploy();
|
||||
me->join_threshold = join_def.join_threshold();
|
||||
me->enable_hedge = join_def.enable_hedge();
|
||||
me->hedge_ticks = join_def.hedge_ticks();
|
||||
me->did_join = false;
|
||||
}
|
||||
|
||||
bool JoinOrder::setup_hedge(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition* out_hedge)
|
||||
{
|
||||
auto const &join_def = this_def.custom_def().join();
|
||||
if(me->enable_hedge && join_def.has_hedge())
|
||||
{
|
||||
*out_hedge->mutable_custom_def() = join_def.hedge();
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
out_hedge->set_side(api::SIDE_SELL);
|
||||
else
|
||||
out_hedge->set_side(api::SIDE_BUY);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void JoinOrder::send_hedge_if_new_fills(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
|
||||
{
|
||||
uint32_t qty = new_state.state.filled_quantity() - orig_state.filled_quantity();
|
||||
if(qty > 0)
|
||||
{
|
||||
double prx = new_state.state.last_price();
|
||||
if(definition().side() == api::SIDE_BUY) //if this is a buy, I'm going to sell my hedge, nticks higher than the fill price
|
||||
prx = prx + tick_size() * me->hedge_ticks;
|
||||
else
|
||||
prx = prx - tick_size() * me->hedge_ticks;
|
||||
|
||||
build_and_send_hedge(Price(prx), Quantity(qty));
|
||||
}
|
||||
}
|
||||
|
||||
void JoinOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
{
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
{
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
{
|
||||
me->opposite_at_inception = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if(me->tick_offset_on_deploy > 0)
|
||||
{
|
||||
double price = me->opposite_at_inception.price() - tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
else
|
||||
{
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
me->opposite_at_inception = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if(me->tick_offset_on_deploy > 0)
|
||||
{
|
||||
double price = me->opposite_at_inception.price() + tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
else
|
||||
{
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
Quantity thresh = SpreaderUtils::_quantity_threshold(me->join_threshold, Quantity(this_state().quantity()));
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if(me->opposite_at_inception.price() > same.price())
|
||||
{}
|
||||
//we join exactly once, but we can't detach because we have to hang around and look for hedges
|
||||
else if(!me->did_join && same.size() >= thresh.value())
|
||||
{
|
||||
me->did_join = true;
|
||||
update_child(Price(same.price()), Quantity(this_state().quantity()));
|
||||
report_state();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if(me->opposite_at_inception.price() < same.price())
|
||||
{}
|
||||
else if(!me->did_join && same.size() >= thresh.value())
|
||||
{
|
||||
me->did_join = true;
|
||||
update_child(Price(same.price()), Quantity(this_state().quantity()));
|
||||
report_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/orderman/orders/book_algos/BookAlgo.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//JoinOrder starts working N ticks away from the market (usually) and then watches for
|
||||
//the opposite inside to become same side inside market; if there is then enough size on that market, it will join.
|
||||
//Fills optionally trigger hedge orders to the opposite side of the market, n ticks away.
|
||||
|
||||
class JoinOrder : public BookAlgo
|
||||
{
|
||||
public:
|
||||
JoinOrder();
|
||||
virtual ~JoinOrder();
|
||||
|
||||
protected:
|
||||
void setup_child(const api::orderman::OrderDefinition&, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
bool setup_hedge(const api::orderman::OrderDefinition& parent, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition*) override;
|
||||
void send_hedge_if_new_fills(const api::orderman::OrderState&, const OrderStateUpdateEvent&) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,153 +0,0 @@
|
||||
#include "LeanOrder.h"
|
||||
|
||||
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "server/sparkd/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct LeanOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
spark::QuantityMeasure cross_threshold;
|
||||
uint32_t max_chase_ticks;
|
||||
|
||||
md::PriceSize opposite_at_inception;
|
||||
};
|
||||
|
||||
|
||||
LeanOrder::LeanOrder() : me(new PImpl()) {}
|
||||
|
||||
LeanOrder::~LeanOrder() {}
|
||||
|
||||
void LeanOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_lean();
|
||||
auto const &lean_def = this_def.custom_def().lean();
|
||||
*out_child->mutable_custom_def() = lean_def.child();
|
||||
|
||||
me->cross_threshold = lean_def.cross_threshold();
|
||||
me->max_chase_ticks = lean_def.max_chase_ticks();
|
||||
}
|
||||
|
||||
void LeanOrder::payup(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() + tick_size() * cross_ticks;
|
||||
double limit = me->opposite_at_inception.price() + tick_size() * me->max_chase_ticks;
|
||||
if(new_price <= limit)
|
||||
update_child(Price(new_price), Quantity(this_state().quantity()));
|
||||
else
|
||||
{
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
detach();
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void LeanOrder::paydown(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() - tick_size() * cross_ticks;
|
||||
double limit = me->opposite_at_inception.price() - tick_size() * me->max_chase_ticks;
|
||||
if(new_price >= limit)
|
||||
update_child(Price(new_price), Quantity(this_state().quantity()));
|
||||
else
|
||||
{
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
detach();
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
|
||||
void LeanOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
{
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
{
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
me->opposite_at_inception = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
else
|
||||
me->opposite_at_inception = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
report_state();
|
||||
}
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
Quantity thresh = SpreaderUtils::_quantity_threshold(me->cross_threshold, Quantity(this_state().quantity()));
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
{
|
||||
const auto& opposite = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if(opposite.price() < me->opposite_at_inception.price())
|
||||
me->opposite_at_inception = opposite; //your lucky day, try to get luckier
|
||||
else if(opposite.price() > me->opposite_at_inception.price())
|
||||
payup(opposite); //you missed, payup
|
||||
else
|
||||
{
|
||||
if(opposite.size() <= thresh.value())
|
||||
payup(opposite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& opposite = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if(opposite.price() > me->opposite_at_inception.price())
|
||||
me->opposite_at_inception = opposite;
|
||||
else if(opposite.price() < me->opposite_at_inception.price())
|
||||
paydown(opposite);
|
||||
else
|
||||
{
|
||||
if(opposite.size() <= thresh.value())
|
||||
paydown(opposite);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/orderman/orders/book_algos/BookAlgo.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//LeanOrder watches for the opposite inside to trade below a certain size.
|
||||
//If size goes below a threshold or the market moves away, LeanOrder Pays up once.
|
||||
//The process repeats until the algo has paid up a max of N ticks from the original opposite inside order.
|
||||
//If the market moves toward, the algo starts watching the new inside market and keeps going
|
||||
|
||||
class LeanOrder : public BookAlgo
|
||||
{
|
||||
public:
|
||||
LeanOrder();
|
||||
virtual ~LeanOrder();
|
||||
|
||||
protected:
|
||||
void setup_child(const api::orderman::OrderDefinition&, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
private:
|
||||
void payup(const md::PriceSize& opposite, uint32_t cross_ticks = 0) override;
|
||||
void paydown(const md::PriceSize& opposite, uint32_t cross_ticks = 0) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,192 +0,0 @@
|
||||
#include "TickOrder.h"
|
||||
|
||||
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "server/sparkd/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct TickOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
uint32_t tick_offset_on_deploy;
|
||||
spark::QuantityMeasure cross_threshold;
|
||||
uint32_t cross_ticks;
|
||||
uint32_t max_chase_ticks;
|
||||
bool detach_on_toward;
|
||||
|
||||
md::PriceSize opposite_at_inception;
|
||||
};
|
||||
|
||||
|
||||
TickOrder::TickOrder() : me(new PImpl()) {}
|
||||
|
||||
TickOrder::~TickOrder() {}
|
||||
|
||||
void TickOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_tick();
|
||||
auto const &tick_def = this_def.custom_def().tick();
|
||||
*out_child->mutable_custom_def() = tick_def.child();
|
||||
|
||||
me->tick_offset_on_deploy = tick_def.tick_offset_on_deploy();
|
||||
me->cross_threshold = tick_def.cross_threshold();
|
||||
me->cross_ticks = tick_def.cross_ticks();
|
||||
me->max_chase_ticks = tick_def.max_chase_ticks();
|
||||
me->cross_threshold = tick_def.cross_threshold();
|
||||
me->detach_on_toward = tick_def.detach_on_toward();
|
||||
}
|
||||
|
||||
void TickOrder::payup(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() + tick_size() * cross_ticks;
|
||||
double limit = me->opposite_at_inception.price() + tick_size() * me->max_chase_ticks;
|
||||
if(new_price <= limit)
|
||||
update_child(Price(new_price), Quantity(this_state().quantity()));
|
||||
else
|
||||
{
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
detach();
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void TickOrder::paydown(const md::PriceSize& opposite, uint32_t cross_ticks)
|
||||
{
|
||||
double new_price = opposite.price() - tick_size() * cross_ticks;
|
||||
double limit = me->opposite_at_inception.price() - tick_size() * me->max_chase_ticks;
|
||||
if(new_price >= limit)
|
||||
update_child(Price(new_price), Quantity(this_state().quantity()));
|
||||
else
|
||||
{
|
||||
update_child(Price(limit), Quantity(this_state().quantity()));
|
||||
detach();
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void TickOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
{
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
{
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
{
|
||||
me->opposite_at_inception = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if(me->tick_offset_on_deploy > 0)
|
||||
{
|
||||
double price = me->opposite_at_inception.price() - tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
else
|
||||
{
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
me->opposite_at_inception = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if(me->tick_offset_on_deploy > 0)
|
||||
{
|
||||
double price = me->opposite_at_inception.price() + tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
else
|
||||
{
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
Quantity thresh = SpreaderUtils::_quantity_threshold(me->cross_threshold, Quantity(this_state().quantity()));
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
{
|
||||
const auto& opposite = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if(opposite.price() < me->opposite_at_inception.price())
|
||||
{
|
||||
if(me->detach_on_toward)
|
||||
detach();
|
||||
else
|
||||
me->opposite_at_inception = opposite;
|
||||
}
|
||||
else if(opposite.price() > me->opposite_at_inception.price())
|
||||
payup(opposite, me->cross_ticks);
|
||||
else
|
||||
{
|
||||
if(opposite.size() <= thresh.value())
|
||||
payup(opposite, me->cross_ticks);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& opposite = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if(opposite.price() > me->opposite_at_inception.price())
|
||||
{
|
||||
if(me->detach_on_toward)
|
||||
detach();
|
||||
else
|
||||
me->opposite_at_inception = opposite;
|
||||
}
|
||||
else if(opposite.price() < me->opposite_at_inception.price())
|
||||
paydown(opposite, me->cross_ticks);
|
||||
else
|
||||
{
|
||||
if(opposite.size() <= thresh.value())
|
||||
paydown(opposite, me->cross_ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/orderman/orders/book_algos/BookAlgo.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//TickOrder deploys N ticks away from the initial opposite inside market and watches for it to trade below a certain size.
|
||||
//If that size goes below a threshold or the market moves away, TickOrder Pays up once.
|
||||
//The process repeats until the algo has paid up a max of N ticks from the original opposite inside order.
|
||||
//If the market moves toward, the algo either starts watching the new inside market and keeps going or detaches depending on a parameter
|
||||
|
||||
class TickOrder : public BookAlgo
|
||||
{
|
||||
public:
|
||||
TickOrder();
|
||||
virtual ~TickOrder();
|
||||
|
||||
protected:
|
||||
void setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
private:
|
||||
void payup(const md::PriceSize& opposite, uint32_t cross_ticks) override;
|
||||
void paydown(const md::PriceSize& opposite, uint32_t cross_ticks) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,154 +0,0 @@
|
||||
#include "TopOrder.h"
|
||||
|
||||
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "server/sparkd/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include <google/protobuf/timestamp.pb.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
|
||||
struct TopOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
uint32_t watch_level_tick_offset;
|
||||
spark::QuantityMeasure top_threshold;
|
||||
bool enable_hedge;
|
||||
int hedge_ticks;
|
||||
OrderDefinition hedge_def;
|
||||
};
|
||||
|
||||
|
||||
TopOrder::TopOrder() : me(new PImpl()) {}
|
||||
|
||||
TopOrder::~TopOrder() {}
|
||||
|
||||
void TopOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_top();
|
||||
auto const &top_def = this_def.custom_def().top();
|
||||
*out_child->mutable_custom_def() = top_def.child();
|
||||
|
||||
me->watch_level_tick_offset = top_def.watch_level_tick_offset();
|
||||
me->top_threshold = top_def.top_threshold();
|
||||
me->enable_hedge = top_def.enable_hedge();
|
||||
me->hedge_ticks = top_def.hedge_ticks();
|
||||
}
|
||||
|
||||
bool TopOrder::setup_hedge(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition* out_hedge)
|
||||
{
|
||||
auto const &top_def = this_def.custom_def().top();
|
||||
if(me->enable_hedge && top_def.has_hedge())
|
||||
{
|
||||
*out_hedge->mutable_custom_def() = top_def.hedge();
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
out_hedge->set_side(api::SIDE_SELL);
|
||||
else
|
||||
out_hedge->set_side(api::SIDE_BUY);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void TopOrder::send_hedge_if_new_fills(const api::orderman::OrderState& orig_state, const OrderStateUpdateEvent& new_state)
|
||||
{
|
||||
uint32_t qty = new_state.state.filled_quantity() - orig_state.filled_quantity();
|
||||
if(qty > 0)
|
||||
{
|
||||
double prx = new_state.state.last_price();
|
||||
if(definition().side() == api::SIDE_BUY) //if this is a buy, I'm going to sell my hedge, nticks higher than the fill price
|
||||
prx = prx + tick_size() * me->hedge_ticks;
|
||||
else
|
||||
prx = prx - tick_size() * me->hedge_ticks;
|
||||
build_and_send_hedge(Price(prx), Quantity(qty));
|
||||
}
|
||||
}
|
||||
|
||||
void TopOrder::on_book_update(const bts::orderman::BookUpdateEvent& book)
|
||||
{
|
||||
switch (this_state().status())
|
||||
{
|
||||
case ORDER_STATUS_WORKING:
|
||||
switch (child_state().status())
|
||||
{
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
{
|
||||
activate_child(Price(this_state().price()), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
report_state();
|
||||
}
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
Quantity thresh = SpreaderUtils::_quantity_threshold(me->top_threshold, Quantity(this_state().quantity()));
|
||||
if(definition().side() == api::SIDE_BUY)
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
if(securities::test_price_equality(this_state().price(), same.price(), tick_size()))
|
||||
{
|
||||
if(same.size() <= thresh.value())
|
||||
{
|
||||
cancel_child();
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
report_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
if(securities::test_price_equality(this_state().price(), same.price(), tick_size()))
|
||||
{
|
||||
if(same.size() <= thresh.value())
|
||||
{
|
||||
cancel_child();
|
||||
transition(ORDER_STATUS_CANCELED);
|
||||
report_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "server/sparkd/orderman/orders/book_algos/BookAlgo.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//If TopOrder becomes the top of book, it cancels if there is less than some threshold of size joined with it.
|
||||
//Fills optionally trigger hedge orders to the opposite side of the market, n ticks away.
|
||||
|
||||
class TopOrder : public BookAlgo
|
||||
{
|
||||
public:
|
||||
TopOrder();
|
||||
virtual ~TopOrder();
|
||||
|
||||
protected:
|
||||
void setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState&, api::orderman::OrderDefinition*) override;
|
||||
bool setup_hedge(const api::orderman::OrderDefinition& parent, const PreActivationOrderServices&,
|
||||
api::orderman::OrderDefinition*) override;
|
||||
void send_hedge_if_new_fills(const api::orderman::OrderState&, const OrderStateUpdateEvent&) override;
|
||||
void on_book_update(const bts::orderman::BookUpdateEvent&) override;
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -15,7 +15,7 @@ SpreaderOrder::SpreaderOrder()
|
||||
_cancelled (false),
|
||||
_cancel_timeout (5000),// TODO:get it from config
|
||||
_active(true),
|
||||
//_bookkeeping_data_ptr(nullptr),
|
||||
_bookkeeping_data_ptr(nullptr),
|
||||
_order_services(nullptr),
|
||||
_reported_state(),
|
||||
_state_worker(nullptr),
|
||||
|
||||
@@ -52,7 +52,7 @@ private:
|
||||
// is false if created from disk
|
||||
const bool _active;
|
||||
// this is the main data
|
||||
std::unique_ptr<SpreaderBookkeepingData> _bookkeeping_data_ptr;
|
||||
SpreaderBookkeepingData* _bookkeeping_data_ptr;
|
||||
// pointer kept to send reports
|
||||
SpreaderOrderServices* _order_services;
|
||||
// state thats sent out in report (its kept here vs creating a new one everytime)
|
||||
@@ -68,10 +68,6 @@ private:
|
||||
/**************************Cons/Dest*******************************/
|
||||
public:
|
||||
SpreaderOrder();
|
||||
SpreaderOrder(const SpreaderOrder&)=delete;
|
||||
SpreaderOrder(SpreaderOrder&&)=delete;
|
||||
SpreaderOrder& operator=(const SpreaderOrder&) = delete;
|
||||
SpreaderOrder& operator=(SpreaderOrder&&) = delete;
|
||||
virtual ~SpreaderOrder();
|
||||
/**************************Order Derived***************************/
|
||||
public:
|
||||
|
||||
@@ -73,23 +73,21 @@ public:
|
||||
SingleFillHedger (SingleFillHedger&&);
|
||||
SingleFillHedger& operator=(const SingleFillHedger&) = delete;
|
||||
SingleFillHedger& operator=(SingleFillHedger&&) = delete;
|
||||
// the a-f actions are associated with the state machine
|
||||
// side effect actions
|
||||
//a. leave the start state and submit the order
|
||||
//1. leave the start state and submit the order
|
||||
void init();
|
||||
//b. called by the spreader when there is an order update
|
||||
//2. called by the spreader when there is an order update
|
||||
// reported by the exchange
|
||||
void on_order_update(const SpreaderChildOrderState& order_update_message);
|
||||
//c. called when the market data associated
|
||||
//3. called when the market data associated
|
||||
// with this security is changed
|
||||
void on_book(bts::orderman::TickToTradeCorID corid, const bts::orderman::Book& book);
|
||||
//d. called when the hedge order is cancelled, either
|
||||
//4. called when the hedge order is cancelled, either
|
||||
// manually or automatically
|
||||
void cancel_hedge();
|
||||
//e. immediatly terminates this hedger, without any attempts
|
||||
//5. immediatly terminates this hedger, without any attempts
|
||||
// to cancel the order
|
||||
void terminate ();
|
||||
//f. on the engine infrastructure time
|
||||
//6. on the engine infrastructure time
|
||||
void on_time(const bts::TimeStamp&);
|
||||
// returns true if the quoter is in the final state, from which
|
||||
// it will never come back
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
SingleLegQuoter::SingleLegQuoter(ISpreaderManager& spreader,
|
||||
const LegPropertyData& this_leg_property,
|
||||
LegPropertyData this_leg_property,
|
||||
size_t n_spreader_legs,
|
||||
SpreaderChildOrderState& child_order_state)
|
||||
:
|
||||
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
const bool _two_legged;
|
||||
const bool _active_quoting;
|
||||
ISpreaderManager& _spreader;
|
||||
const LegPropertyData& _this_leg_data;
|
||||
const LegPropertyData _this_leg_data;
|
||||
const size_t _n_spreader_legs;
|
||||
SpreaderChildOrderState& _child_order_state;
|
||||
// computing the quoting price/quantity for multiple
|
||||
@@ -60,43 +60,41 @@ private:
|
||||
QuoterEndStateWorker _end_state_worker;
|
||||
public:
|
||||
SingleLegQuoter(ISpreaderManager& spreader,
|
||||
const LegPropertyData& this_leg_property,
|
||||
LegPropertyData this_leg_property,
|
||||
size_t n_spreader_legs,
|
||||
SpreaderChildOrderState& child_order_state);
|
||||
public:
|
||||
SingleLegQuoter() = delete;
|
||||
SingleLegQuoter(SingleLegQuoter&&); // implementation has an assert since it will not be called
|
||||
SingleLegQuoter(const SingleLegQuoter&) = delete;
|
||||
SingleLegQuoter (SingleLegQuoter&&);
|
||||
SingleLegQuoter (const SingleLegQuoter&) = delete;
|
||||
SingleLegQuoter& operator=(const SingleLegQuoter&) = delete;
|
||||
SingleLegQuoter& operator=(SingleLegQuoter&&) = delete;
|
||||
public:
|
||||
// the a-h actions are associated with the state machine
|
||||
// side effect actions
|
||||
//a. this is called only once. this could have been passed in
|
||||
//1. this is called only once. this all could have been passed in
|
||||
// the constructor, but the spreader is created in 2 stages.
|
||||
// all the memory is allocated in a slow path and then values
|
||||
// are initialized on fast activation path
|
||||
void one_time_activate (Price, Quantity);
|
||||
//b. called by the spreader a change in
|
||||
//2. called by the spreader a change in
|
||||
// order status(or fills) is reported
|
||||
// by the exchange
|
||||
// by the exchange
|
||||
void update_order_status(const SpreaderChildOrderState& u);
|
||||
//c. called when the market data associated
|
||||
//3. called when the market data associated
|
||||
// with this security is changed
|
||||
void on_book(bts::orderman::TickToTradeCorID cid, size_t leg_index, const bts::orderman::Book&);
|
||||
//d. called when the order is cancelled, either
|
||||
//4. called when the order is cancelled, either
|
||||
// manually or automatically
|
||||
void cancel_order();
|
||||
//e. the quoter forgets everything and goes to the end state
|
||||
//5. the quoter forgets everything and goes to the end state
|
||||
void terminate ();
|
||||
//f. time update
|
||||
//6. time update
|
||||
void on_time(const bts::TimeStamp&);
|
||||
//g. this is called by the spreader when a quoting fill
|
||||
//7. this is called by the spreader when a quoting fill
|
||||
// (ie not hedging fill) happens on one of the quoting
|
||||
// legs
|
||||
void update_other_leg_fills(size_t leg_index, Quantity filled_quantity);
|
||||
//h. called when the user modified the spreader order price or quantity
|
||||
//8. called when the user modified the spreader order price or quantity
|
||||
void update_order_price_quantity(Price price, Quantity quantity);
|
||||
// true if the quoter has an active working order. the order can
|
||||
// be in an unknown state, but as long as an order has been sent
|
||||
@@ -127,8 +125,8 @@ public:
|
||||
private:
|
||||
bts::orderman::OrderKey _key() {return _child_order_state.order_key();}
|
||||
TimeStamp _current_time () const {return _spreader.current_time();}
|
||||
// TODO: state out and move the string to a const
|
||||
std::string _log_prefix(){
|
||||
std::string _log_prefix()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << " spreader_order_key: " << _spreader.spreader_order_key();
|
||||
oss << " sec_key: " << _this_leg_data.security_key();
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ void SpreaderEndStateWorker::on_init_recovered(const bts::api::orderman::OrderRe
|
||||
void SpreaderEndStateWorker::on_activate(Price price, Quantity quantity,
|
||||
SpreaderOrderServices&){
|
||||
// this can only happen if the security could not be resolved
|
||||
// resulting in START->END. however on_active could still
|
||||
// resulting in START->END. however on_active will could still
|
||||
// be called
|
||||
bts_assert (!_spreader->_bookkeeping_data().all_legs_resolved());
|
||||
_spreader->_bookkeeping_data().update_order(price, quantity);
|
||||
|
||||
+10
-11
@@ -6,27 +6,26 @@ namespace orderman {
|
||||
|
||||
class SpreaderOrder;
|
||||
|
||||
class SpreaderEndStateWorker : public ISpreaderStateWorker{
|
||||
class SpreaderEndStateWorker : public ISpreaderStateWorker
|
||||
{
|
||||
private:
|
||||
SpreaderOrder* const _spreader;
|
||||
public:
|
||||
SpreaderEndStateWorker (SpreaderOrder*);
|
||||
void on_transition() override;
|
||||
SpreaderState state() const override;
|
||||
void on_transition() override;
|
||||
void on_init_new(const api::orderman::OrderDefinition&,
|
||||
const SpreaderPreActivationOrderServices&) override;
|
||||
void on_init_recovered(const bts::api::orderman::OrderRecord&,
|
||||
const SpreaderPreActivationOrderServices&) override;
|
||||
void on_init_new(api::orderman::OrderDefinition const&, const SpreaderPreActivationOrderServices&) override;
|
||||
void on_init_recovered(const bts::api::orderman::OrderRecord&, const SpreaderPreActivationOrderServices&) override;
|
||||
void on_activate(Price, Quantity, SpreaderOrderServices&) override;
|
||||
void on_update(Price, Quantity) override;
|
||||
void on_cancel() override;
|
||||
void on_book(TickToTradeCorID, const Book&) override;
|
||||
void on_book(bts::orderman::TickToTradeCorID, const bts::orderman::Book&) override;
|
||||
void on_time(const TimeStamp&) override;
|
||||
void on_quoter_order_error(SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_fill(SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_status_update(SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_error(bts::orderman::SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_fill(bts::orderman::SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_status_update(bts::orderman::SpreaderLegData& leg_data) override;
|
||||
void on_hedger_order_error(SingleFillHedger&) override;
|
||||
void on_hedger_order_update(SingleFillHedger&) override;
|
||||
void on_check_termination() override;
|
||||
void on_check_termination () override;
|
||||
};
|
||||
}}
|
||||
+3
-3
@@ -22,10 +22,10 @@ void SpreaderStartStateWorker::on_init_new(const api::orderman::OrderDefinition&
|
||||
bts_assert(!_spreader->_inited());
|
||||
std::vector<LegPropertyData> spreader_legs;
|
||||
if (SpreaderUtils::resolve_synthetic_spread (spreader_pao_services, order_def, spreader_legs, false))
|
||||
_spreader->_bookkeeping_data_ptr = std::make_unique<SpreaderBookkeepingData>(*_spreader, _spreader->key(), order_def, spreader_pao_services, spreader_legs);
|
||||
_spreader->_bookkeeping_data_ptr = new SpreaderBookkeepingData (*_spreader, _spreader->key(), order_def, spreader_pao_services, spreader_legs);
|
||||
else
|
||||
{
|
||||
_spreader->_bookkeeping_data_ptr = std::make_unique<SpreaderBookkeepingData>(*_spreader, _spreader->key(), order_def, spreader_pao_services, std::vector<LegPropertyData>());
|
||||
_spreader->_bookkeeping_data_ptr = new SpreaderBookkeepingData (*_spreader, _spreader->key(), order_def, spreader_pao_services, std::vector<LegPropertyData>());
|
||||
// if securities can not be resolved, the order is terminated
|
||||
LOG(OrderMan, Error, "SpreaderStartStateWorker.on_init_new key: {} , could not resolve securities", _spreader->key().value());
|
||||
std::string msg = "Could not resolve securities";
|
||||
@@ -36,7 +36,7 @@ void SpreaderStartStateWorker::on_init_recovered(const bts::api::orderman::Order
|
||||
{
|
||||
bts_assert(!_spreader->_inited());
|
||||
// we are not handling this yet, so lets send it directly to END
|
||||
_spreader->_bookkeeping_data_ptr = std::make_unique<SpreaderBookkeepingData>(*_spreader, _spreader->key(), order_record.def(), spreader_pao_services, std::vector<LegPropertyData>());
|
||||
_spreader->_bookkeeping_data_ptr = new SpreaderBookkeepingData (*_spreader, _spreader->key(), order_record.def(), spreader_pao_services, std::vector<LegPropertyData>());
|
||||
LOG(OrderMan, Warn, "SpreaderStartStateWorker.on_init_recovered key: {} , going to END", _spreader->key().value());
|
||||
std::string msg = "Recovered spreader orders and not supported";
|
||||
_spreader->_transform_to_END (&msg);
|
||||
|
||||
+39
-24
@@ -9,47 +9,50 @@ SpreaderWorkingStateWorker::SpreaderWorkingStateWorker (SpreaderOrder* spreader)
|
||||
:
|
||||
_spreader(spreader)
|
||||
{}
|
||||
SpreaderState SpreaderWorkingStateWorker::state() const {
|
||||
return SpreaderState::WORKING;
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_transition(){
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_init_new(api::orderman::OrderDefinition const&,
|
||||
const SpreaderPreActivationOrderServices&){
|
||||
SpreaderState SpreaderWorkingStateWorker::state() const {return SpreaderState::WORKING;}
|
||||
void SpreaderWorkingStateWorker::on_transition()
|
||||
{}
|
||||
void SpreaderWorkingStateWorker::on_init_new(api::orderman::OrderDefinition const&, const SpreaderPreActivationOrderServices&)
|
||||
{
|
||||
bts_assert_msg (false, "SpreaderWorkingStateWorker::on_init_new is an illegal operation");
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_init_recovered(const bts::api::orderman::OrderRecord&,
|
||||
const SpreaderPreActivationOrderServices&){
|
||||
void SpreaderWorkingStateWorker::on_init_recovered(const bts::api::orderman::OrderRecord&, const SpreaderPreActivationOrderServices&)
|
||||
{
|
||||
bts_assert_msg (false, "SpreaderWorkingStateWorker::on_init_recovered is an illegal operation");
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_activate(Price, Quantity,
|
||||
SpreaderOrderServices&){
|
||||
void SpreaderWorkingStateWorker::on_activate(Price, Quantity, SpreaderOrderServices&)
|
||||
{
|
||||
bts_assert_msg (false, "SpreaderWorkingStateWorker::on_activate is an illegal operation");
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_update(Price price, Quantity quantity){
|
||||
if (_spreader->_quoters_can_accept_modification()){
|
||||
void SpreaderWorkingStateWorker::on_update(Price price, Quantity quantity)
|
||||
{
|
||||
if (_spreader->_quoters_can_accept_modification())
|
||||
{
|
||||
_spreader->_set_price_quantity (price, quantity);
|
||||
// updating all active quoters, nothing to do with the hedgers
|
||||
for (size_t q_idx = 0; q_idx < _spreader->_bookkeeping_data().n_legs(); q_idx++){
|
||||
for (size_t q_idx = 0; q_idx < _spreader->_bookkeeping_data().n_legs(); q_idx++)
|
||||
{
|
||||
SpreaderLegData& leg_data = _spreader->_bookkeeping_data().get_leg_data(q_idx);
|
||||
if (leg_data.leg_property_data().is_active_quoting())
|
||||
leg_data.quoter().update_order_price_quantity (price, quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_cancel(){
|
||||
void SpreaderWorkingStateWorker::on_cancel()
|
||||
{
|
||||
_spreader->_cancel_orders (true, true);
|
||||
_spreader->_transform_to_WAITING_FOR_CANCEL_ALL ();
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_book(TickToTradeCorID cid,
|
||||
const Book& book){
|
||||
void SpreaderWorkingStateWorker::on_book(bts::orderman::TickToTradeCorID cid, const bts::orderman::Book& book)
|
||||
{
|
||||
SpreaderBookkeepingData& bookkeeping_data = _spreader->_bookkeeping_data();
|
||||
// this is a key->index lookup
|
||||
SpreaderLegData& md_leg = bookkeeping_data.get_leg_data(book.security_key());
|
||||
// updating md state (this is the only place where its done)
|
||||
md_leg.update_book(book);
|
||||
// sending md to all quoters
|
||||
for (size_t q_idx = 0; q_idx < bookkeeping_data.n_legs(); q_idx++){
|
||||
for (size_t q_idx = 0; q_idx < bookkeeping_data.n_legs(); q_idx++)
|
||||
{
|
||||
SpreaderLegData& d = bookkeeping_data.get_leg_data(q_idx);
|
||||
if (d.leg_property_data().is_active_quoting())
|
||||
d.quoter().on_book(cid, md_leg.leg_property_data().leg_index(), book);
|
||||
@@ -58,9 +61,11 @@ void SpreaderWorkingStateWorker::on_book(TickToTradeCorID cid,
|
||||
for (size_t h_idx = 0; h_idx < md_leg.n_hedgers(); h_idx++)
|
||||
md_leg.get_hedger(h_idx).on_book(cid, book);
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_time(const TimeStamp& ts){
|
||||
void SpreaderWorkingStateWorker::on_time(const TimeStamp& ts)
|
||||
{
|
||||
SpreaderBookkeepingData& bookkeeping_data = _spreader->_bookkeeping_data();
|
||||
for (size_t q_idx = 0; q_idx < bookkeeping_data.n_legs(); q_idx++){
|
||||
for (size_t q_idx = 0; q_idx < bookkeeping_data.n_legs(); q_idx++)
|
||||
{
|
||||
SpreaderLegData& leg_data = bookkeeping_data.get_leg_data(q_idx);
|
||||
if (leg_data.leg_property_data().is_active_quoting())
|
||||
leg_data.quoter().on_time(ts);
|
||||
@@ -68,13 +73,15 @@ void SpreaderWorkingStateWorker::on_time(const TimeStamp& ts){
|
||||
leg_data.get_hedger(h_idx).on_time (ts);
|
||||
}
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_quoter_order_error(SpreaderLegData& leg_data){
|
||||
void SpreaderWorkingStateWorker::on_quoter_order_error(bts::orderman::SpreaderLegData& leg_data)
|
||||
{
|
||||
// this is because the error message could also have status changes
|
||||
leg_data.quoter().update_order_status (leg_data.order_update_message());
|
||||
_spreader->_cancel_orders (true, true);
|
||||
_spreader->_transform_to_WAITING_FOR_CANCEL_ALL ();
|
||||
}
|
||||
void SpreaderWorkingStateWorker::on_quoter_order_fill(SpreaderLegData& leg_data){
|
||||
void SpreaderWorkingStateWorker::on_quoter_order_fill(bts::orderman::SpreaderLegData& leg_data)
|
||||
{
|
||||
// this is the most complex part of the spreader. the following needs to be done
|
||||
// 1. tell other quoters about it (so they reduce their sizes)
|
||||
// 2. launch a new hedger
|
||||
@@ -94,9 +101,11 @@ void SpreaderWorkingStateWorker::on_quoter_order_fill(SpreaderLegData& leg_data)
|
||||
LOG(OrderMan, Info, "SpreaderWorkingStateWorker::on_quoter_order_fill order: key: {} order_key: {} status: {} filled_qty: {} last_price: {}",
|
||||
_spreader->key(), order_update_message.order_key(), order_update_message.status(), order_update_message.last_update_filled_quantity(), order_update_message.last_update_filled_price());
|
||||
bts_assert (order_update_message.last_update_filled_quantity().value() > 0);
|
||||
for (size_t q_idx = 0; q_idx < _spreader->_bookkeeping_data().n_legs(); q_idx++){
|
||||
for (size_t q_idx = 0; q_idx < _spreader->_bookkeeping_data().n_legs(); q_idx++)
|
||||
{
|
||||
SpreaderLegData& other_leg_data = _spreader->_bookkeeping_data().get_leg_data (q_idx);
|
||||
if (q_idx != leg_data.leg_property_data().leg_index()){
|
||||
if (q_idx != leg_data.leg_property_data().leg_index())
|
||||
{
|
||||
// if it is an actively quoting leg, inform the quoter about it
|
||||
// so it can decrease the proportional size
|
||||
if (other_leg_data.leg_property_data().is_active_quoting())
|
||||
@@ -131,4 +140,10 @@ void SpreaderWorkingStateWorker::on_check_termination()
|
||||
if (_spreader->_all_quoter_orders_in_terminal_state() && _spreader->_all_hedger_orders_in_terminal_state())
|
||||
_spreader->_transform_to_END ();
|
||||
}
|
||||
bool SpreaderWorkingStateWorker::_is_leg_filled (size_t leg_idx) const
|
||||
{
|
||||
const SpreaderLegData& leg_data = _spreader->_bookkeeping_data().get_leg_data(leg_idx);
|
||||
Quantity leg_target_qty = SpreaderUtils::get_leg_quantity_from_spreader_quantity (leg_data.leg_property_data(), _spreader->_bookkeeping_data().quantity());
|
||||
return leg_data.total_fills().value() >= leg_target_qty.value();
|
||||
}
|
||||
}}
|
||||
+10
-11
@@ -14,22 +14,21 @@ public:
|
||||
SpreaderWorkingStateWorker (SpreaderOrder*);
|
||||
void on_transition() override;
|
||||
SpreaderState state() const override;
|
||||
void on_init_new(api::orderman::OrderDefinition const&,
|
||||
const SpreaderPreActivationOrderServices&) override;
|
||||
void on_init_recovered(const bts::api::orderman::OrderRecord&,
|
||||
const SpreaderPreActivationOrderServices&) override;
|
||||
void on_activate(Price, Quantity,
|
||||
SpreaderOrderServices&) override;
|
||||
void on_init_new(api::orderman::OrderDefinition const&, const SpreaderPreActivationOrderServices&) override;
|
||||
void on_init_recovered(const bts::api::orderman::OrderRecord&, const SpreaderPreActivationOrderServices&) override;
|
||||
void on_activate(Price, Quantity, SpreaderOrderServices&) override;
|
||||
void on_update(Price, Quantity) override;
|
||||
void on_cancel() override;
|
||||
void on_book(TickToTradeCorID,
|
||||
const Book&) override;
|
||||
void on_book(bts::orderman::TickToTradeCorID, const bts::orderman::Book&) override;
|
||||
void on_time(const TimeStamp&) override;
|
||||
void on_quoter_order_error(SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_fill(SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_status_update(SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_error(bts::orderman::SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_fill(bts::orderman::SpreaderLegData& leg_data) override;
|
||||
void on_quoter_order_status_update(bts::orderman::SpreaderLegData& leg_data) override;
|
||||
void on_hedger_order_error(SingleFillHedger&) override;
|
||||
void on_hedger_order_update(SingleFillHedger&) override;
|
||||
void on_check_termination () override;
|
||||
private:
|
||||
bool _is_leg_filled (size_t leg_idx) const;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -30,7 +30,7 @@ def print_traces(last,traces):
|
||||
if last in in_rec and "zero" in in_rec:
|
||||
first = in_rec["zero"]
|
||||
shot = in_rec[last]
|
||||
o
|
||||
if shot-first < 500000:
|
||||
print "%d -> %d" % (k, shot-first)
|
||||
previous = first
|
||||
for key, value in sorted(in_rec.iteritems(), key=lambda (k,v): (v,k)):
|
||||
@@ -57,26 +57,25 @@ def transform(delta_exps,raw_events):
|
||||
|
||||
exps = parse_exps(sys.argv[1:])
|
||||
traces = parse_log()
|
||||
print traces
|
||||
# print_traces("SocketConnection_write_pre_write",traces)
|
||||
# times = transform(exps,traces)
|
||||
print_traces("SocketConnection_write_pre_write",traces)
|
||||
times = transform(exps,traces)
|
||||
|
||||
# clean_times = []
|
||||
# for t in times:
|
||||
# if t['t'] < 500000:
|
||||
# clean_times.append(t)
|
||||
clean_times = []
|
||||
for t in times:
|
||||
if t['t'] < 500000:
|
||||
clean_times.append(t)
|
||||
|
||||
# frame = DataFrame(clean_times)
|
||||
frame = DataFrame(clean_times)
|
||||
# print "Overview (all data)"
|
||||
# print "--------------------"
|
||||
# print frame
|
||||
# print
|
||||
|
||||
# frame = frame.drop('id', 1)
|
||||
# print "Medians (all data)"
|
||||
# print "------------------"
|
||||
# print frame.median()
|
||||
# print
|
||||
frame = frame.drop('id', 1)
|
||||
print "Medians (all data)"
|
||||
print "------------------"
|
||||
print frame.median()
|
||||
print
|
||||
|
||||
# print "StdDevs (all data)"
|
||||
# print "------------------"
|
||||
@@ -103,5 +102,3 @@ print traces
|
||||
# print "StdDevs (clean data)"
|
||||
# print "--------------------"
|
||||
# print frame.std()
|
||||
|
||||
Python finished at Tue Jun 5 14:54:09
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
./parse_log.sh $1 | ./analyze.py t=SocketConnection_write_pre_write-MDSource
|
||||
./parse_log.sh ~/sparkdata/data/logging/log | ./analyze.py t=SocketConnection_write_pre_write-zero
|
||||
@@ -1,2 +1,3 @@
|
||||
#!/bin/bash
|
||||
cat $1 | grep "|Latency" | awk -F "|" '{print $9}' | awk '{print $2,$3,$1}'
|
||||
cat $1 | grep "| Latency" | awk '{ print $6,$7,$5 }' | sort -n
|
||||
|
||||
@@ -9,8 +9,39 @@
|
||||
|
||||
#include <lttng/tracepoint.h>
|
||||
|
||||
TRACEPOINT_EVENT(bts_trace, md,
|
||||
TP_ARGS(char *, text, long, security),
|
||||
TP_FIELDS(
|
||||
ctf_string(msg, text)
|
||||
ctf_integer_hex(long, sec,security)
|
||||
))
|
||||
|
||||
TRACEPOINT_EVENT(bts_trace, trace_order,
|
||||
TRACEPOINT_EVENT(bts_trace, eye,
|
||||
TP_ARGS(char *, text, long, security),
|
||||
TP_FIELDS(
|
||||
ctf_string(msg, text)
|
||||
ctf_integer_hex(long, sec,security)
|
||||
))
|
||||
|
||||
TRACEPOINT_EVENT(bts_trace, btscmd,
|
||||
TP_ARGS(char *, text),
|
||||
TP_FIELDS(
|
||||
ctf_string(msg, text)
|
||||
))
|
||||
|
||||
TRACEPOINT_EVENT(bts_trace, o2,
|
||||
TP_ARGS(char *, text),
|
||||
TP_FIELDS(
|
||||
ctf_string(msg, text)
|
||||
))
|
||||
|
||||
TRACEPOINT_EVENT(bts_trace,quickfix,
|
||||
TP_ARGS(char *, text),
|
||||
TP_FIELDS(
|
||||
ctf_string(msg, text)
|
||||
))
|
||||
|
||||
TRACEPOINT_EVENT(bts_trace, quoter,
|
||||
TP_ARGS(char *, text, long long, argument1, long long, argument2),
|
||||
TP_FIELDS(
|
||||
ctf_string(msg, text)
|
||||
@@ -18,6 +49,18 @@ TRACEPOINT_EVENT(bts_trace, trace_order,
|
||||
ctf_integer(long long, arg2,argument2)
|
||||
))
|
||||
|
||||
TRACEPOINT_EVENT(bts_trace, driver,
|
||||
TP_ARGS(char *, text, long long, arg1,long long, sec_key,float, bid, float, ask, int, bid_sz,int, ask_sz, int, seq_n),
|
||||
TP_FIELDS(
|
||||
ctf_string(msg, text)
|
||||
ctf_integer(long long, arg1,arg1)
|
||||
ctf_integer(long long, sec_key,sec_key)
|
||||
ctf_float(float, bid,bid)
|
||||
ctf_float(float, ask,ask)
|
||||
ctf_integer(int, bid_sz,bid_sz)
|
||||
ctf_integer(int, ask_sz,ask_sz)
|
||||
ctf_integer (int,seq_n,seq_n)
|
||||
))
|
||||
#endif /* _BTS_TP_H */
|
||||
|
||||
#include <lttng/tracepoint-event.h>
|
||||
@@ -17,6 +17,7 @@ echo OUT=$OUT
|
||||
rm -rf $OUT/api
|
||||
mkdir -p $OUT/api
|
||||
mkdir -p $OUT/api/fable
|
||||
# BUILD_DIR
|
||||
|
||||
if [ "$#" -gt 0 ]
|
||||
then
|
||||
|
||||
@@ -55,12 +55,4 @@ input[type=text].login-input, input[type=password].login-input
|
||||
vertical-align: middle
|
||||
|
||||
table .fabutton
|
||||
margin-top: 0.9ex
|
||||
|
||||
.no-border
|
||||
border: 0
|
||||
td
|
||||
border: 0
|
||||
th
|
||||
border: 0
|
||||
|
||||
margin-top: 0.9ex
|
||||
@@ -20,22 +20,19 @@ let init (context:ServiceContext.ServiceContext) =
|
||||
mdRelayProductData = [||]
|
||||
mdRelayExpNameData = Array.empty<ExpirationNameList>
|
||||
scratch = emptyAccount
|
||||
previousScratch = emptyAccount
|
||||
AllValue = " "
|
||||
AllValueDisplay = "All"
|
||||
}, Access.getAdminData admin UpdateAdminData NoAdminData
|
||||
|
||||
let existingSelection (scratch:BTSAccount) (previousScratch:BTSAccount) (data:AdminData) =
|
||||
printf "%O" data.accounts
|
||||
let existingSelection (scratch:BTSAccount) (data:AdminData) =
|
||||
List.choose id [
|
||||
data.accounts |> Array.tryFind (fun a -> a.name = scratch.name)
|
||||
data.accounts |> Array.tryFind (fun a -> a.name = previousScratch.name)
|
||||
data.accounts |> Array.tryHead
|
||||
Some emptyAccount]
|
||||
|> List.head
|
||||
|
||||
let preserveSelection (data:AdminData) model =
|
||||
let account = existingSelection model.scratch model.previousScratch data
|
||||
let account = existingSelection model.scratch data
|
||||
{ model with scratch = account }
|
||||
|
||||
|
||||
@@ -101,7 +98,7 @@ let erase (arr:'a[]) i =
|
||||
let update msg (model:Model) : Model * Cmd<Msg> =
|
||||
match msg with
|
||||
| UpdateAdminData data ->
|
||||
let presentAccounts = data.accounts |> Array.filter (fun a -> a.isPresent) |> Array.sortBy (fun a -> a.name.ToLower())
|
||||
let presentAccounts = data.accounts |> Array.filter (fun a -> a.isPresent) |> Array.sortBy (fun a -> a.name)
|
||||
let data = { data with accounts = presentAccounts }
|
||||
{ model with adminData = Some data }
|
||||
|> preserveSelection data, Cmd.ofExistingPromise (requestProducts model.mdRelay) id (box>>ErrorOccured)
|
||||
@@ -110,14 +107,14 @@ let update msg (model:Model) : Model * Cmd<Msg> =
|
||||
model, Cmd.Empty
|
||||
| SelectAccount account ->
|
||||
// When an account is selected 'scratch' account is discarded
|
||||
{ model with previousScratch = model.scratch; scratch = account }, Cmd.Empty
|
||||
{ model with scratch = account }, Cmd.Empty
|
||||
| DeleteAccount account ->
|
||||
match Fable.Import.Browser.window.confirm (sprintf "Are you sure you want to delete '%s'" account.name) with
|
||||
| true -> model, Cmd.ofExistingPromise (deleteAccount model.admin account) id (box>>ErrorOccured)
|
||||
| false -> model, Cmd.Empty
|
||||
| AddAccount ->
|
||||
// Create a new scratch account. Send to server after user has filled in details
|
||||
{ model with previousScratch = model.scratch; scratch = emptyAccount }, Cmd.ofMsg <| SelectTab General
|
||||
{ model with scratch = emptyAccount }, Cmd.ofMsg <| SelectTab General
|
||||
| SaveScratch ->
|
||||
match isNewAccount model.scratch with
|
||||
| true -> model, Cmd.ofExistingPromise (createAccount model.admin model.scratch) id (box>>ErrorOccured)
|
||||
|
||||
@@ -28,7 +28,6 @@ type Model = {
|
||||
mdRelayProductData: Product[]
|
||||
mdRelayExpNameData: ExpirationNameList[]
|
||||
scratch : BTSAccount
|
||||
previousScratch : BTSAccount
|
||||
AllValue: string
|
||||
AllValueDisplay: string
|
||||
}
|
||||
|
||||
+95
-102
@@ -61,29 +61,12 @@ let mappedDataGridWithAddDelete<'a,'r> title (descriptor:MappedDataGridDescripto
|
||||
//
|
||||
let generalView model dispatch =
|
||||
let account = model.scratch
|
||||
|
||||
let revertFun =
|
||||
match model.adminData with
|
||||
| Some data -> fun _ -> (dispatch <| SelectAccount (State.existingSelection model.scratch model.previousScratch data))
|
||||
| None -> fun _ -> (dispatch <| SelectAccount emptyAccount)
|
||||
|
||||
let modal = Modal.showCreate
|
||||
"Create Account"
|
||||
(div [] [
|
||||
Fields.text "Name" account.name [] (AccountName >> Update >> dispatch)
|
||||
Fields.text "Description" account.description [] (Description >> Update >> dispatch)
|
||||
Fields.checkbox "Active" "" account.isActive [] (IsActive >> Update >> dispatch)
|
||||
])
|
||||
(fun _ -> dispatch SaveScratch)
|
||||
revertFun
|
||||
|
||||
div [] [
|
||||
p [ Style [FontWeight "Bold"] ] [ str ("ID: " + (string account.id))]
|
||||
br []
|
||||
Fields.text "Name" account.name [] (AccountName >> Update >> dispatch)
|
||||
Fields.text "Description" account.description [] (Description >> Update >> dispatch)
|
||||
Fields.checkbox "Active" "" account.isActive [] (IsActive >> Update >> dispatch)
|
||||
(if State.isNewAccount account then modal else span[][])
|
||||
]
|
||||
|
||||
let clipLimitsView model dispatch =
|
||||
@@ -91,12 +74,12 @@ let clipLimitsView model dispatch =
|
||||
{
|
||||
cells =
|
||||
[
|
||||
TableControl.Cell (str "")
|
||||
TableControl.Cell (str "Exchange")
|
||||
TableControl.Cell (str "Security Type")
|
||||
TableControl.Cell (str "Symbol")
|
||||
TableControl.Cell (str "Security")
|
||||
TableControl.Cell (str "Clip Size")
|
||||
{ content = str ""; className = ""; isHeader = true }
|
||||
{ content = str "Exchange"; className = ""; isHeader = true }
|
||||
{ content = str "Security Type"; className = ""; isHeader = true }
|
||||
{ content = str "Symbol"; className = ""; isHeader = true }
|
||||
{ content = str "Security"; className = ""; isHeader = true }
|
||||
{ content = str "Clip Size"; className = ""; isHeader = true }
|
||||
]
|
||||
className = ""
|
||||
header = None
|
||||
@@ -155,41 +138,51 @@ let clipLimitsView model dispatch =
|
||||
[ for limit in model.scratch.securityClipLimits.limit do yield {
|
||||
cells =
|
||||
[
|
||||
TableControl.Cell (Button.fabutton "Remove" "times" Button.Enabled (fun _ -> (Array.findIndex (fun l -> limit = l) model.scratch.securityClipLimits.limit) |> DeleteClipLimit |> dispatch))
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
{ content = Button.fabutton "Remove" "times" Button.Enabled (fun _ -> (Array.findIndex (fun l -> limit = l) model.scratch.securityClipLimits.limit) |> DeleteClipLimit |> dispatch); className = ""; isHeader = false }
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
(EnumUtils.toHuman<bts.api.Venue> false venues limit.security.exchange)
|
||||
(EnumUtils.allToHuman<bts.api.Venue> false venues)
|
||||
[]
|
||||
((changeVenue limit) >> ClipLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
(EnumUtils.toHuman<bts.api.SecurityType> false securityTypes limit.security.securityType)
|
||||
(EnumUtils.allToHuman<bts.api.SecurityType> false securityTypes)
|
||||
[]
|
||||
((changeType limit) >> ClipLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
limit.security.securitySymbol
|
||||
symbolArray
|
||||
[]
|
||||
((changeSymbol limit) >> ClipLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.mapSelect
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.mapSelect
|
||||
""
|
||||
(JSMap.toMap (getSecurityMap limit))
|
||||
None
|
||||
(Some (sprintf "%i" limit.security.securityId))
|
||||
((changeSecurityId limit) >> ClipLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.text
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.text
|
||||
""
|
||||
(sprintf "%i" limit.clipSize)
|
||||
[ Style [Width "10em"]]
|
||||
((changeClipSize limit) >> ClipLimits >> dispatch)
|
||||
)
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
]
|
||||
className = ""
|
||||
header = None
|
||||
@@ -213,12 +206,12 @@ let productLimits model dispatch =
|
||||
{
|
||||
cells =
|
||||
[
|
||||
TableControl.Cell (str "")
|
||||
TableControl.Cell (str "Exchange")
|
||||
TableControl.Cell (str "Security Type")
|
||||
TableControl.Cell (str "Symbol")
|
||||
TableControl.Cell (str "Max Net Long Position")
|
||||
TableControl.Cell (str "Max Net Short Position")
|
||||
{ content = str ""; className = ""; isHeader = true }
|
||||
{ content = str "Exchange"; className = ""; isHeader = true }
|
||||
{ content = str "Security Type"; className = ""; isHeader = true }
|
||||
{ content = str "Symbol"; className = ""; isHeader = true }
|
||||
{ content = str "Max Net Long Position"; className = ""; isHeader = true }
|
||||
{ content = str "Max Net Short Position"; className = ""; isHeader = true }
|
||||
]
|
||||
className = ""
|
||||
header = None
|
||||
@@ -258,40 +251,50 @@ let productLimits model dispatch =
|
||||
[ for limit in model.scratch.productPositionLimits do yield {
|
||||
cells =
|
||||
[
|
||||
TableControl.Cell (Button.fabutton "Remove" "times" Button.Enabled (fun _ -> (Array.findIndex (fun l -> limit = l) model.scratch.productPositionLimits) |> DeleteProductLimit |> dispatch))
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
{ content = Button.fabutton "Remove" "times" Button.Enabled (fun _ -> (Array.findIndex (fun l -> limit = l) model.scratch.productPositionLimits) |> DeleteProductLimit |> dispatch); className = ""; isHeader = false }
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
(EnumUtils.toHuman<bts.api.Venue> false venues limit.product.exchange)
|
||||
(EnumUtils.allToHuman<bts.api.Venue> false venues)
|
||||
[]
|
||||
((changeVenue limit) >> ProductLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
(EnumUtils.toHuman<bts.api.SecurityType> false securityTypes limit.product.securityType)
|
||||
(EnumUtils.allToHuman<bts.api.SecurityType> false securityTypes)
|
||||
[]
|
||||
((changeType limit) >> ProductLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
limit.product.securitySymbol
|
||||
symbolArray
|
||||
[]
|
||||
((changeSymbol limit) >> ProductLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.text
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.text
|
||||
""
|
||||
(sprintf "%i" limit.longPositionLimit)
|
||||
[ Style [Width "10em"]]
|
||||
((changeLongPositionLimit limit) >> ProductLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.text
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.text
|
||||
""
|
||||
(sprintf "%i" limit.shortPositionLimit)
|
||||
[ Style [Width "10em"]]
|
||||
((changeShortPositionLimit limit) >> ProductLimits >> dispatch)
|
||||
)
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
]
|
||||
className = ""
|
||||
header = None
|
||||
@@ -323,13 +326,12 @@ let securityLimits model dispatch =
|
||||
{
|
||||
cells =
|
||||
[
|
||||
TableControl.Cell (str "")
|
||||
TableControl.Cell (str "Exchange")
|
||||
TableControl.Cell (str "Security Type")
|
||||
TableControl.Cell (str "Symbol")
|
||||
TableControl.Cell (str "Security")
|
||||
TableControl.Cell (str "Max Net Long Position")
|
||||
TableControl.Cell (str "Max Net Short Position")
|
||||
{ content = str ""; className = ""; isHeader = true }
|
||||
{ content = str "Exchange"; className = ""; isHeader = true }
|
||||
{ content = str "Security Type"; className = ""; isHeader = true }
|
||||
{ content = str "Symbol"; className = ""; isHeader = true }
|
||||
{ content = str "Max Net Long Position"; className = ""; isHeader = true }
|
||||
{ content = str "Max Net Short Position"; className = ""; isHeader = true }
|
||||
]
|
||||
className = ""
|
||||
header = None
|
||||
@@ -337,25 +339,6 @@ let securityLimits model dispatch =
|
||||
|
||||
let symbolArray = Array.concat [| [| model.AllValueDisplay |]; Array.map<bts.api.Product, string> (fun a -> a.symbol ) model.mdRelayProductData |]
|
||||
|
||||
let getSecurityMap (limit:AccountSecurityPositionLimitUnit) =
|
||||
let allOption = {
|
||||
product = {
|
||||
exchange = bts.api.Venue.UNSET
|
||||
``type`` = bts.api.SecurityType.UNSET
|
||||
symbol = model.AllValueDisplay
|
||||
};
|
||||
names = (JSMap.add<string> "0" model.AllValueDisplay JSMap.empty<string>)
|
||||
}:ExpirationNameList
|
||||
|
||||
Array.concat [ [| allOption |]; model.mdRelayExpNameData]
|
||||
|> Array.filter (fun (x:bts.co2.ExpirationNameList) -> (
|
||||
((x.product.exchange = limit.security.exchange) || (x.product.exchange = bts.api.Venue.UNSET))
|
||||
&& ((x.product.``type`` = limit.security.securityType) || (x.product.``type`` = bts.api.SecurityType.UNSET))
|
||||
&& ((x.product.symbol = limit.security.securitySymbol) || (x.product.symbol = model.AllValueDisplay))
|
||||
)
|
||||
)
|
||||
|> Seq.map (fun x -> x.names ) |> JSMap.concat
|
||||
|
||||
let modifyRow (oldRow:AccountSecurityPositionLimitUnit) (newRow:AccountSecurityPositionLimitUnit) =
|
||||
let i = Array.findIndex (fun l -> oldRow = l) model.scratch.securityPositionLimits
|
||||
let left = Array.append (model.scratch.securityPositionLimits.[..i-1]) [| newRow |]
|
||||
@@ -378,9 +361,6 @@ let securityLimits model dispatch =
|
||||
| true -> model.AllValue
|
||||
modifyRow oldRow { oldRow with security = { oldRow.security with securitySymbol = v } }
|
||||
|
||||
let changeSecurityId (oldRow:AccountSecurityPositionLimitUnit) (value:string) =
|
||||
modifyRow oldRow { oldRow with security = { oldRow.security with securityId = (uint32 value) } }
|
||||
|
||||
let changeLongPositionLimit (oldRow:AccountSecurityPositionLimitUnit) (value:string) =
|
||||
modifyRow oldRow { oldRow with longPositionLimit = (uint32 value) }
|
||||
|
||||
@@ -391,47 +371,50 @@ let securityLimits model dispatch =
|
||||
[ for limit in model.scratch.securityPositionLimits do yield {
|
||||
cells =
|
||||
[
|
||||
TableControl.Cell (Button.fabutton "Remove" "times" Button.Enabled (fun _ -> (Array.findIndex (fun l -> limit = l) model.scratch.securityPositionLimits) |> DeleteSecurityLimit |> dispatch))
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
{ content = Button.fabutton "Remove" "times" Button.Enabled (fun _ -> (Array.findIndex (fun l -> limit = l) model.scratch.securityPositionLimits) |> DeleteSecurityLimit |> dispatch); className = ""; isHeader = false }
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
(EnumUtils.toHuman<bts.api.Venue> false venues limit.security.exchange)
|
||||
(EnumUtils.allToHuman<bts.api.Venue> false venues)
|
||||
[]
|
||||
((changeVenue limit) >> SecurityLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
(EnumUtils.toHuman<bts.api.SecurityType> false securityTypes limit.security.securityType)
|
||||
(EnumUtils.allToHuman<bts.api.SecurityType> false securityTypes)
|
||||
[]
|
||||
((changeType limit) >> SecurityLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.enumSelect<string>
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.enumSelect<string>
|
||||
""
|
||||
limit.security.securitySymbol
|
||||
symbolArray
|
||||
[]
|
||||
((changeSymbol limit) >> SecurityLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.mapSelect
|
||||
""
|
||||
(JSMap.toMap (getSecurityMap limit))
|
||||
None
|
||||
(Some (sprintf "%i" limit.security.securityId))
|
||||
((changeSecurityId limit) >> SecurityLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.text
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.text
|
||||
""
|
||||
(sprintf "%i" limit.longPositionLimit)
|
||||
[ Style [Width "10em"]]
|
||||
((changeLongPositionLimit limit) >> SecurityLimits >> dispatch)
|
||||
)
|
||||
TableControl.Cell (Fields.text
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{ content = Fields.text
|
||||
""
|
||||
(sprintf "%i" limit.shortPositionLimit)
|
||||
[ Style [Width "10em"]]
|
||||
((changeShortPositionLimit limit) >> SecurityLimits >> dispatch)
|
||||
)
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
]
|
||||
className = ""
|
||||
header = None
|
||||
@@ -466,10 +449,18 @@ let ConnectionsView (model:Accounts.Types.Model) dispatch =
|
||||
let rws:TableControl.row list =
|
||||
[for name in conNames do yield {
|
||||
cells = [
|
||||
(TableControl.Cell (Button.fabutton "" "times" Button.Enabled (fun _ -> name |> ( RemoveConnection >> dispatch))))
|
||||
(TableControl.Cell (str name))
|
||||
{
|
||||
content = Button.fabutton "" "times" Button.Enabled (fun _ -> name |> ( RemoveConnection >> dispatch))
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{
|
||||
content = str name
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
]
|
||||
className="no-border"
|
||||
className=""
|
||||
header = None
|
||||
}
|
||||
]
|
||||
@@ -485,7 +476,9 @@ let ConnectionsView (model:Accounts.Types.Model) dispatch =
|
||||
|> Map.ofArray
|
||||
|
||||
let rows =
|
||||
rws @ [{cells = [ {content = Fields.mapSelect "" conMap (Some "Add Connection") None (fun v -> v |> (AddConnection >> dispatch)); className = ""; isHeader = true; colSpan = 2 } ]; className = "no-border"; header = None }]
|
||||
if not (Map.isEmpty conMap) then
|
||||
rws @ [{cells = [ {content = Fields.mapSelect "" conMap (Some "Add Connection") None (fun v -> v |> (AddConnection >> dispatch)); className = ""; isHeader = true } ]; className = ""; header = None }]
|
||||
else rws
|
||||
div []
|
||||
[
|
||||
TableControl.createTable None rows
|
||||
|
||||
@@ -8,6 +8,7 @@ open Fable.Import.React
|
||||
open Global
|
||||
open Fable
|
||||
open JSMap
|
||||
open Fable
|
||||
|
||||
let text (lbl:string) (dv:string) (attrs:IHTMLProp list) onchange =
|
||||
div [ ClassName "field" ]
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
module Modal
|
||||
|
||||
open Fable.Core
|
||||
open Fable.Core.JsInterop
|
||||
open Fable.Helpers.React
|
||||
open Fable.Helpers.React.Props
|
||||
open Fable.Import.React
|
||||
open Global
|
||||
open Fable
|
||||
|
||||
|
||||
let show (title:string) (content:ReactElement) (footerContent:ReactElement) (onCloseClick: MouseEvent -> Unit) =
|
||||
div [ Class "modal is-active" ]
|
||||
[ div [ Class "modal-background" ][ ]
|
||||
div [ Class "modal-card" ]
|
||||
[ header [ Class "modal-card-head" ]
|
||||
[ p [ Class "modal-card-title" ][ str title ]
|
||||
button [ Class "delete"; OnClick onCloseClick ] [ ]
|
||||
]
|
||||
section [ Class "modal-card-body" ] [ content ]
|
||||
footer [ Class "modal-card-foot" ] [ footerContent ]
|
||||
]
|
||||
]
|
||||
|
||||
let showCreate title content (onCreateClick: MouseEvent -> Unit) (onCloseClick: MouseEvent -> Unit) =
|
||||
show
|
||||
title
|
||||
content
|
||||
(div [] [ button [ Class "button is-success"; OnClick onCreateClick ]
|
||||
[ str "Create" ]
|
||||
button [ Class "button"; OnClick onCloseClick ]
|
||||
[ str "Cancel" ]
|
||||
])
|
||||
(onCloseClick: MouseEvent -> Unit)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ open Fable.Helpers.React
|
||||
open Fable.Helpers.React.Props
|
||||
open Fable.Import.React
|
||||
|
||||
type cell = { content:ReactElement; className:string; isHeader:bool; colSpan:int }
|
||||
|
||||
let Cell (content:ReactElement) = { content = content; className = ""; isHeader = false; colSpan = 0 }
|
||||
type cell = { content:ReactElement; className:string; isHeader:bool }
|
||||
|
||||
type row = {
|
||||
cells:cell list
|
||||
@@ -18,8 +16,8 @@ type row = {
|
||||
|
||||
let createCell (c:cell) =
|
||||
match c.isHeader with
|
||||
| false -> td [ ClassName c.className; ColSpan (float c.colSpan) ] [ c.content ]
|
||||
| true -> th [ ClassName c.className; ColSpan (float c.colSpan) ] [ c.content ]
|
||||
| false -> td [ ClassName c.className ] [ c.content ]
|
||||
| true -> th [ ClassName c.className ] [ c.content ]
|
||||
|
||||
let createRow (r:row) =
|
||||
match r.header with
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
module Global
|
||||
open Fable.Core
|
||||
|
||||
let Class = Fable.Helpers.React.Props.HTMLAttr.ClassName
|
||||
|
||||
type AlertLevel =
|
||||
| Info
|
||||
|
||||
@@ -85,7 +85,6 @@ let init (context:ServiceContext.ServiceContext) : Model * Cmd<Msg> =
|
||||
userList = []
|
||||
accountsList = []
|
||||
scratchUser = defaultScratchUser
|
||||
PreviousScratchUser = defaultScratchUser
|
||||
currentTab = Tab.General
|
||||
admin = admin
|
||||
adminData = None
|
||||
@@ -97,31 +96,13 @@ let rec removeUser (user:BTSUser) (userList:BTSUser list) =
|
||||
| hd::tl -> hd::removeUser user tl
|
||||
| _ -> []
|
||||
|
||||
let getFirstUser usrlst =
|
||||
let head = (List.sortBy (fun (u:BTSUser) -> u.name.ToLower()) usrlst) |> List.tryHead
|
||||
match head with
|
||||
| None -> defaultScratchUser
|
||||
| Some hd -> hd
|
||||
let getCurrentUser (user:BTSUser) (userList:BTSUser list) =
|
||||
List.tryFind (fun (x:BTSUser) -> x.id = user.id) userList
|
||||
|
||||
let findUserInList (user:BTSUser) (userList:BTSUser list) =
|
||||
List.tryFind (fun (x:BTSUser) ->
|
||||
x.id = user.id
|
||||
|| (user.id = 0u && x.name = user.name)
|
||||
)
|
||||
userList
|
||||
|
||||
let getCurrentOrFirstUser (model:Model) (userList:BTSUser list) =
|
||||
match (findUserInList model.scratchUser userList), (findUserInList model.PreviousScratchUser userList) with
|
||||
| Some cu, _ -> cu
|
||||
| None, Some pu -> pu
|
||||
| None, None -> getFirstUser userList
|
||||
|
||||
let getCurrentUser (model:Model) = getCurrentOrFirstUser model model.userList
|
||||
|
||||
|
||||
|
||||
|
||||
let userIsdirty (initialState:BTSUser) (scratch:BTSUser) (existing:BTSUser) = (scratch <> existing) && (scratch <> initialState)
|
||||
let userIsdirty (initialState:BTSUser) (scratch:BTSUser) (existing:BTSUser option) =
|
||||
match existing with
|
||||
| None -> (scratch <> initialState)
|
||||
| Some ex -> (scratch <> ex) && (scratch <> initialState)
|
||||
|
||||
let userIsSelected (initialState:BTSUser) (scratch:BTSUser) = scratch <> initialState
|
||||
|
||||
@@ -199,11 +180,16 @@ let update msg (model:Users.Types.Model) : Model * Cmd<Msg> =
|
||||
| UpdateAdminData adm ->
|
||||
let usrlst = Seq.toList (Seq.filter (fun (usr:BTSUser) -> usr.isPresent) adm.users)
|
||||
|
||||
let cusr =
|
||||
match getCurrentUser model.scratchUser usrlst with
|
||||
| None -> (List.sortBy (fun (u:BTSUser) -> u.name.ToLower()) usrlst).Head
|
||||
| Some cu -> cu
|
||||
|
||||
{model with
|
||||
adminData = Some adm
|
||||
userList = usrlst
|
||||
accountsList = (Seq.toList adm.accounts)
|
||||
scratchUser = (getCurrentOrFirstUser model usrlst)
|
||||
scratchUser = cusr
|
||||
}, Cmd.Empty
|
||||
| RpcResponse str ->
|
||||
printf "%s" str
|
||||
@@ -227,16 +213,16 @@ let update msg (model:Users.Types.Model) : Model * Cmd<Msg> =
|
||||
| false -> model, Cmd.Empty
|
||||
|
||||
| AddUser ->
|
||||
{ model with PreviousScratchUser = model.scratchUser; scratchUser = { defaultScratchUser with name = "New User" } }, Cmd.ofMsg (SwitchTab General)
|
||||
{ model with scratchUser = { defaultScratchUser with name = "New User" } }, Cmd.ofMsg (SwitchTab General)
|
||||
| SwitchTab tb ->
|
||||
{model with currentTab = tb}, Cmd.Empty
|
||||
|
||||
| SwitchUser usr ->
|
||||
{ model with PreviousScratchUser = model.scratchUser; scratchUser = usr }, Cmd.Empty
|
||||
{ model with scratchUser = usr }, Cmd.Empty
|
||||
| SaveUser ->
|
||||
// match on id = 0 to see if it's a new user
|
||||
match model.scratchUser.id with
|
||||
| 0u -> { model with PreviousScratchUser = model.scratchUser}, Cmd.ofExistingPromise (createUser model.admin model.scratchUser) id (box>>ErrorOccured)
|
||||
| 0u -> model, Cmd.ofExistingPromise (createUser model.admin model.scratchUser) id (box>>ErrorOccured)
|
||||
| _ -> model, Cmd.ofExistingPromise (saveUser model.admin model.scratchUser) id (box>>ErrorOccured)
|
||||
| ErrorOccured msg ->
|
||||
//Browser.console.log msg
|
||||
|
||||
@@ -13,7 +13,6 @@ type Model = {
|
||||
userList: BTSUser list
|
||||
accountsList: BTSAccount list
|
||||
scratchUser:BTSUser
|
||||
PreviousScratchUser:BTSUser
|
||||
currentTab:Tab
|
||||
admin : IAdminService
|
||||
adminData : AdminData option
|
||||
|
||||
@@ -27,28 +27,18 @@ let isActiveUser id (currentUser:BTSUser) =
|
||||
|
||||
let saveButton (model:Users.Types.Model) dispatch =
|
||||
let buttonText = if State.isNewAccount model.scratchUser then "Create" else "Save"
|
||||
let dirty = State.userIsdirty State.defaultScratchUser model.scratchUser (State.getCurrentUser model)
|
||||
let dirty = State.userIsdirty State.defaultScratchUser model.scratchUser (State.getCurrentUser model.scratchUser model.userList)
|
||||
let enabled = if dirty then Button.Enabled else Button.Disabled
|
||||
let flavor = if dirty then "is-success" else "is-light"
|
||||
Button.button buttonText flavor "" enabled (fun _ -> dispatch SaveUser)
|
||||
|
||||
let generalTabContent (model:Model) (dispatch:Msg->Unit) =
|
||||
let modal = Modal.showCreate
|
||||
"Create User"
|
||||
(div [][
|
||||
Fields.text "Name" model.scratchUser.name [ ] (Name >> UpdateField >> dispatch)
|
||||
Fields.password "Password" model.scratchUser.password [ ] (Password >> UpdateField >> dispatch)
|
||||
Fields.checkbox "Active" "Allows the user to login and access information" model.scratchUser.isActive [] (IsActive >> UpdateField >> dispatch)
|
||||
])
|
||||
(fun _ -> dispatch SaveUser)
|
||||
(fun _ -> SwitchUser (State.getCurrentUser model) |> dispatch)
|
||||
div [] [
|
||||
p [ Style [FontWeight "Bold"] ] [ str ("ID: " + model.scratchUser.id.ToString())]
|
||||
br []
|
||||
Fields.text "Name" model.scratchUser.name [ ] (Name >> UpdateField >> dispatch)
|
||||
Fields.password "Password" model.scratchUser.password [ ] (Password >> UpdateField >> dispatch)
|
||||
Fields.checkbox "Active" "Allows the user to login and access information" model.scratchUser.isActive [] (IsActive >> UpdateField >> dispatch)
|
||||
(if State.isNewAccount model.scratchUser then modal else span[][])
|
||||
]
|
||||
|
||||
let accountNameLookup (accountsList:BTSAccount list) (id:string) =
|
||||
@@ -62,25 +52,19 @@ let accountsTabContent (model:Model) (dispatch:Msg->Unit) =
|
||||
let keys =
|
||||
Global.Reflection.keys accountPermissions
|
||||
|> Array.map (fun i -> i, accountNameLookup model.accountsList i)
|
||||
let headerRow:TableControl.row =
|
||||
{
|
||||
cells =
|
||||
[
|
||||
TableControl.Cell(str "")
|
||||
TableControl.Cell(str "Account")
|
||||
TableControl.Cell(str "Trading Permission")
|
||||
TableControl.Cell(str "Admin Permission")
|
||||
]
|
||||
className = ""
|
||||
header = None
|
||||
}
|
||||
let rws:TableControl.row list =
|
||||
[for (key,name) in keys do yield {
|
||||
cells = [
|
||||
TableControl.Cell(Button.fabutton "" "times" Button.Enabled (fun _ -> key |> (RemoveAccount >> UpdateField >> dispatch)))
|
||||
TableControl.Cell(str name)
|
||||
TableControl.Cell( str "Full")
|
||||
TableControl.Cell(str "Full")
|
||||
{
|
||||
content = Button.fabutton "" "times" Button.Enabled (fun _ -> key |> (RemoveAccount >> UpdateField >> dispatch))
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
{
|
||||
content = str name
|
||||
className = ""
|
||||
isHeader = false
|
||||
}
|
||||
]
|
||||
className=""
|
||||
header = None
|
||||
@@ -95,13 +79,12 @@ let accountsTabContent (model:Model) (dispatch:Msg->Unit) =
|
||||
|> Map.ofList
|
||||
|
||||
let rows =
|
||||
rws @ [{cells = [
|
||||
{ content = Fields.mapSelect "" accMap (Some "Add Account") None (AddAccount >> UpdateField >> dispatch); className = ""; isHeader = false; colSpan = 4 }
|
||||
]; className = ""; header = None }]
|
||||
|
||||
if not (Map.isEmpty accMap) then
|
||||
rws @ [{cells = [ {content = Fields.mapSelect "" accMap (Some "Add Account") None (AddAccount >> UpdateField >> dispatch); className = ""; isHeader = true } ]; className = ""; header = None }]
|
||||
else rws
|
||||
div []
|
||||
[
|
||||
TableControl.createTable (Some headerRow) rows
|
||||
TableControl.createTable None rows
|
||||
]
|
||||
|
||||
let mdEntitlementsTabContent (model:Model) (dispatch:Msg->Unit) =
|
||||
@@ -207,8 +190,11 @@ let usersMenu (usersList:BTSUser list) (currentUser:BTSUser) (dispatch:Msg->Unit
|
||||
]
|
||||
|
||||
let root (model:Model) (dispatch:Msg->Unit) =
|
||||
|
||||
let dirty = State.userIsdirty State.defaultScratchUser model.scratchUser (State.getCurrentUser model)
|
||||
let definitelyAUser =
|
||||
match (State.getCurrentUser model.scratchUser model.userList) with
|
||||
| Some useThis -> useThis
|
||||
| None -> State.defaultScratchUser
|
||||
let dirty = State.userIsdirty State.defaultScratchUser model.scratchUser (State.getCurrentUser model.scratchUser model.userList)
|
||||
|
||||
(match model.adminData with
|
||||
| None -> div[][str "Loading user list..."]
|
||||
@@ -243,7 +229,7 @@ let root (model:Model) (dispatch:Msg->Unit) =
|
||||
div [ ClassName "column is-narrow" ]
|
||||
[
|
||||
yield saveButton model dispatch
|
||||
yield Button.button "Revert" "is-light" "" (if dirty then Button.Enabled else Button.Disabled) (fun _ -> SwitchUser (State.getCurrentUser model) |> dispatch)
|
||||
yield Button.button "Revert" "is-light" "" (if dirty then Button.Enabled else Button.Disabled) (fun _ -> SwitchUser definitelyAUser |> dispatch)
|
||||
]
|
||||
]
|
||||
tabContent model dispatch
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
<Compile Include="Controls/DataGrid.fs" />
|
||||
<Compile Include="Controls/Fields.fs" />
|
||||
<Compile Include="Controls/Table.fs" />
|
||||
<Compile Include="Controls/Modal.fs" />
|
||||
<Compile Include="Access.fs" />
|
||||
<!-- Login -->
|
||||
<Compile Include="Login/Login.fs" />
|
||||
|
||||
@@ -2,6 +2,4 @@
|
||||
setlocal
|
||||
@call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\VsDevCmd.bat" > nul
|
||||
cd /d %~dp0/../
|
||||
setlocal
|
||||
set path=%spark_build_python_path%;%path%
|
||||
msbuild /m /nologo /p:debugtype=portable /p:platform=x64 /p:GenerateFullPaths=true /clp:verbosity=minimal /clp:summary %* spark_client.sln
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
@echo off
|
||||
pushd %~dp0\..
|
||||
cd %~dp0\..
|
||||
@@ -1,6 +0,0 @@
|
||||
@ echo off
|
||||
REM wow....doskey is still around...
|
||||
|
||||
doskey bc=pskill spark.exe ^& buildspark
|
||||
doskey bs=killsparkd ^& buildsparkd
|
||||
doskey ba=pskill spark.exe ^& buildspark ^&^& killsparkd ^& buildsparkd
|
||||
+1
-2
@@ -3,5 +3,4 @@ setlocal
|
||||
cd %~dp0\..
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\VsDevCmd.bat" > nul
|
||||
cd /d %~dp0\..
|
||||
setlocal
|
||||
cmd /C start "" "C:\Program Files\Microsoft VS Code\Code.exe" "%*"
|
||||
cmd /C start "" "C:\Program Files\Microsoft VS Code\Code.exe" "%*" -n
|
||||
Reference in new issue
Block a user