Compare commits
101
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ec2423ad2 | ||
|
|
1a88e52a08 | ||
|
|
a1b52d083d | ||
|
|
f364317cfa | ||
|
|
f474af1925 | ||
|
|
03ac5035fc | ||
|
|
e78d8208fa | ||
|
|
7f8c591f75 | ||
|
|
7d9685f995 | ||
|
|
699736ff52 | ||
|
|
8d0e7bc467 | ||
|
|
df784e9961 | ||
|
|
af39aab153 | ||
|
|
885c1ad9f8 | ||
|
|
91fdd6ec58 | ||
|
|
3adfd7056a | ||
|
|
5f845f1355 | ||
|
|
5d4bb1d421 | ||
|
|
9836e46dd9 | ||
|
|
bf9d5c2c6d | ||
|
|
ef1c3a7959 | ||
|
|
b07f6aba59 | ||
|
|
a7b3b0fa6d | ||
|
|
f7a3b3ebc1 | ||
|
|
d291e4a022 | ||
|
|
292e5575d0 | ||
|
|
1ad57dbf45 | ||
|
|
b25f5556af | ||
|
|
c10a4be411 | ||
|
|
de312c3023 | ||
|
|
93ff56c5cd | ||
|
|
5254b7367a | ||
|
|
6d2efb7dc9 | ||
|
|
50214741e7 | ||
|
|
56950b682d | ||
|
|
83b1bbf087 | ||
|
|
203998e201 | ||
|
|
59d88d3cc1 | ||
|
|
13f9717195 | ||
|
|
855e0d7ef9 | ||
|
|
27a1cb277c | ||
|
|
3107d5f382 | ||
|
|
e1940faeac | ||
|
|
a2f22482f6 | ||
|
|
ccea4571a6 | ||
|
|
3a72a4a82e | ||
|
|
ed6affb6b5 | ||
|
|
2fc208ef74 | ||
|
|
64f86db86b | ||
|
|
5e960479c7 | ||
|
|
837aa51adf | ||
|
|
d7a7b480ad | ||
|
|
d94afe9867 | ||
|
|
410e17aec5 | ||
|
|
a8bd99af56 | ||
|
|
e11b5928ce | ||
|
|
6bcf352813 | ||
|
|
eeec7b0076 | ||
|
|
bacdb1c4cd | ||
|
|
1e4ad28088 | ||
|
|
e013f93169 | ||
|
|
002c051c06 | ||
|
|
42fd7b3f40 | ||
|
|
f73d163312 | ||
|
|
ee124401b9 | ||
|
|
ec7417a9c6 | ||
|
|
536f4703ab | ||
|
|
5f848a36e2 | ||
|
|
a7c714591a | ||
|
|
4655a68593 | ||
|
|
9677d8996e | ||
|
|
3f0a9e442e | ||
|
|
f3387b57af | ||
|
|
abed4bbff6 | ||
|
|
bb90429ada | ||
|
|
1efbc2fd45 | ||
|
|
04027ed980 | ||
|
|
5a850c314c | ||
|
|
b566668d83 | ||
|
|
6679695931 | ||
|
|
23a8112dd3 | ||
|
|
b9a07a8e21 | ||
|
|
86deaf2edb | ||
|
|
fc347250e3 | ||
|
|
aae867a555 | ||
|
|
37a454cf51 | ||
|
|
5162b9f0c0 | ||
|
|
ad757495bd | ||
|
|
e473c18a30 | ||
|
|
640ec0b1ed | ||
|
|
c3e2622a22 | ||
|
|
7d05f76fe4 | ||
|
|
7a8a38f89d | ||
|
|
1e54cb1daf | ||
|
|
7b714018d2 | ||
|
|
c3371d0971 | ||
|
|
322e3d81ba | ||
|
|
4ada5a89f4 | ||
|
|
15aa016aa0 | ||
|
|
e77f3169de | ||
|
|
660c5d3a06 |
No files matched your search
@@ -39,6 +39,7 @@
|
||||
|
||||
<Message Importance="High" Text="Generating service %(Proto3.FileName)" />
|
||||
<Exec Command="
|
||||
set PATH=$(spark_build_python_path);%path% &
|
||||
set PYTHONPATH=$(Externals)\python &
|
||||
$(protoc) ^
|
||||
%(Proto3.FullPath) ^
|
||||
|
||||
@@ -15,34 +15,33 @@
|
||||
<ResourceDictionary Source="/view/Controls/Popup/PopupControl.xaml"/>
|
||||
<ResourceDictionary Source="/Modules/Spreader/ParameterSectionView.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<sys:Double x:Key="GroupLevelIndent">18</sys:Double>
|
||||
<sys:Double x:Key="RowHeaderWidth">285</sys:Double>
|
||||
<sys:Double x:Key="RowHeight">16</sys:Double>
|
||||
<sys:Double x:Key="NegativeRowHeight">-16</sys:Double>
|
||||
<sys:Double x:Key="ItemsHostCanBeDisabledCollapsedHeight">15</sys:Double>
|
||||
<Thickness
|
||||
x:Key="GroupLevelMargin"
|
||||
Left="{StaticResource GroupLevelIndent}"
|
||||
Left="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
|
||||
/>
|
||||
<Thickness
|
||||
x:Key="GroupContentAlignedWithHeaderMargin"
|
||||
Left="{StaticResource GroupLevelIndent}"
|
||||
Left="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
|
||||
Top="{StaticResource NegativeRowHeight}"
|
||||
/>
|
||||
<cv:SyntheticPriceCalcDisplayTextConverter x:Key="SyntheticPriceCalcDisplayConverter"/>
|
||||
<cv:TickFactorsTextConverter x:Key="TickFactorsTextConverter"/>
|
||||
<usp:RowHeaderGroupIndentConverter
|
||||
x:Key="RowHeaderGroupIndentConverter"
|
||||
Indent="{StaticResource GroupLevelIndent}"
|
||||
Indent="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
|
||||
/>
|
||||
<usp:RowHeaderGroupIndentConverter
|
||||
x:Key="ReverseRowHeaderGroupIndentConverter"
|
||||
Indent="{StaticResource GroupLevelIndent}"
|
||||
Indent="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
|
||||
Reverse="True"
|
||||
/>
|
||||
<usp:RowGroupExpanderWidthConverter
|
||||
x:Key="RowGroupExpanderWidthConverter"
|
||||
Indent="{StaticResource GroupLevelIndent}"
|
||||
Indent="{x:Static usp:ExecutionStrategyEditor.GroupLevelIndent}"
|
||||
Offset="{StaticResource RowHeight}"
|
||||
RowHeaderWidth="{StaticResource RowHeaderWidth}"
|
||||
/>
|
||||
@@ -293,7 +292,7 @@
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<DataGridTextColumn x:Key="AddColumnPlaceholder" x:Shared="False" IsReadOnly="True">
|
||||
<DataGridTextColumn x:Key="{x:Static usp:ExecutionStrategyEditor.AddColumnPlaceholderKey}" x:Shared="False" IsReadOnly="True">
|
||||
<DataGridTextColumn.Header>
|
||||
<c:PopupControl
|
||||
ShowExpanderArrow="False" PopupPlacement="Right" Header="Add Leg" Padding="6,0"
|
||||
@@ -308,7 +307,7 @@
|
||||
</c:PopupControl>
|
||||
</DataGridTextColumn.Header>
|
||||
</DataGridTextColumn>
|
||||
<DataTemplate x:Key="RemovableColumnHeaderTemplate">
|
||||
<DataTemplate x:Key="{x:Static usp:ExecutionStrategyEditor.RemovableColumnHeaderTemplateKey}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
@@ -321,7 +320,9 @@
|
||||
<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,7 +25,13 @@ namespace mbts.ui.spark {
|
||||
/// <summary>
|
||||
/// Interaction logic for ExecutionStrategyEditor.xaml
|
||||
/// </summary>
|
||||
partial class ExecutionStrategyEditor : Control {
|
||||
partial class ExecutionStrategyEditor : Control {
|
||||
public const string
|
||||
RemovableColumnHeaderTemplateKey = "RemovableColumnHeaderTemplate",
|
||||
NonRemovableColumnHeaderTemplateKey = "ReadOnlyColumnHeaderTemplate",
|
||||
AddColumnPlaceholderKey = "AddColumnPlaceholder";
|
||||
public const double GroupLevelIndent = 18;
|
||||
|
||||
static ExecutionStrategyEditor() =>
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExecutionStrategyEditor), new FrameworkPropertyMetadata(typeof(ExecutionStrategyEditor)));
|
||||
|
||||
@@ -196,7 +202,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("AddColumnPlaceholder"));
|
||||
if (IsNew) dg.Columns.Add((DataGridColumn)FindResource(AddColumnPlaceholderKey));
|
||||
_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));
|
||||
}
|
||||
@@ -204,7 +210,7 @@ namespace mbts.ui.spark {
|
||||
|
||||
DataGridColumn _column_factory(LegSpreaderParamsTuple l) {
|
||||
var col = new DataGridColumnInternal(l.leg.Security, ParameterCellTemplateConverter, ParameterCellEditingTemplateConverter) { Width = 75, };
|
||||
if (IsNew) col.HeaderTemplate = (DataTemplate)FindResource("RemovableColumnHeaderTemplate");
|
||||
col.HeaderTemplate = (DataTemplate)FindResource(IsNew ? RemovableColumnHeaderTemplateKey : NonRemovableColumnHeaderTemplateKey);
|
||||
DataGridEx.SetRightBorder(col, new DataGridExVerticalLine(false, true, Colors.DarkGray, 1));
|
||||
return col;
|
||||
}
|
||||
@@ -240,7 +246,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("data_type") { Converter = cell_template_converter });
|
||||
rv.SetBinding(ContentControl.ContentTemplateProperty, new Binding(nameof(ICell.data_type)) { Converter = cell_template_converter });
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,10 @@ namespace mbts.ui.spark
|
||||
void _items_presenter_SizeChanged(object sender, SizeChangedEventArgs e) { InvalidateVisual(); }
|
||||
|
||||
protected override void draw_vertical_line(DrawingContext dc, double x, DataGridExVerticalLine line) {
|
||||
if (_items_presenter != null) dc.DrawLine(line.pen.Value, new Point(x - 10, 58), new Point(x - 10, _items_presenter.ActualHeight + 58));
|
||||
x -= GroupLevelIndent;
|
||||
var start = _grid.TranslatePoint(new Point(x, _grid.super_header_height + _col_header_presenter.ActualHeight), this);
|
||||
var stop = _grid.TranslatePoint(new Point(x, _grid.ActualHeight), this);
|
||||
dc.DrawLine(line.pen.Value, start, stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ namespace mbts.spark {
|
||||
public FillsVM(IBus bus, OrdersModel model, StartupData state)
|
||||
: this(bus, model, _get_observable(bus, model, state), state.sorting_criteria, state.filters) { }
|
||||
|
||||
FillsVM(IBus bus, OrdersModel model, IObservable<ITableUpdateTransform<FillTuple, string>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
|
||||
: base(RXCollection.CreateTableUpdate(fills_source), sorting_criteria) {
|
||||
FillsVM(IBus bus, OrdersModel model, IObservable<IIncrementalUpdate<FillTuple, string>> fills_source, IReadOnlyCollection<SortingCriteria> sorting_criteria, FilterStates filters)
|
||||
: base(RXCollection.CreateIncrementalUpdate(fills_source), sorting_criteria) {
|
||||
_model = model;
|
||||
account_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_accounts(), r => r.account, nameof(Row.account));
|
||||
user_filter = DiscreteValuesFilterVM.Create<Row, string>(_model.factory.subscribe_users(), r => r.user, nameof(Row.user));
|
||||
security_filter = DiscreteValuesFilterVM.Create<Row, SecurityImmutable, SecurityCriteria>(
|
||||
fills_source.with_respect_to(ft => ft.security ?? SecurityDataHelper.Defaults.SecurityImmutable, s => s.Id, ft => ft.key).Select(t => (t.added, t.removed)),
|
||||
fills_source.with_respect_to(ft => ft.security ?? SecurityDataHelper.Defaults.SecurityImmutable, s => s.Id, ft => ft.key).Select(t => (t.added.Values, t.removed.Values)),
|
||||
r => r.security,
|
||||
SecurityCriteria.from_securities,
|
||||
SecurityCriteria.get_to_securities(bus),
|
||||
@@ -126,7 +126,7 @@ namespace mbts.spark {
|
||||
protected override Row row_factory(FillTuple row_model) => new Row(row_model, _model);
|
||||
protected override IEnumerable<SortingCriteria> get_default_sorting() { yield return new SortingCriteria(nameof(Row.fill_time), false); }
|
||||
|
||||
static IObservable<ITableUpdateTransform<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, StartupData state) {
|
||||
static IObservable<IIncrementalUpdate<FillTuple, string>> _get_observable(IBus bus, OrdersModel model, StartupData state) {
|
||||
if (state.orders != null && state.orders.Length == 0) {
|
||||
var orders_hash = new HashSet<uint>(state.orders);
|
||||
return model.on_fill_tuples.filter(f => orders_hash.Contains(f.fill.OrderKey));
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace bts.shell {
|
||||
s.ask_break_line = LadderSettings.Default.ask_break_line;
|
||||
if (s.mid_break_line == null)
|
||||
s.mid_break_line = LadderSettings.Default.mid_break_line;
|
||||
s.row_height = LadderRender.measure_row_height(s);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,19 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Threading;
|
||||
|
||||
namespace mbts.spark {
|
||||
class FillTupleSource : IDisposable {
|
||||
public static IObservable<ITableUpdateTransform<FillTuple, string>> create_observable(
|
||||
public static IObservable<IIncrementalUpdate<FillTuple, string>> create_observable(
|
||||
IObservable<IReadOnlyDictionary<uint, SecurityMD>> securities,
|
||||
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders,
|
||||
IObservable<TableUpdate<FillImmutable, string>> fills
|
||||
) {
|
||||
FillTupleSource source = null;
|
||||
int ref_count = 0;
|
||||
return Observable.Create<TableUpdateTransform<FillTuple, string>>(observer => {
|
||||
return Observable.Create<IIncrementalUpdate<FillTuple, string>>(observer => {
|
||||
if (Interlocked.Increment(ref ref_count) == 1) {
|
||||
SparkUtils.lazy_assert(source == null, () => fmt_assert_msg("source should be null, there must be a leak."));
|
||||
source = new FillTupleSource(securities, orders, fills);
|
||||
@@ -40,35 +41,38 @@ namespace mbts.spark {
|
||||
IObservable<TableUpdate<OrderRecordImmutable, uint>> orders,
|
||||
IObservable<TableUpdate<FillImmutable, string>> fills) {
|
||||
State state = new State();
|
||||
var src = Observable.FromEvent<Action<TableUpdateTransform<FillTuple, string>>, TableUpdateTransform<FillTuple, string>>(cb => _updated += cb, cb => _updated -= cb);
|
||||
fill_tuples = Observable.Create<TableUpdateTransform<FillTuple, string>>(observer => {
|
||||
var init_state = state.fill_tuples.state.ToDictionary();
|
||||
observer.OnNext(new TableUpdateTransform<FillTuple, string>(init_state, init_state.Values.ToList(), new List<FillTuple>(0)));
|
||||
return src.Subscribe(observer);
|
||||
fill_tuples = Observable.Create<IIncrementalUpdate<FillTuple, string>>(observer => {
|
||||
observer.OnNext(new IncrementalUpdate<FillTuple, string>(state.full_state.ToDictionary(), new Dictionary<string, FillTuple>(0)));
|
||||
return _subject.Subscribe(observer);
|
||||
});
|
||||
_dispose = Disposable.Create(
|
||||
securities.ObserveOnDispatcher().Subscribe(s => update(state.update_securities(s))),
|
||||
orders.ObserveOnDispatcher().Subscribe(o => update(state.update_orders(o))),
|
||||
fills.ObserveOnDispatcher().Subscribe(f => update(state.update_fills(f)))
|
||||
);
|
||||
void update(State new_state) {
|
||||
state = new_state;
|
||||
_updated?.Invoke(state.fill_tuples);
|
||||
}
|
||||
securities.ObserveOnDispatcher().Subscribe(s => {
|
||||
state.update_securities(s);
|
||||
_subject.OnNext(state.last_update);
|
||||
}),
|
||||
orders.ObserveOnDispatcher().Subscribe(o => {
|
||||
state.update_orders(o);
|
||||
_subject.OnNext(state.last_update);
|
||||
}),
|
||||
fills.ObserveOnDispatcher().Subscribe(f => {
|
||||
state.update_fills(f);
|
||||
_subject.OnNext(state.last_update);
|
||||
}));
|
||||
}
|
||||
|
||||
event Action<TableUpdateTransform<FillTuple, string>> _updated;
|
||||
readonly Subject<IIncrementalUpdate<FillTuple, string>> _subject = new Subject<IIncrementalUpdate<FillTuple, string>>();
|
||||
|
||||
IObservable<TableUpdateTransform<FillTuple, string>> fill_tuples { get; }
|
||||
IObservable<IIncrementalUpdate<FillTuple, string>> fill_tuples { get; }
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
|
||||
class State {
|
||||
readonly IReadOnlyDictionary<string, FillImmutable> _fills;
|
||||
readonly IReadOnlyDictionary<uint, OrderRecordImmutable> _orders;
|
||||
readonly IReadOnlyDictionary<uint, SecurityMD> _securities;
|
||||
readonly TableUpdateTransform<FillTuple, string> _fill_tuples;
|
||||
IReadOnlyDictionary<string, FillImmutable> _fills;
|
||||
IReadOnlyDictionary<uint, OrderRecordImmutable> _orders;
|
||||
IReadOnlyDictionary<uint, SecurityMD> _securities;
|
||||
IncrementalUpdate<FillTuple, string> _last_update;
|
||||
readonly Dictionary<string, FillTuple> _state;
|
||||
readonly SetMap<uint, string> _order_fills;
|
||||
readonly SetMap<uint, string> _security_fills;
|
||||
|
||||
@@ -76,7 +80,8 @@ namespace mbts.spark {
|
||||
new Dictionary<string, FillImmutable>(),
|
||||
new Dictionary<uint, OrderRecordImmutable>(),
|
||||
new Dictionary<uint, SecurityMD>(),
|
||||
new TableUpdateTransform<FillTuple, string>(new Dictionary<string, FillTuple>(), new List<FillTuple>(), new List<FillTuple>()),
|
||||
new IncrementalUpdate<FillTuple, string>(new Dictionary<string, FillTuple>(), new Dictionary<string, FillTuple>()),
|
||||
new Dictionary<string, FillTuple>(),
|
||||
new SetMap<uint, string>(),
|
||||
new SetMap<uint, string>()
|
||||
) { }
|
||||
@@ -85,94 +90,117 @@ namespace mbts.spark {
|
||||
IReadOnlyDictionary<string, FillImmutable> fills,
|
||||
IReadOnlyDictionary<uint, OrderRecordImmutable> orders,
|
||||
IReadOnlyDictionary<uint, SecurityMD> securities,
|
||||
TableUpdateTransform<FillTuple, string> fill_tuples,
|
||||
IncrementalUpdate<FillTuple, string> last_update,
|
||||
Dictionary<string, FillTuple> state,
|
||||
SetMap<uint, string> order_fill_tuples,
|
||||
SetMap<uint, string> security_fill_tuples
|
||||
) {
|
||||
_fills = fills;
|
||||
_orders = orders;
|
||||
_securities = securities;
|
||||
_fill_tuples = fill_tuples;
|
||||
_last_update = last_update;
|
||||
_state = state;
|
||||
_order_fills = order_fill_tuples;
|
||||
_security_fills = security_fill_tuples;
|
||||
}
|
||||
|
||||
public TableUpdateTransform<FillTuple, string> fill_tuples => _fill_tuples;
|
||||
public IncrementalUpdate<FillTuple, string> last_update => _last_update;
|
||||
public IReadOnlyDictionary<string, FillTuple> full_state => _state;
|
||||
|
||||
public State update_fills(TableUpdate<FillImmutable, string> fills) {
|
||||
var removed = fills.removed.Select(get_fill_tuple).ToList();
|
||||
var added = fills.added.Select(get_fill_tuple).ToList();
|
||||
var order_fills = _order_fills.clone();
|
||||
var security_fills = _security_fills.clone();
|
||||
public void update_fills(TableUpdate<FillImmutable, string> fills) {
|
||||
var removed = fills.removed.ToDictionary(f => f.FillKey, get_fill_tuple);
|
||||
var added = fills.added.ToDictionary(f => f.FillKey, get_fill_tuple);
|
||||
HashSet<string> added_keys = new HashSet<string>(fills.added.Select(f => f.FillKey)),
|
||||
removed_keys = new HashSet<string>(fills.removed.Select(f => f.FillKey));
|
||||
foreach (var ft in removed) {
|
||||
string k = ft.fill.FillKey;
|
||||
if (!added_keys.Contains(k)) {
|
||||
order_fills.remove(ft.fill.OrderKey, k);
|
||||
security_fills.remove(ft.fill.SecurityKey, k);
|
||||
foreach (var kv in removed) {
|
||||
_state.Remove(kv.Key);
|
||||
if (!added_keys.Contains(kv.Key)) {
|
||||
_order_fills.remove(kv.Value.fill.OrderKey, kv.Key);
|
||||
_security_fills.remove(kv.Value.fill.SecurityKey, kv.Key);
|
||||
}
|
||||
}
|
||||
foreach (var ft in added) {
|
||||
string k = ft.fill.FillKey;
|
||||
if (!removed_keys.Contains(k)) {
|
||||
order_fills.add(ft.fill.OrderKey, k);
|
||||
security_fills.add(ft.fill.SecurityKey, k);
|
||||
foreach (var kv in added) {
|
||||
_state.Add(kv.Key, kv.Value);
|
||||
if (!removed_keys.Contains(kv.Key)) {
|
||||
_order_fills.add(kv.Value.fill.OrderKey, kv.Key);
|
||||
_security_fills.add(kv.Value.fill.SecurityKey, kv.Key);
|
||||
}
|
||||
}
|
||||
return new State(fills.state, _orders, _securities, _fill_tuples.update(removed, added, f => f.key), order_fills, security_fills);
|
||||
_fills = fills.state;
|
||||
_last_update = new IncrementalUpdate<FillTuple, string>(added, removed);
|
||||
//
|
||||
FillTuple get_fill_tuple(FillImmutable fill) =>
|
||||
new FillTuple(fill, _orders.TryGetValue(fill.OrderKey, out OrderRecordImmutable o) ? o : new OrderRecordImmutable?(),
|
||||
_securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
|
||||
}
|
||||
|
||||
public State update_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
|
||||
List<FillTuple> added = new List<FillTuple>(), removed = new List<FillTuple>();
|
||||
orders.detect_changed_keys(o => o.Key, out HashSet<uint> removed_keys, out HashSet<uint> added_keys, out HashSet<uint> changed_keys);
|
||||
foreach (var k in removed_keys) {
|
||||
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(_orders[k], fill_keys));
|
||||
added.AddRange(get_fill_tuples(null, fill_keys));
|
||||
public void update_orders(TableUpdate<OrderRecordImmutable, uint> orders) {
|
||||
Dictionary<string, FillTuple> added = new Dictionary<string, FillTuple>(), removed = new Dictionary<string, FillTuple>();
|
||||
Dictionary<uint, (OrderRecordImmutable oldval, OrderRecordImmutable newval)> changed_orders = new Dictionary<uint, (OrderRecordImmutable oldval, OrderRecordImmutable newval)>();
|
||||
foreach (var o in orders.removed) {
|
||||
if (orders.try_get(o.Key, out OrderRecordImmutable newval))
|
||||
changed_orders.Add(o.Key, (o, newval));
|
||||
else if (_order_fills.try_lookup(o.Key, out ISet<string> fill_keys)) {
|
||||
foreach (FillTuple ft in get_fill_tuples(o, fill_keys))
|
||||
removed.Add(ft.key, ft);
|
||||
foreach (FillTuple ft in get_fill_tuples(null, fill_keys))
|
||||
added.Add(ft.key, ft);
|
||||
}
|
||||
}
|
||||
foreach (var k in added_keys) {
|
||||
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(null, fill_keys));
|
||||
added.AddRange(get_fill_tuples(orders.get(k), fill_keys));
|
||||
foreach (var o in orders.added) {
|
||||
if (!changed_orders.ContainsKey(o.Key) && _order_fills.try_lookup(o.Key, out ISet<string> fill_keys)) {
|
||||
foreach (FillTuple ft in get_fill_tuples(null, fill_keys))
|
||||
removed.Add(ft.key, ft);
|
||||
foreach (FillTuple ft in get_fill_tuples(o, fill_keys))
|
||||
added.Add(ft.key, ft);
|
||||
}
|
||||
}
|
||||
foreach (var k in changed_keys) {
|
||||
if (_order_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(_orders[k], fill_keys));
|
||||
added.AddRange(get_fill_tuples(orders.get(k), fill_keys));
|
||||
foreach (var kv in changed_orders) {
|
||||
if (_order_fills.try_lookup(kv.Key, out ISet<string> fill_keys)) {
|
||||
var fs_pairs = get_fill_security_pairs(fill_keys).ToList();
|
||||
foreach (FillTuple ft in fs_pairs.Select(fsp => new FillTuple(fsp.fill, kv.Value.oldval, fsp.security)))
|
||||
removed.Add(ft.key, ft);
|
||||
foreach (FillTuple ft in fs_pairs.Select(fsp => new FillTuple(fsp.fill, kv.Value.newval, fsp.security)))
|
||||
added.Add(ft.key, ft);
|
||||
}
|
||||
}
|
||||
return new State(_fills, orders.state, _securities, _fill_tuples.update(removed, added, ft => ft.key), _order_fills, _security_fills);
|
||||
_orders = orders.state;
|
||||
_last_update = new IncrementalUpdate<FillTuple, string>(added, removed);
|
||||
//
|
||||
IEnumerable<FillTuple> get_fill_tuples(OrderRecordImmutable? o, ISet<string> fill_keys) =>
|
||||
fill_keys.Select(k => {
|
||||
var fill = _fills[k];
|
||||
return new FillTuple(fill, o, _securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
|
||||
});
|
||||
|
||||
IEnumerable<(FillImmutable fill, SecurityImmutable? security)> get_fill_security_pairs(ISet<string> fill_keys) =>
|
||||
fill_keys.Select(k => {
|
||||
var fill = _fills[k];
|
||||
return (fill, _securities.TryGetValue(fill.SecurityKey, out SecurityMD s) ? s.security : new SecurityImmutable?());
|
||||
});
|
||||
}
|
||||
|
||||
public State update_securities(IReadOnlyDictionary<uint, SecurityMD> securities) {
|
||||
List<FillTuple> added = new List<FillTuple>(), removed = new List<FillTuple>();
|
||||
public void update_securities(IReadOnlyDictionary<uint, SecurityMD> securities) {
|
||||
Dictionary<string, FillTuple> added = new Dictionary<string, FillTuple>(), removed = new Dictionary<string, FillTuple>();
|
||||
_securities.Keys.difference(securities.Keys, out IEnumerable<uint> added_keys, out IEnumerable<uint> removed_keys);
|
||||
foreach (var k in removed_keys) {
|
||||
if (_security_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(_securities[k].security, fill_keys));
|
||||
added.AddRange(get_fill_tuples(null, fill_keys));
|
||||
foreach (FillTuple ft in get_fill_tuples(_securities[k].security, fill_keys))
|
||||
removed.Add(ft.key, ft);
|
||||
foreach (FillTuple ft in get_fill_tuples(null, fill_keys))
|
||||
added.Add(ft.key, ft);
|
||||
}
|
||||
}
|
||||
foreach (var k in added_keys) {
|
||||
if (_security_fills.try_lookup(k, out ISet<string> fill_keys)) {
|
||||
removed.AddRange(get_fill_tuples(null, fill_keys));
|
||||
added.AddRange(get_fill_tuples(securities[k].security, fill_keys));
|
||||
foreach (FillTuple ft in get_fill_tuples(null, fill_keys))
|
||||
removed.Add(ft.key, ft);
|
||||
foreach (FillTuple ft in get_fill_tuples(securities[k].security, fill_keys))
|
||||
added.Add(ft.key, ft);
|
||||
}
|
||||
}
|
||||
return new State(_fills, _orders, securities, _fill_tuples.update(removed, added, f => f.key), _order_fills, _security_fills);
|
||||
_securities = securities;
|
||||
_last_update = new IncrementalUpdate<FillTuple, string>(added, removed);
|
||||
//
|
||||
IEnumerable<FillTuple> get_fill_tuples(SecurityImmutable? s, ISet<string> fill_keys) =>
|
||||
fill_keys.Select(k => {
|
||||
|
||||
@@ -20,6 +20,10 @@ 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; }
|
||||
@@ -33,6 +37,10 @@ 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
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,6 +14,10 @@ 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);
|
||||
|
||||
@@ -27,6 +31,10 @@ 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,15 +1,17 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
private class SpreaderOrderVM : OrderTypeVMBase<SpreaderOrderDefinition>, IOrderTypeVM, IDisposable {
|
||||
readonly Property<SpreaderOrderDefinitionImmutable?> _template;
|
||||
readonly StrategyEditorWrapper _strategy_editor_wrapper;
|
||||
IDisposable _dispose;
|
||||
SyntheticEditorVM _strategy_editor;
|
||||
|
||||
public SpreaderOrderVM(OrdersModel model, IObservable<SecurityImmutable> security_source)
|
||||
: this(model, security_source, security_source.SelectMany(s => model.spreader.security_strategy(s.Id)
|
||||
@@ -26,15 +28,15 @@ namespace mbts.spark {
|
||||
IObservable<InlineUserProperty<SpreaderOrderDefinitionImmutable>> order_template_source)
|
||||
: base(model) {
|
||||
_template = backing(nameof(template), new SpreaderOrderDefinitionImmutable?());
|
||||
_strategy_editor_wrapper = new StrategyEditorWrapper(this);
|
||||
_dispose = Disposable.Create(
|
||||
_strategy_editor_wrapper,
|
||||
_template.ObserveOnDispatcher().Subscribe(vals => {
|
||||
_validate();
|
||||
}),
|
||||
order_template_source.ObserveOnDispatcher().Subscribe(prop => {
|
||||
if (prop.force_override || (!template.HasValue && prop.has_value)) {
|
||||
_template.Value = prop.value;
|
||||
if (_strategy_editor != null)
|
||||
_strategy_editor.set_synthetic(prop.value.Security, prop.value.ExecutionStrategy);
|
||||
_validate();
|
||||
}
|
||||
})
|
||||
@@ -43,23 +45,14 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public SpreaderOrderDefinitionImmutable? template => _template.Value;
|
||||
|
||||
public SyntheticEditorVM strategy_editor {
|
||||
get {
|
||||
if (_strategy_editor == null && template.HasValue) {
|
||||
var def = template.Value;
|
||||
_strategy_editor = new SyntheticEditorVM(model.spreader, def.Security, def.ExecutionStrategy, model.security_has_working_orders(def.Security.Id));
|
||||
}
|
||||
return _strategy_editor;
|
||||
}
|
||||
}
|
||||
|
||||
public SyntheticEditorVM strategy_editor => _strategy_editor_wrapper.vm;
|
||||
public IEnumerable<DialogSkirtItem> skirt_items => _strategy_editor_wrapper.skirt_items;
|
||||
public override bool editable_properties_open {
|
||||
get => base.editable_properties_open;
|
||||
set {
|
||||
if (!value && template.HasValue && _strategy_editor != null) {
|
||||
if (!value && template.HasValue && strategy_editor != null) {
|
||||
var mutable_template = template.Value.CloneToMutable();
|
||||
mutable_template.ExecutionStrategy = _strategy_editor.get_strategy().CloneToMutable();
|
||||
mutable_template.ExecutionStrategy = _strategy_editor_wrapper.vm.get_strategy().CloneToMutable();
|
||||
_template.Value = new SpreaderOrderDefinitionImmutable(mutable_template);
|
||||
}
|
||||
base.editable_properties_open = value;
|
||||
@@ -80,6 +73,39 @@ namespace mbts.spark {
|
||||
set_error(nameof(template), "Waiting for security and/or strategy.", true);
|
||||
else clear_error(nameof(template));
|
||||
}
|
||||
|
||||
// lazily initializes SyntheticEditorVM and associated dialog skirt items
|
||||
class StrategyEditorWrapper : IDisposable {
|
||||
readonly SpreaderOrderVM _owner;
|
||||
SyntheticEditorVM _vm;
|
||||
DialogSkirtItem _save_strategy_item;
|
||||
|
||||
public StrategyEditorWrapper(SpreaderOrderVM owner) {
|
||||
_owner = owner;
|
||||
_owner._template.Changed += _order_template_changed;
|
||||
}
|
||||
|
||||
public SyntheticEditorVM vm { get { _init(); return _vm; } }
|
||||
public IEnumerable<DialogSkirtItem> skirt_items { get { _init(); yield return _save_strategy_item; } }
|
||||
|
||||
public void Dispose() {
|
||||
Disposable.dispose(ref _vm);
|
||||
_owner._template.Changed -= _order_template_changed;
|
||||
}
|
||||
|
||||
void _init() {
|
||||
if (_vm == null && _owner.template.HasValue) {
|
||||
var def = _owner.template.Value;
|
||||
_vm = new SyntheticEditorVM(_owner.model.spreader, def.Security, def.ExecutionStrategy, _owner.model.security_has_working_orders(def.Security.Id));
|
||||
_save_strategy_item = new DialogSkirtItem("Save Strategy", _vm.commit);
|
||||
}
|
||||
}
|
||||
|
||||
void _order_template_changed(SpreaderOrderDefinitionImmutable? oldval, SpreaderOrderDefinitionImmutable? newval) {
|
||||
if (_vm != null && newval.HasValue)
|
||||
_vm.set_synthetic(newval.Value.Security, newval.Value.ExecutionStrategy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace mbts.spark {
|
||||
public SpreaderModel spreader => _spreader;
|
||||
public IObservable<TableUpdate<OrderRecordImmutable, uint>> on_orders { get; }
|
||||
public IObservable<TableUpdate<FillImmutable, string>> on_fills { get; }
|
||||
public IObservable<ITableUpdateTransform<FillTuple, string>> on_fill_tuples { get; }
|
||||
public IObservable<IIncrementalUpdate<FillTuple, string>> on_fill_tuples { get; }
|
||||
public IObservable<IReadOnlyDictionary<uint,OrderRecordImmutable>> working_orders { get; }
|
||||
|
||||
public IObservable<ITableUpdateTransform<OrderRecordImmutable, uint>> on_security_orders(uint security) =>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<StartupObject>mbts.ui.shell.App</StartupObject>
|
||||
<RootNamespace>bts</RootNamespace>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
|
||||
@@ -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 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 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 class AW2 {
|
||||
|
||||
@@ -54,5 +54,33 @@
|
||||
rv |= try_convert(args[1], out arg2);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static bool try_convert_args<T1, T2, T3>(object[] args, out T1 arg1, out T2 arg2, out T3 arg3) {
|
||||
if (args == null || args.Length != 3) {
|
||||
arg1 = default;
|
||||
arg2 = default;
|
||||
arg3 = default;
|
||||
return false;
|
||||
}
|
||||
bool rv = try_convert(args[0], out arg1);
|
||||
rv |= try_convert(args[1], out arg2);
|
||||
rv |= try_convert(args[2], out arg3);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static bool try_convert_args<T1, T2, T3, T4>(object[] args, out T1 arg1, out T2 arg2, out T3 arg3, out T4 arg4) {
|
||||
if (args == null || args.Length != 4) {
|
||||
arg1 = default;
|
||||
arg2 = default;
|
||||
arg3 = default;
|
||||
arg4 = default;
|
||||
return false;
|
||||
}
|
||||
bool rv = try_convert(args[0], out arg1);
|
||||
rv |= try_convert(args[1], out arg2);
|
||||
rv |= try_convert(args[2], out arg3);
|
||||
rv |= try_convert(args[3], out arg4);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,27 @@ using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.utils {
|
||||
interface IIncrementalUpdate<T, TKey> where TKey : IEquatable<TKey> {
|
||||
IReadOnlyDictionary<TKey, T> added { get; }
|
||||
IReadOnlyDictionary<TKey, T> removed { get; }
|
||||
}
|
||||
|
||||
interface ITableUpdateTransform<T, TKey> where TKey : IEquatable<TKey> {
|
||||
IReadOnlyDictionary<TKey, T> state { get; }
|
||||
IReadOnlyList<T> added { get; }
|
||||
IReadOnlyList<T> removed { get; }
|
||||
}
|
||||
|
||||
class IncrementalUpdate<T, TKey> : IIncrementalUpdate<T, TKey> where TKey : IEquatable<TKey> {
|
||||
public IncrementalUpdate(IReadOnlyDictionary<TKey, T> added, IReadOnlyDictionary<TKey, T> removed) {
|
||||
this.added = added;
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<TKey, T> added { get; }
|
||||
public IReadOnlyDictionary<TKey, T> removed { get; }
|
||||
}
|
||||
|
||||
class TableUpdateTransform<T, TKey> : ITableUpdateTransform<T, TKey> where TKey : IEquatable<TKey> {
|
||||
readonly Dictionary<TKey, T> _state;
|
||||
readonly IReadOnlyList<T> _added, _removed;
|
||||
@@ -44,6 +59,15 @@ namespace mbts.utils {
|
||||
table._state.Add(key(item), item);
|
||||
return table;
|
||||
}
|
||||
|
||||
public TableUpdateTransform<T, TKey> update(IIncrementalUpdate<T, TKey> update, Func<T, TKey> key) {
|
||||
var table = new TableUpdateTransform<T, TKey>(new Dictionary<TKey, T>(_state), added, removed);
|
||||
foreach (var k in update.removed.Keys)
|
||||
table._state.Remove(k);
|
||||
foreach (var kv in update.added)
|
||||
table._state.Add(kv.Key, kv.Value);
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
[Browsable(false)]
|
||||
@@ -69,6 +93,33 @@ namespace mbts.utils {
|
||||
source.Scan(new PivotTableUpdateTransform<T, TKey, TResult, TResultKey>(selector, result_key, key),
|
||||
(pivot, table) => pivot.update(table));
|
||||
|
||||
/// <summary>
|
||||
/// Converts sequence to show incremental changes with respect to the [TResult] value returned by result_selector.
|
||||
/// </summary>
|
||||
public static IObservable<IIncrementalUpdate<TResult, TResultKey>> with_respect_to<T, TKey, TResult, TResultKey>(
|
||||
this IObservable<IIncrementalUpdate<T, TKey>> source, Func<T, TResult> selector, Func<TResult, TResultKey> result_key, Func<T, TKey> key)
|
||||
where TKey : IEquatable<TKey>
|
||||
where TResultKey : IEquatable<TResultKey> =>
|
||||
//
|
||||
source.Scan(new PivotIncrementalUpdate<T, TKey, TResult, TResultKey>(selector, result_key, key),
|
||||
(pivot, table) => pivot.update(table));
|
||||
|
||||
/// <summary>
|
||||
/// Uses a Scan operator to accumulate state in the resulting ITableUpdateTransform sequence.
|
||||
/// </summary>
|
||||
public static IObservable<ITableUpdateTransform<T, TKey>> table<T, TKey>(this IObservable<IIncrementalUpdate<T, TKey>> source, Func<T, TKey> key) where TKey : IEquatable<TKey> =>
|
||||
source.Scan(
|
||||
new TableUpdateTransform<T, TKey>(),
|
||||
(table, update) => table.update(update, key));
|
||||
|
||||
/// <summary>
|
||||
/// Filters the incremental added/removed lists by a predicate. Observers are only notified when the filtered added/removed lists have elements.
|
||||
/// </summary>
|
||||
public static IObservable<IIncrementalUpdate<T, TKey>> filter<T, TKey>(this IObservable<IIncrementalUpdate<T, TKey>> source, Func<T, bool> predicate)
|
||||
where TKey : IEquatable<TKey> => source
|
||||
.Select(u => new IncrementalUpdate<T, TKey>(u.added.Where(kv => predicate(kv.Value)).ToDictionary(), u.removed.Where(kv => predicate(kv.Value)).ToDictionary()))
|
||||
.Where(u => u.added.Count > 0 || u.removed.Count > 0);
|
||||
|
||||
class FilteredTableUpdateTransform<T, TKey> : ITableUpdateTransform<T, TKey> where TKey : IEquatable<TKey> {
|
||||
internal FilteredTableUpdateTransform(ITableUpdateTransform<T, TKey> source, Func<T, bool> predicate) {
|
||||
state = source.state;
|
||||
@@ -81,6 +132,57 @@ namespace mbts.utils {
|
||||
public IReadOnlyList<T> removed { get; }
|
||||
}
|
||||
|
||||
class PivotIncrementalUpdate<T, TKey, TResult, TResultKey> : IIncrementalUpdate<TResult, TResultKey> where TKey : IEquatable<TKey> where TResultKey : IEquatable<TResultKey> {
|
||||
readonly Dictionary<TResultKey, TResult> _state;
|
||||
readonly SetMap<TResultKey, TKey> _key_map;
|
||||
readonly Func<T, TResult> _selector;
|
||||
readonly Func<TResult, TResultKey> _result_key;
|
||||
readonly Func<T, TKey> _key;
|
||||
|
||||
internal PivotIncrementalUpdate(Func<T, TResult> selector, Func<TResult, TResultKey> result_key, Func<T, TKey> key) :
|
||||
this(new Dictionary<TResultKey, TResult>(), new Dictionary<TResultKey, TResult>(0), new Dictionary<TResultKey, TResult>(0), new SetMap<TResultKey, TKey>(), selector, result_key, key) { }
|
||||
|
||||
PivotIncrementalUpdate(Dictionary<TResultKey, TResult> state, IReadOnlyDictionary<TResultKey, TResult> added, IReadOnlyDictionary<TResultKey, TResult> removed,
|
||||
SetMap<TResultKey, TKey> key_map, Func<T, TResult> selector, Func<TResult, TResultKey> result_key, Func<T, TKey> key) {
|
||||
//
|
||||
_state = state;
|
||||
this.added = added;
|
||||
this.removed = removed;
|
||||
_key_map = key_map;
|
||||
_selector = selector;
|
||||
_result_key = result_key;
|
||||
_key = key;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<TResultKey, TResult> state => _state;
|
||||
public IReadOnlyDictionary<TResultKey, TResult> added { get; }
|
||||
public IReadOnlyDictionary<TResultKey, TResult> removed { get; }
|
||||
|
||||
public PivotIncrementalUpdate<T, TKey, TResult, TResultKey> update(IIncrementalUpdate<T, TKey> table) {
|
||||
Dictionary<TResultKey, TResult> removed = new Dictionary<TResultKey, TResult>(), added = new Dictionary<TResultKey, TResult>();
|
||||
var state_clone = new Dictionary<TResultKey, TResult>(_state);
|
||||
foreach (var kv in table.removed) {
|
||||
var result = _selector(kv.Value);
|
||||
var rkey = _result_key(result);
|
||||
if (_key_map.removes_key(rkey, kv.Key))
|
||||
removed.Add(rkey, result);
|
||||
}
|
||||
foreach (var kv in table.added) {
|
||||
var result = _selector(kv.Value);
|
||||
var rkey = _result_key(result);
|
||||
if (_key_map.adds_key(rkey, kv.Key)) {
|
||||
if (!removed.Remove(rkey))
|
||||
added.Add(rkey, result);
|
||||
}
|
||||
}
|
||||
foreach (var k in removed.Keys)
|
||||
state_clone.Remove(k);
|
||||
foreach (var kv in added)
|
||||
state_clone.Add(kv.Key, kv.Value);
|
||||
return new PivotIncrementalUpdate<T, TKey, TResult, TResultKey>(state_clone, added, removed, _key_map, _selector, _result_key, _key);
|
||||
}
|
||||
}
|
||||
|
||||
class PivotTableUpdateTransform<T, TKey, TResult, TResultKey> : ITableUpdateTransform<TResult, TResultKey> where TKey : IEquatable<TKey> where TResultKey : IEquatable<TResultKey> {
|
||||
readonly Dictionary<TResultKey, TResult> _state;
|
||||
readonly SetMap<TResultKey, TKey> _key_map;
|
||||
|
||||
@@ -1,141 +1,220 @@
|
||||
using mbts.spark;
|
||||
using mbts.ui.extensions;
|
||||
using bts.api;
|
||||
using mbts.spark;
|
||||
using mbts.ui.utility.text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using static mbts.config.Configuration;
|
||||
using FontFamily = System.Windows.Media.FontFamily;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
public class Badge: Control {
|
||||
readonly static double _dpi = get<bts.config.System>().dpi_ratio;
|
||||
public class Badge : Control {
|
||||
public const double LINE_SPACING = 1;
|
||||
// Storing the defaults here for readability
|
||||
// Warning: these properties are used to calculate the ladder's row_height. Please change the values below if you want to change properties that affect the layout.
|
||||
// ** - value is not the actual PropertyMetadata.DefaultValue
|
||||
public static readonly Thickness
|
||||
DefaultMargin = new Thickness(4, 2, 4, 2), // **
|
||||
DefaultPadding = new Thickness(1),
|
||||
DefaultBorderThickness = new Thickness(2);
|
||||
public static readonly double
|
||||
DefaultCornerRadius = 1,
|
||||
DefaultFontSize = 10,
|
||||
DefaultWidth = 63; // **
|
||||
public static readonly FontFamily DefaultFontFamily = new FontFamily("Consolas");
|
||||
|
||||
readonly GlyphContentRect _gcr = new GlyphContentRect();
|
||||
Brush _background, _foreground, _border_brush;
|
||||
|
||||
Typeface _face;
|
||||
FormattedText _center_text;
|
||||
FormattedText _top_text;
|
||||
FormattedText _bottom_text;
|
||||
static Badge() {
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(typeof(Badge)));
|
||||
WidthProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(15.0));
|
||||
HeightProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(10.0));
|
||||
FontSizeProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender /*| FrameworkPropertyMetadataOptions.AffectsMeasure*/, update_text));
|
||||
ForegroundProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(Brushes.White, update_text));
|
||||
BackgroundProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
BorderThicknessProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(new Thickness(1)));
|
||||
BorderBrushProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(Brushes.Black));
|
||||
FontFamilyProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(new FontFamily("Consolas"), FrameworkPropertyMetadataOptions.AffectsRender /*| FrameworkPropertyMetadataOptions.AffectsMeasure*/, update_text));
|
||||
|
||||
//TextProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, update_text));
|
||||
//TopTextProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, update_text));
|
||||
//BottomTextProperty.OverrideMetadata(typeof(Badge), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, update_text));
|
||||
// MinWidth
|
||||
WPFHelper.override_metadata<double, Badge>(
|
||||
MinWidthProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.min_width = v,
|
||||
0);
|
||||
// Width
|
||||
WPFHelper.override_metadata<double, Badge>(
|
||||
WidthProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.width = v,
|
||||
double.NaN);
|
||||
// Max Width
|
||||
WPFHelper.override_metadata<double, Badge>(
|
||||
MaxWidthProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.max_width = v,
|
||||
double.MaxValue);
|
||||
// Min Height
|
||||
WPFHelper.override_metadata<double, Badge>(
|
||||
MinHeightProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.min_height = v,
|
||||
0);
|
||||
// Height
|
||||
WPFHelper.override_metadata<double, Badge>(
|
||||
HeightProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.height = v,
|
||||
double.NaN);
|
||||
// Max Height
|
||||
WPFHelper.override_metadata<double, Badge>(
|
||||
MaxHeightProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.max_height = v,
|
||||
double.MaxValue);
|
||||
// FontSize
|
||||
WPFHelper.override_metadata<double, Badge>(
|
||||
FontSizeProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.font_size = v,
|
||||
DefaultFontSize);
|
||||
// Foreground
|
||||
WPFHelper.override_metadata<Brush, Badge>(
|
||||
ForegroundProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._foreground = v,
|
||||
Brushes.White);
|
||||
// Background
|
||||
WPFHelper.override_metadata<Brush, Badge>(
|
||||
BackgroundProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._background = v,
|
||||
Brushes.Transparent);
|
||||
// BorderThickness
|
||||
WPFHelper.override_metadata<Thickness, Badge>(
|
||||
BorderThicknessProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.border_thickness = v,
|
||||
DefaultBorderThickness);
|
||||
// BorderBrush
|
||||
WPFHelper.override_metadata<Brush, Badge>(
|
||||
BorderBrushProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._border_brush = v,
|
||||
Brushes.Black);
|
||||
// FontFamily
|
||||
WPFHelper.override_metadata<FontFamily, Badge>(
|
||||
FontFamilyProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.glyph_context = b._gcr.glyph_context.with(v),
|
||||
DefaultFontFamily);
|
||||
// HoriontalAlignment
|
||||
WPFHelper.override_metadata<HorizontalAlignment, Badge>(
|
||||
HorizontalAlignmentProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsArrange |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._gcr.horizontal_alignment = v,
|
||||
HorizontalAlignment.Stretch);
|
||||
// HoriontalContentAlignment
|
||||
WPFHelper.override_metadata<HorizontalAlignment, Badge>(
|
||||
HorizontalContentAlignmentProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsArrange |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._gcr.horizontal_content_alignment = v,
|
||||
HorizontalAlignment.Center);
|
||||
// VerticalAlignment
|
||||
WPFHelper.override_metadata<VerticalAlignment, Badge>(
|
||||
VerticalAlignmentProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsArrange |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._gcr.vertical_alignment = v,
|
||||
VerticalAlignment.Center);
|
||||
// VerticalContentAlignment
|
||||
WPFHelper.override_metadata<VerticalAlignment, Badge>(
|
||||
VerticalContentAlignmentProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsArrange |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._gcr.vertical_content_alignment = v,
|
||||
VerticalAlignment.Center);
|
||||
// Padding
|
||||
WPFHelper.override_metadata<Thickness, Badge>(
|
||||
PaddingProperty,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(p, v) => p._gcr.padding = v,
|
||||
DefaultPadding);
|
||||
}
|
||||
|
||||
public string Text {
|
||||
get { return (string)GetValue(TextProperty); }
|
||||
set { SetValue(TextProperty, value); }
|
||||
}
|
||||
public static readonly DependencyProperty TextProperty =
|
||||
DependencyProperty.Register("Text", typeof(string), typeof(Badge), new PropertyMetadata(string.Empty,
|
||||
format_text((b,v) => b._center_text= v)));
|
||||
|
||||
|
||||
public string TopText {
|
||||
get { return (string)GetValue(TopTextProperty); }
|
||||
set { SetValue(TopTextProperty, value); }
|
||||
}
|
||||
public static readonly DependencyProperty TopTextProperty =
|
||||
DependencyProperty.Register(nameof(TopText), typeof(string), typeof(Badge), new PropertyMetadata(string.Empty,
|
||||
format_text((b, v) => b._top_text = v)));
|
||||
|
||||
public string BottomText {
|
||||
get { return (string)GetValue(BottomTextProperty); }
|
||||
set { SetValue(BottomTextProperty, value); }
|
||||
public Badge() {
|
||||
_gcr.min_width = MinWidth;
|
||||
_gcr.width = Width;
|
||||
_gcr.max_width = MaxWidth;
|
||||
_gcr.min_height = MinHeight;
|
||||
_gcr.height = Height;
|
||||
_gcr.max_height = Height;
|
||||
_gcr.padding = Padding;
|
||||
_gcr.border_thickness = BorderThickness;
|
||||
_gcr.horizontal_alignment = HorizontalAlignment;
|
||||
_gcr.horizontal_content_alignment = HorizontalContentAlignment;
|
||||
_gcr.vertical_alignment = VerticalAlignment;
|
||||
_gcr.vertical_content_alignment = VerticalContentAlignment;
|
||||
_background = Background;
|
||||
_foreground = Foreground;
|
||||
_border_brush = BorderBrush;
|
||||
_gcr.font_size = FontSize;
|
||||
_gcr.text_lines = TextLines;
|
||||
_gcr.glyph_context = GlyphContext.create(FontFamily, FontWeight);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty BottomTextProperty =
|
||||
DependencyProperty.Register(nameof(BottomText), typeof(string), typeof(Badge), new PropertyMetadata(string.Empty,
|
||||
format_text((b,v)=>b._bottom_text=v)));
|
||||
public static readonly DependencyProperty TextLinesProperty =
|
||||
WPFHelper.create_dp<IReadOnlyList<string>, Badge>(
|
||||
nameof(TextLines),
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure |
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(b, v) => b._gcr.text_lines = v,
|
||||
new string[0]);
|
||||
|
||||
public IReadOnlyList<string> TextLines {
|
||||
get => GetValue(TextLinesProperty) as IReadOnlyList<string>;
|
||||
set => SetValue(TextLinesProperty, value);
|
||||
}
|
||||
|
||||
public double CornerRadius {
|
||||
get { return (double)GetValue(CornerRadiusProperty); }
|
||||
set { SetValue(CornerRadiusProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CornerRadiusProperty =
|
||||
DependencyProperty.Register("CornerRadius", typeof(double), typeof(Badge), new PropertyMetadata(3.0));
|
||||
WPFHelper.create_dp<double, Badge>(nameof(CornerRadius), DefaultCornerRadius);
|
||||
|
||||
static PropertyChangedCallback format_text(Action<Badge,FormattedText> f) {
|
||||
return (d, e) => {
|
||||
Badge b = (Badge)d;
|
||||
ensure_fontface(b);
|
||||
string text = e.NewValue as string;
|
||||
if (string.IsNullOrEmpty(text)) f(b, null);
|
||||
f(b, new FormattedText(text,
|
||||
System.Globalization.CultureInfo.CurrentCulture,
|
||||
FlowDirection.LeftToRight,
|
||||
b._face, b.FontSize, b.Foreground, _dpi));
|
||||
b.InvalidateVisual();
|
||||
};
|
||||
protected override Size ArrangeOverride(Size finalSize) => finalSize;
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize) {
|
||||
_gcr.available_size = availableSize;
|
||||
return _gcr.layout_size;
|
||||
}
|
||||
static FormattedText format_text(Badge badge, DependencyProperty source) {
|
||||
FormattedText result = null;
|
||||
var value = badge.GetValue(source);
|
||||
format_text((b, v) => result = v)(badge, new DependencyPropertyChangedEventArgs(source, value, value));
|
||||
return result;
|
||||
}
|
||||
|
||||
static void update_text(DependencyObject d, DependencyPropertyChangedEventArgs args) {
|
||||
// Force formatted text regeneration when font/color changes
|
||||
Badge badge = (Badge)d;
|
||||
badge._face = null;
|
||||
ensure_fontface(badge);
|
||||
badge._center_text = format_text(badge, TextProperty);
|
||||
badge._top_text = format_text(badge, TopTextProperty);
|
||||
badge._bottom_text = format_text(badge, BottomTextProperty);
|
||||
}
|
||||
|
||||
private static void ensure_fontface(Badge b) {
|
||||
b._face = b._face ?? new Typeface(b.FontFamily, b.FontStyle, b.FontWeight, b.FontStretch);
|
||||
}
|
||||
|
||||
//protected override Size MeasureOverride(Size availableSize) {
|
||||
// Double calc_height = 0.0;
|
||||
// Double calc_width = 0.0;
|
||||
// Size size = new Size(0, 0);
|
||||
|
||||
// Size center_size = new Size(_center_text.Width, _center_text.Height);
|
||||
// Size top_size = new Size(_top_text.Width, _top_text.Height);
|
||||
// Size bottom_size = new Size(_bottom_text.Width, _bottom_text.Height);
|
||||
|
||||
// calc_width = Math.Max(center_size.Width, top_size.Width);
|
||||
// calc_width = Math.Max(calc_width, bottom_size.Width);
|
||||
|
||||
// calc_height += center_size.Height;
|
||||
// calc_height += top_size.Height;
|
||||
// calc_height += bottom_size.Height;
|
||||
|
||||
// size.Width = calc_width;
|
||||
// size.Height = calc_height;
|
||||
|
||||
// return size;
|
||||
|
||||
//}
|
||||
|
||||
protected override void OnRender(DrawingContext dc) {
|
||||
base.OnRender(dc);
|
||||
double border_thickness = BorderThickness.Top;
|
||||
var pen = new Pen(BorderBrush, border_thickness);
|
||||
dc.DrawRoundedRectangle(Background, pen, new Rect(0, 0 + border_thickness, ActualWidth, ActualHeight - 2 * border_thickness), CornerRadius, CornerRadius);
|
||||
if (_center_text != null)
|
||||
dc.DrawText(_center_text, new Point((ActualWidth-_center_text.Width)/2, (ActualHeight-_center_text.Height)/2));
|
||||
if (_top_text != null)
|
||||
dc.DrawText(_top_text, new Point(border_thickness, border_thickness));
|
||||
if (_bottom_text != null)
|
||||
dc.DrawText(_bottom_text, new Point(border_thickness, ActualHeight - _bottom_text.Height - border_thickness));
|
||||
dc.DrawRoundedRectangle(Background, pen, new Rect(0, border_thickness, _gcr.layout_size.Width, _gcr.layout_size.Height), CornerRadius, CornerRadius);
|
||||
var gr = _gcr.glyph_run;
|
||||
if (gr != null)
|
||||
dc.DrawGlyphRun(_foreground, gr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BadgeTextLinesConverter : IMultiValueConverter {
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
|
||||
if (ObjectHelper.try_convert_args(values, out Side side, out uint quantity, out uint filled, out mbts.spark.LadderSettings.BadgeStyle style))
|
||||
return BadgeRender.format(side, quantity, filled, style);
|
||||
else return new string[0];
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ namespace mbts.spark.filters {
|
||||
) => new DynamicItemsFilter<TRow, TValue, TState>(items, get_row_val, get_state, load_state, prop_name, equality_comparer, comparer);
|
||||
|
||||
public static IDataGridColumnPersistentFilter<TRow, TState> Create<TRow, TValue, TState>(
|
||||
IObservable<(IReadOnlyList<TValue> added, IReadOnlyList<TValue> removed)> items,
|
||||
IObservable<(IEnumerable<TValue> added, IEnumerable<TValue> removed)> items,
|
||||
Func<TRow, TValue> get_row_val,
|
||||
Func<ISet<TValue>, TState> get_state,
|
||||
Func<TState, IObservable<IEnumerable<TValue>>> load_state,
|
||||
@@ -122,7 +122,7 @@ namespace mbts.spark.filters {
|
||||
}
|
||||
|
||||
public DynamicItemsFilter(
|
||||
IObservable<(IReadOnlyList<TValue> added, IReadOnlyList<TValue> removed)> items,
|
||||
IObservable<(IEnumerable<TValue> added, IEnumerable<TValue> removed)> items,
|
||||
Func<TRow, TValue> get_row_val,
|
||||
Func<ISet<TValue>, TState> get_state,
|
||||
Func<TState, IObservable<IEnumerable<TValue>>> load_state,
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
SelectedItem="{Binding Mode, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
/>
|
||||
<Grid x:Name="PeriodRoot" Grid.Row="1" Margin="0,6,0,0" IsEnabled="False">
|
||||
<Grid x:Name="PeriodRoot" Grid.Row="1" Margin="24,6,0,0" IsEnabled="False">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="4"/>
|
||||
|
||||
@@ -73,8 +73,9 @@ namespace mbts.ui.controls {
|
||||
public enum TimeFilterMode {
|
||||
Anytime,
|
||||
Today,
|
||||
[Description("Period:")]
|
||||
Period,
|
||||
[Description("Glbx Open")]
|
||||
[Description("Since Open")]
|
||||
CMEOpen
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
<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">
|
||||
@@ -155,6 +156,186 @@
|
||||
</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>
|
||||
@@ -180,21 +361,31 @@
|
||||
/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.IcebergOrderTemplate>
|
||||
</usp:OrderModificationTemplateSelector.IcebergOrderTemplate>
|
||||
<usp:OrderModificationTemplateSelector.SpreaderTemplate>
|
||||
<DataTemplate>
|
||||
<usp:ExecutionStrategyEditor
|
||||
HorizontalAlignment="Stretch" Margin="6"
|
||||
DataContext="{Binding strategy_editor}"
|
||||
SyntheticName="{Binding name}"
|
||||
IsNew="{Binding is_new}"
|
||||
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
|
||||
PriceCalc="{Binding price_calc}"
|
||||
ParameterRows="{Binding rows}"
|
||||
LegTuples="{Binding leg_tuples}"
|
||||
RemoveLegCommand="{Binding remove_leg}"
|
||||
ContractPickerModel="{Binding contract_picker_model}"
|
||||
/>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<usp:ExecutionStrategyEditor
|
||||
Margin="6"
|
||||
HorizontalAlignment="Stretch"
|
||||
DataContext="{Binding strategy_editor}"
|
||||
SyntheticName="{Binding name}"
|
||||
TickFactors="{Binding tick_factors}"
|
||||
AvailableTickFactors="{Binding available_tick_factors_view}"
|
||||
IsNew="False"
|
||||
IsNewLegPopupOpen="{Binding is_new_leg_popup_open}"
|
||||
PriceCalc="{Binding price_calc}"
|
||||
ParameterRows="{Binding rows}"
|
||||
LegTuples="{Binding leg_tuples}"
|
||||
RemoveLegCommand="{Binding remove_leg}"
|
||||
ContractPickerModel="{Binding contract_picker_model}"
|
||||
/>
|
||||
<c:DialogSkirt Grid.Row="1" Margin="-4" RightItems="{Binding skirt_items}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</usp:OrderModificationTemplateSelector.SpreaderTemplate>
|
||||
</usp:OrderModificationTemplateSelector>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<c:Badge Background="{Binding Metadata, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource OrderTypeBrushConverter}}" Width="15" Height="10" HorizontalAlignment="Left" />
|
||||
<c:Badge Background="{Binding Metadata, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource OrderTypeBrushConverter}}" BorderThickness="1" Width="15" Height="10" HorizontalAlignment="Left" />
|
||||
<TextBlock Text="{Binding OrderType, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource OrderTypeDisplayTextConverter}}" Margin="20,0,0,0" HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Window}">
|
||||
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Loaded="style_loaded" x:Name="window_border" u:WindowStyle.IsNonClientArea="true">
|
||||
<Border BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Loaded="style_loaded" x:Name="window_border" u:WindowStyle.IsNonClientArea="true">
|
||||
<Grid Margin="{TemplateBinding Margin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="20" />
|
||||
@@ -134,8 +134,8 @@
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=WindowState, RelativeSource={RelativeSource self}}" Value="Normal">
|
||||
<Setter Property="Padding" Value="0,0,0,0" />
|
||||
<Setter Property="BorderThickness" Value="7,1,2.66,2.66" TargetName="window_border" />
|
||||
<Setter Property="Padding" Value="0,0,2,2" />
|
||||
<Setter Property="BorderThickness" Value="7,1,1,1" TargetName="window_border" />
|
||||
</DataTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
|
||||
@@ -149,6 +149,15 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public static string abbreviate(this Side side) {
|
||||
switch (side) {
|
||||
case Side.Buy: return "B";
|
||||
case Side.Sell: return "S";
|
||||
case Side.Undefined:
|
||||
default: return "E";
|
||||
}
|
||||
}
|
||||
|
||||
public static string order_sub_type_display_name(OrderSubType sub_type, bool use_long_name) {
|
||||
switch(sub_type) {
|
||||
case OrderSubType.HedgingLeg:
|
||||
|
||||
@@ -13,9 +13,10 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
class LadderOrderVM : DisposableViewModelBase, IEquatable<LadderOrderVM> {
|
||||
class LadderOrderVM : DisposableViewModelBase {
|
||||
#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));
|
||||
@@ -34,7 +35,8 @@ namespace mbts.spark {
|
||||
readonly Property<FontFamily> _font_family;
|
||||
readonly Property<FontWeight> _font_weight;
|
||||
readonly Property<double> _font_size;
|
||||
readonly Property<Brush> _foreground;
|
||||
readonly Property<Brush> _foreground, _background, _hoverbackground, _border;
|
||||
readonly Property<string> _summary, _description;
|
||||
IObservable<LadderSettings.BadgeStyle> _bs;
|
||||
LadderSettings.BadgeStyle _current_badge_style;
|
||||
int _n_active_price_change_ops = 0;
|
||||
@@ -56,25 +58,26 @@ 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;
|
||||
Brush get_foreground(LadderSettings s) => s.orders_column.foreground.rgb_brush();
|
||||
_text = _bottom_line = _top_line = string.Empty;
|
||||
//
|
||||
add_order(o);
|
||||
_dispose = Disposable.Create(
|
||||
_bs.Subscribe( badge => {
|
||||
_bs.Subscribe(badge => {
|
||||
_current_badge_style = badge;
|
||||
_set_badge_text();
|
||||
})
|
||||
);
|
||||
|
||||
//
|
||||
add_order(o);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _init() {
|
||||
public void update() {
|
||||
// update brushes
|
||||
Color color;
|
||||
switch (side) {
|
||||
@@ -94,27 +97,23 @@ 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();
|
||||
}
|
||||
|
||||
private void _set_badge_text() {
|
||||
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;
|
||||
case LadderSettings.BadgeStyle.Normal:
|
||||
default:
|
||||
text = $"{filled}/{quantity}"; break;
|
||||
}
|
||||
}
|
||||
text_lines = BadgeRender.format(side, quantity, filled, _current_badge_style);
|
||||
RaisePropertyChanged(nameof(text_lines));
|
||||
}
|
||||
|
||||
public void add_order(OrderRecordImmutable new_order) {
|
||||
_orders[new_order.Key] = new_order;
|
||||
_init();
|
||||
}
|
||||
|
||||
public void remove_order(uint order_key) {
|
||||
_orders.Remove(order_key);
|
||||
}
|
||||
|
||||
//public void update(OrderRecordImmutable neworder) {
|
||||
@@ -137,57 +136,9 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
string _buy_sell_indicator {
|
||||
get {
|
||||
switch (side) {
|
||||
case Side.Buy: return "B";
|
||||
case Side.Sell: return "S";
|
||||
case Side.Undefined:
|
||||
default:
|
||||
return "E";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<OrderRecordImmutable> _parent_and_child_not_sharing_slot() => _orders.Values.Where(o => !_orders.ContainsKey(o.ParentKey));
|
||||
|
||||
private string _text;
|
||||
public string text {
|
||||
get { return _text; }
|
||||
private set {
|
||||
_text = value;
|
||||
if (value != string.Empty) {
|
||||
top_line = string.Empty;
|
||||
bottom_line = string.Empty;
|
||||
}
|
||||
RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
private string _top_line;
|
||||
public string top_line {
|
||||
get { return _top_line; }
|
||||
private set {
|
||||
_top_line = value;
|
||||
if (value != string.Empty) {
|
||||
text = string.Empty;
|
||||
}
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
private string _bottom_line;
|
||||
public string bottom_line {
|
||||
get { return _bottom_line; }
|
||||
private set {
|
||||
_bottom_line = value;
|
||||
if (value != string.Empty) {
|
||||
text = string.Empty;
|
||||
}
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
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 IReadOnlyList<string> text_lines { get; private set; }
|
||||
public bool has_parent => _orders.Values.Any( order => order.has_parent());
|
||||
public bool has_phantom_price => !double.IsNaN(_phantom_price);
|
||||
|
||||
@@ -204,10 +155,11 @@ 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; 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 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 IEnumerable<OrderRecordImmutable> orders => _orders.Values;
|
||||
|
||||
void _cancel_order() {
|
||||
@@ -239,40 +191,8 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
public override string ToString() {
|
||||
return $"Price: {this.price} Order Count: {this.orders.Count()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,8 +143,9 @@ namespace mbts.spark {
|
||||
return price_cell.panel;
|
||||
else if (_price_list != null && !double.IsNaN(_row_height)) {
|
||||
var panel = new StackPanel() {
|
||||
Margin = Badge.DefaultMargin,
|
||||
Orientation = Orientation.Horizontal,
|
||||
MaxWidth = _col_width,
|
||||
MaxWidth = _col_width
|
||||
//MaxHeight = _row_height,
|
||||
//ClipToBounds = true
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using bts.spark;
|
||||
using mbts.spark.utilities;
|
||||
using bts.api;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls.color_picker;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
@@ -14,6 +14,7 @@ namespace mbts.spark {
|
||||
class LadderSettingsVM : ViewModelBase, IWindowSettingsVM<LadderSettings> {
|
||||
readonly Property<LadderSettings.BadgeStyle> _badge_style;
|
||||
readonly Property<uint> _alternate_order_size;
|
||||
readonly DelegateProperty<double> _row_height;
|
||||
LadderSettings _current;
|
||||
LadderColumnSettingsVM
|
||||
_orders = new OrdersColumnSettingsVM(),
|
||||
@@ -30,6 +31,13 @@ namespace mbts.spark {
|
||||
_badge_style = backing<LadderSettings.BadgeStyle>(nameof(badge_style));
|
||||
_badge_style.Changed += _badge_style_Changed;
|
||||
_alternate_order_size = backing<uint>(nameof(alternate_order_size));
|
||||
_row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result()));
|
||||
_orders.font_size_changed += on_font_size_changed;
|
||||
_bid.font_size_changed += on_font_size_changed;
|
||||
_price.font_size_changed += on_font_size_changed;
|
||||
_ask.font_size_changed += on_font_size_changed;
|
||||
_last.font_size_changed += on_font_size_changed;
|
||||
void on_font_size_changed(double oldval, double newval) => _row_height.refresh();
|
||||
}
|
||||
|
||||
private void _badge_style_Changed(LadderSettings.BadgeStyle oldval, LadderSettings.BadgeStyle newval) {
|
||||
@@ -38,6 +46,7 @@ namespace mbts.spark {
|
||||
} else if (newval == LadderSettings.BadgeStyle.Normal && _orders.font_size == 7) {
|
||||
_orders.font_size = 10;
|
||||
}
|
||||
_row_height.refresh();
|
||||
}
|
||||
|
||||
public LadderSettings.BadgeStyle badge_style {
|
||||
@@ -50,6 +59,8 @@ namespace mbts.spark {
|
||||
set => _alternate_order_size.Value = value;
|
||||
}
|
||||
|
||||
public double row_height => _row_height.Value;
|
||||
|
||||
public IEnumerable<LadderColumnSettingsVM> column_items {
|
||||
get {
|
||||
yield return _orders;
|
||||
@@ -89,7 +100,6 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public LadderSettings get_result() {
|
||||
const double row_padding = 12;
|
||||
var clone = SparkUtils.clone_proto(_current);
|
||||
clone.badge_style = badge_style;
|
||||
clone.alternate_order_size = alternate_order_size;
|
||||
@@ -101,13 +111,7 @@ namespace mbts.spark {
|
||||
clone.bid_break_line = _bid_mkt_line.get_settings();
|
||||
clone.ask_break_line = _ask_mkt_line.get_settings();
|
||||
clone.mid_break_line = _mid_mkt_line.get_settings();
|
||||
clone.row_height = new[] {
|
||||
clone.orders_column,
|
||||
clone.bid_column,
|
||||
clone.price_column,
|
||||
clone.ask_column,
|
||||
clone.last_column
|
||||
}.Max(cs => cs.font_size) + row_padding;
|
||||
clone.row_height = LadderRender.measure_row_height(clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -131,6 +135,7 @@ namespace mbts.spark {
|
||||
_background = backing(nameof(background), Colors.White);
|
||||
_font_display_name = backing(nameof(font_display_name),
|
||||
() => TextUtils.display_name(font_family, font_weight, font_size), nameof(font_family), nameof(font_weight), nameof(font_size));
|
||||
_font_size.Changed += (oldval, newval) => font_size_changed?.Invoke(oldval, newval);
|
||||
}
|
||||
|
||||
public string name { get; }
|
||||
@@ -145,6 +150,7 @@ namespace mbts.spark {
|
||||
public Color background { get => _background.Value; set => _background.Value = value; }
|
||||
public ObservableCollection<ColorPaletteVM> foreground_palettes { get; }
|
||||
public ObservableCollection<ColorPaletteVM> background_palettes { get; }
|
||||
public event Action<double, double> font_size_changed;
|
||||
|
||||
public virtual string sample_text => "999-56789.01234";
|
||||
|
||||
@@ -178,7 +184,9 @@ namespace mbts.spark {
|
||||
public class OrdersColumnSettingsVM : LadderColumnSettingsVM, ITemplateSelectorKey {
|
||||
public OrdersColumnSettingsVM() : base("Orders") { }
|
||||
|
||||
public override string sample_text => "12/50";
|
||||
public uint sample_quantity { get; } = 50;
|
||||
public uint sample_filled { get; } = 12;
|
||||
public Side sample_side { get; } = Side.Buy;
|
||||
public Brush badge_border { get; } = new SolidColorBrush(BTSColors.MarketData.ask);
|
||||
public Brush badge_background { get; } = Brushes.White;
|
||||
|
||||
@@ -213,4 +221,4 @@ namespace mbts.spark {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
TextElement.FontSize="{x:Static u:FontSizes.Small}"
|
||||
>
|
||||
<UserControl.Resources>
|
||||
<c:BadgeTextLinesConverter x:Key="BadgeTextLinesConverter"/>
|
||||
<u:KeyedTemplateSelector x:Key="ColumnSampleTemplateSelector">
|
||||
<u:KeyedTemplateSelector.Default>
|
||||
<DataTemplate>
|
||||
@@ -28,18 +29,23 @@
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:LadderSettingsView.OrdersColumnSampleTemplateKey}">
|
||||
<DataTemplate>
|
||||
<c:Badge
|
||||
Width="50"
|
||||
Height="20"
|
||||
Background="{Binding badge_background}"
|
||||
BorderBrush="{Binding badge_border}"
|
||||
BorderThickness="3"
|
||||
FontFamily="{Binding font_family}"
|
||||
FontSize="{Binding font_size}"
|
||||
FontWeight="{Binding font_weight}"
|
||||
Foreground="{Binding foreground, Converter={StaticResource ColorToBrushConverter}}"
|
||||
Text="{Binding sample_text}"
|
||||
VerticalAlignment="Center"
|
||||
/>
|
||||
Width="{x:Static c:Badge.DefaultWidth}"
|
||||
>
|
||||
<c:Badge.TextLines>
|
||||
<MultiBinding Converter="{StaticResource BadgeTextLinesConverter}">
|
||||
<Binding Path="sample_side"/>
|
||||
<Binding Path="sample_quantity"/>
|
||||
<Binding Path="sample_filled"/>
|
||||
<Binding Path="DataContext.badge_style" RelativeSource="{RelativeSource AncestorType=usp:LadderSettingsView}"/>
|
||||
</MultiBinding>
|
||||
</c:Badge.TextLines>
|
||||
</c:Badge>
|
||||
</DataTemplate>
|
||||
</u:TemplateSelectorKeyValue>
|
||||
</u:KeyedTemplateSelector>
|
||||
@@ -82,7 +88,7 @@
|
||||
<ItemsControl Grid.Row="1" ItemsSource="{Binding column_items}" Margin="8,0">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Height="27">
|
||||
<Grid Height="{Binding DataContext.row_height, Mode=OneWay, RelativeSource={RelativeSource AncestorType=usp:LadderSettingsView}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition SharedSizeGroup="c1"/>
|
||||
@@ -124,7 +130,7 @@
|
||||
<c:FontPicker SelectedFontSize="{Binding font_size}" Typeface="{Binding typeface}"/>
|
||||
</c:PopupControl>
|
||||
<Border Grid.Column="4" Background="{Binding background, Converter={StaticResource ColorToBrushConverter}}" BorderBrush="Black" BorderThickness="2,0,0,0" Padding="4,0">
|
||||
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ColumnSampleTemplateSelector}"/>
|
||||
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ColumnSampleTemplateSelector}" VerticalContentAlignment="Center"/>
|
||||
</Border>
|
||||
<Rectangle Grid.Column="1" Grid.ColumnSpan="5" Fill="DimGray" Height="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"/>
|
||||
</Grid>
|
||||
|
||||
@@ -26,6 +26,7 @@ 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;
|
||||
@@ -62,46 +63,77 @@ 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
|
||||
// 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();
|
||||
|
||||
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();
|
||||
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 = uo.stop_price(); }
|
||||
if (filled == 0) { slot_price_target = wo.stop_price(); }
|
||||
} else if (order_type == CustomOrderDefinition.CustomOneofCase.StopLimit) {
|
||||
// consistent behavior as Stop order above.
|
||||
if (filled == 0) { slot_price_target = uo.stop_limit_price(); }
|
||||
if (filled == 0) { slot_price_target = wo.stop_limit_price(); }
|
||||
}
|
||||
|
||||
slot_price_vs_orders.TryGetValue(slot_price_target, out LadderOrderVM slot_vm);
|
||||
if (slot_vm != null) {
|
||||
slot_vm.add_order(uo);
|
||||
if (_slot_price_vs_orders.TryGetValue(slot_price_target, out LadderOrderVM slot_vm)) {
|
||||
slot_vm.add_order(wo);
|
||||
} else {
|
||||
slot_vm = new LadderOrderVM(uo, slot_price_target, this, _bus, _settings_manager.subscribe_current().Select(s => s.badge_style));
|
||||
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_price_vs_orders[slot_price_target] = slot_vm;
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
orders.sync_with(slot_price_vs_orders.Values);
|
||||
orders_cache = working;
|
||||
|
||||
// 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);
|
||||
}),
|
||||
WorkLoop.Start($"Ladder '{security}'", _tick)
|
||||
);
|
||||
@@ -152,7 +184,6 @@ 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,6 +227,41 @@
|
||||
</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>
|
||||
@@ -371,16 +406,7 @@
|
||||
<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" 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>
|
||||
<Button Grid.Row="1" HorizontalContentAlignment="Stretch" Command="{Binding settings_command}" Style="{StaticResource SettingsButton}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Border Grid.Column="2" HorizontalAlignment="Stretch">
|
||||
@@ -422,15 +448,9 @@
|
||||
<Setter Property="FontFamily" Value="{Binding font_family}"/>
|
||||
<Setter Property="FontSize" Value="{Binding font_size}"/>
|
||||
<Setter Property="Foreground" Value="{Binding foreground}"/>
|
||||
<Setter Property="Text" Value="{Binding text}" />
|
||||
<Setter Property="TopText" Value="{Binding top_line}" />
|
||||
<Setter Property="BottomText" Value="{Binding bottom_line}" />
|
||||
<Setter Property="BorderThickness" Value="2" />
|
||||
<Setter Property="Width" Value="63" />
|
||||
<Setter Property="Height" Value="23" />
|
||||
<Setter Property="Margin" Value="4,0,0,0" />
|
||||
<Setter Property="TextLines" Value="{Binding text_lines}" />
|
||||
<Setter Property="Width" Value="{x:Static controls:Badge.DefaultWidth}"/>
|
||||
<Setter Property="ToolTip" Value="{Binding description}" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
@@ -481,4 +501,4 @@
|
||||
</Button>
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
@@ -1,20 +1,61 @@
|
||||
using bts.api;
|
||||
using mbts.co2;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.utility.text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.spark {
|
||||
static class BadgeRender {
|
||||
// IMPORTANT: the return values for this method MUST BE IN SYNC with the "format" method, else "measure_height" and "LadderRender.measure_row_height" will be broken.
|
||||
static int _line_count_for_style(LadderSettings.BadgeStyle badge_style) => badge_style == LadderSettings.BadgeStyle.Alternate ? 2 : 1;
|
||||
// IMPORTANT: at the risk of being redundant, see above comment.
|
||||
public static IReadOnlyList<string> format(Side side, uint quantity, uint filled, LadderSettings.BadgeStyle style) {
|
||||
switch (style) {
|
||||
case LadderSettings.BadgeStyle.Alternate:
|
||||
return new[] {
|
||||
$"{side.abbreviate()}:{filled}",
|
||||
$"W:{quantity - filled}"
|
||||
};
|
||||
default: return new[] { $"{filled}/{quantity}" };
|
||||
}
|
||||
}
|
||||
|
||||
public static double measure_height(LadderSettings.BadgeStyle badge_style, Thickness border_thickness, Thickness padding, double font_size) =>
|
||||
measure_height(_line_count_for_style(badge_style), border_thickness, padding, font_size);
|
||||
|
||||
public static double measure_height(int line_count, Thickness border_thickness, Thickness padding, double font_size) =>
|
||||
WPFHelper.control_base_height(border_thickness, padding) + font_size * line_count + Badge.LINE_SPACING * (line_count - 1);
|
||||
}
|
||||
|
||||
static class LadderRender {
|
||||
// Note: the orders-column has a separate "row_padding" value: Badge.DefaultMargin
|
||||
// (see how it is incorporated in measure_row_height() below)
|
||||
public static readonly Thickness row_padding = new Thickness(6);
|
||||
|
||||
// IMPORTANT: this method ONLY WORKS if Badges use the BadgeRender.margin/border_thickness/padding values.
|
||||
// TODO: elminate the dependency on BadgeRender.margin/border_thickness/padding by integrating them into settings.
|
||||
public static double measure_row_height(LadderSettings settings) =>
|
||||
Math.Max(new[] {
|
||||
settings.bid_column.font_size,
|
||||
settings.price_column.font_size,
|
||||
settings.ask_column.font_size,
|
||||
settings.last_column.font_size
|
||||
}.Max() + row_padding.Top + row_padding.Bottom,
|
||||
BadgeRender.measure_height(
|
||||
settings.badge_style, Badge.DefaultBorderThickness, Badge.DefaultPadding, settings.orders_column.font_size
|
||||
) + Badge.DefaultMargin.Top + Badge.DefaultMargin.Bottom);
|
||||
|
||||
public static int index_of(double[] prices, double price, IComparer<double> comparer) =>
|
||||
Array.BinarySearch(prices, price, comparer);
|
||||
|
||||
public static GlyphRun format_text(string text, int row, double row_height, LadderColumnSettings settings, GlyphContext glyph_context) =>
|
||||
glyph_context.get_glyph_run(text, settings.font_size, settings.width, row_height, HorizontalAlignment.Center, 0, VerticalAlignment.Center, row * row_height);
|
||||
glyph_context.get_glyph_run(text, settings.font_size, settings.width, row_height, HorizontalAlignment.Center, 0, VerticalAlignment.Center, row * row_height, true);
|
||||
|
||||
public static double[] gen_prices(double mid, int row_count, ContractSpecificationImmutable spec) {
|
||||
// require odd number
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using bts.api;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
@@ -25,7 +26,7 @@ namespace mbts.spark
|
||||
|
||||
public IObservable<List<PnLData>> on_pnl_data =>
|
||||
// combine w/ Securities to get SecurityMD
|
||||
_order_model.on_fill_tuples.CombineLatest(_bus.subscribe(SparkTags.SecuritiesTag), (fills, securities) => {
|
||||
_order_model.on_fill_tuples.table(ft => ft.key).CombineLatest(_bus.subscribe(SparkTags.SecuritiesTag), (fills, securities) => {
|
||||
List<PnLData> pnl_data = new List<PnLData>(fills.state.Count);
|
||||
foreach (var ft in fills.state.Values) {
|
||||
if (ft.security.HasValue && securities.TryGetValue(ft.security.Value.Id, out SecurityMD sec_md)) {
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace mbts.spark {
|
||||
|
||||
partial class CVOrderBookVM {
|
||||
public class CVOrderBookColumnCollection : ObservableCollection<ColumnBase>, IDisposable {
|
||||
public CVOrderBookColumnCollection(IBus bus, OrdersModel model, OrderBookStartupData startup_data, IObservable<(IReadOnlyList<SecurityImmutable> added, IReadOnlyList<SecurityImmutable> removed)> order_securities) {
|
||||
public CVOrderBookColumnCollection(IBus bus, OrdersModel model, OrderBookStartupData startup_data, IObservable<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)> order_securities) {
|
||||
// security
|
||||
Add(security_column = new SecurityColumn(this,
|
||||
DiscreteValuesFilterVM.Create<CVOrderBookRowVM, SecurityImmutable, SecurityCriteria>(
|
||||
|
||||
@@ -258,7 +258,6 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace mbts.spark {
|
||||
// copying the collection locally avoids constantly casting rows_view to ICollectionViewLiveShaping.
|
||||
readonly ObservableCollection<string> _live_sorting_props;
|
||||
readonly Property<CVOrderBookRowVM> _current_row;
|
||||
readonly Subject<(IReadOnlyList<SecurityImmutable> added, IReadOnlyList<SecurityImmutable> removed)> _order_securities_subject =
|
||||
new Subject<(IReadOnlyList<SecurityImmutable> added, IReadOnlyList<SecurityImmutable> removed)>();
|
||||
readonly Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)> _order_securities_subject =
|
||||
new Subject<(IEnumerable<SecurityImmutable> added, IEnumerable<SecurityImmutable> removed)>();
|
||||
|
||||
IReadOnlyDictionary<uint, OrderRecordImmutable> _orders_cache = new Dictionary<uint, OrderRecordImmutable>();
|
||||
Dictionary<uint, CVOrderBookRowVM> _order_vms_cache = new Dictionary<uint, CVOrderBookRowVM>();
|
||||
|
||||
@@ -28,6 +28,10 @@ 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,
|
||||
@@ -188,6 +192,30 @@ 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) {
|
||||
@@ -199,6 +227,10 @@ 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;
|
||||
@@ -210,10 +242,14 @@ 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.orange.rgb_color(), NoProps = true };
|
||||
case CustomOrderDefinition.CustomOneofCase.Stop: return new OrderMetadata { KeyBinding = "S", Color = BTSColors.red_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;
|
||||
@@ -223,10 +259,14 @@ 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.orange.rgb_color() };
|
||||
case OrderState.CustomOneofCase.Stop: return new OrderMetadata { KeyBinding = "S", Color = BTSColors.red_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;
|
||||
@@ -258,6 +298,10 @@ 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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace mbts.ui.utility.text {
|
||||
|
||||
public GlyphRun get_glyph_run(
|
||||
string text, double font_size, double available_width, double available_height,
|
||||
HorizontalAlignment x_alignment, double x_offset, VerticalAlignment y_alignment, double y_offset) {
|
||||
HorizontalAlignment x_alignment, double x_offset, VerticalAlignment y_alignment, double y_offset, bool use_layout_rounding = false) {
|
||||
//
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return null;
|
||||
@@ -61,7 +61,7 @@ namespace mbts.ui.utility.text {
|
||||
var advance_widths = new double[text.Length];
|
||||
if (!_try_get_glyph_params(text, font_size, ref glyph_indices, ref advance_widths, out double glyph_width))
|
||||
return null;
|
||||
var origin = _calculate_glyph_run_origin(font_size, available_width, available_height, glyph_width, x_alignment, x_offset, y_alignment, y_offset);
|
||||
var origin = _calculate_glyph_run_origin(font_size, available_width, available_height, glyph_width, x_alignment, x_offset, y_alignment, y_offset, use_layout_rounding);
|
||||
return new GlyphRun(_glyph_typeface, 0, false, font_size, HighDpiExtensions.fdpi_ratio, glyph_indices, origin, advance_widths, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace mbts.ui.utility.text {
|
||||
|
||||
static Point _calculate_glyph_run_origin(
|
||||
double font_size, double available_width, double available_height, double glyph_width,
|
||||
HorizontalAlignment x_alignment, double x_offset, VerticalAlignment y_alignment, double y_offset ) {
|
||||
HorizontalAlignment x_alignment, double x_offset, VerticalAlignment y_alignment, double y_offset, bool use_layout_rounding = false) {
|
||||
//
|
||||
double x, y;
|
||||
switch (x_alignment) {
|
||||
@@ -104,6 +104,10 @@ namespace mbts.ui.utility.text {
|
||||
default: y = available_height - 0.5 * (available_height - font_size); break;
|
||||
}
|
||||
y += y_offset;
|
||||
if (use_layout_rounding) {
|
||||
x = Math.Round(x);
|
||||
y = Math.Round(y);
|
||||
}
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
@@ -148,6 +152,64 @@ namespace mbts.ui.utility.text {
|
||||
return true;
|
||||
}
|
||||
|
||||
#region MultiLine
|
||||
public bool arrange_glyph_run(
|
||||
IReadOnlyList<string> lines, double line_spacing, TextAlignment text_alignment, double font_size,
|
||||
out ushort[] glyph_indices, out double[] advance_widths, out Point[] glyph_offsets, out double glyph_width, out double glyph_height) {
|
||||
//
|
||||
glyph_width = 0;
|
||||
glyph_height = 0;
|
||||
if (lines.is_null_or_empty()) {
|
||||
glyph_indices = new ushort[0];
|
||||
advance_widths = new double[0];
|
||||
glyph_offsets = new Point[0];
|
||||
return false;
|
||||
}
|
||||
int size = lines.Sum(s => s.Length);
|
||||
glyph_indices = new ushort[size];
|
||||
advance_widths = new double[size];
|
||||
glyph_offsets = new Point[size];
|
||||
switch (text_alignment) {
|
||||
case TextAlignment.Left:
|
||||
return _try_get_glyph_params_left_aligned(lines, line_spacing, font_size, ref glyph_indices, ref advance_widths, ref glyph_offsets, out glyph_width, out glyph_height);
|
||||
case TextAlignment.Center: // TODO
|
||||
case TextAlignment.Right: // TODO
|
||||
default: goto case TextAlignment.Left;
|
||||
}
|
||||
}
|
||||
|
||||
public GlyphRun from_arranged(double font_size, Point origin, ushort[] indices, double[] advance_widths, Point[] offsets) =>
|
||||
new GlyphRun(_glyph_typeface, 0, false, font_size, HighDpiExtensions.fdpi_ratio, indices, origin, advance_widths, offsets, null, null, null, null, null);
|
||||
|
||||
bool _try_get_glyph_params_left_aligned(
|
||||
IReadOnlyList<string> lines, double line_spacing, double font_size, ref ushort[] glyph_indices, ref double[] advance_widths, ref Point[] glyph_offsets, out double glyph_width, out double glyph_height) {
|
||||
//
|
||||
glyph_width = 0;
|
||||
glyph_height = 0;
|
||||
double x_offset = 0;
|
||||
int n = 0;
|
||||
foreach (string text in lines) {
|
||||
double line_width = 0;
|
||||
foreach (char c in text) {
|
||||
if (!_char_map.TryGetValue(c, out (ushort index, double width) gc))
|
||||
return false;
|
||||
glyph_indices[n] = gc.index;
|
||||
double w = gc.width * font_size;
|
||||
advance_widths[n] = w;
|
||||
line_width += w;
|
||||
glyph_offsets[n] = new Point(x_offset, glyph_height);
|
||||
n++;
|
||||
}
|
||||
x_offset -= line_width;
|
||||
if (line_width > glyph_width)
|
||||
glyph_width = line_width;
|
||||
glyph_height += (font_size + line_spacing);
|
||||
}
|
||||
glyph_height -= line_spacing; // remove the last line space
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
static ReadOnlyDictionary<int, (ushort index, double width)> _get_glyph_char_map(GlyphTypeface glyph_typeface) =>
|
||||
_glyph_char_maps.GetOrAdd(glyph_typeface, gt => new Lazy<ReadOnlyDictionary<int, (ushort index, double width)>>(() => {
|
||||
var widths = glyph_typeface.AdvanceWidths;
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
using mbts.ui.utility.text;
|
||||
using mbts.utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui {
|
||||
interface IVisualContentRect {
|
||||
Size layout_size { get; }
|
||||
Rect content_area { get; }
|
||||
}
|
||||
|
||||
// the "Control" in 'IControlVisualContentRect' is a double entendre:
|
||||
// it hosts a set of input-properties inspired by the System.Windows.Control class,
|
||||
// and it gives the caller "control" to modify the layout and content_area.
|
||||
interface IControlVisualContentRect : IVisualContentRect {
|
||||
Thickness border_thickness { get; set; }
|
||||
Thickness padding { get; set; }
|
||||
HorizontalAlignment horizontal_alignment { get; set; }
|
||||
HorizontalAlignment horizontal_content_alignment { get; set; }
|
||||
VerticalAlignment vertical_alignment { get; set; }
|
||||
VerticalAlignment vertical_content_alignment { get; set; }
|
||||
double content_width { get; set; }
|
||||
double content_height { get; set; }
|
||||
double min_width { get; set; }
|
||||
double width { get; set; }
|
||||
double max_width { get; set; }
|
||||
double min_height { get; set; }
|
||||
double height { get; set; }
|
||||
double max_height { get; set; }
|
||||
Size available_size { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a set of standard r/w properties to measure and arrange the layout of a rectangular border-region with content inside.
|
||||
/// </summary>
|
||||
class VisualContentRect : IControlVisualContentRect {
|
||||
Thickness
|
||||
_padding,
|
||||
_border_thickness;
|
||||
HorizontalAlignment
|
||||
_horizontal_alignment,
|
||||
_horizontal_content_alignment = HorizontalAlignment.Center;
|
||||
VerticalAlignment
|
||||
_vertical_alignment,
|
||||
_vertical_content_alignment = VerticalAlignment.Center;
|
||||
double
|
||||
_content_width,
|
||||
_content_height,
|
||||
_min_width,
|
||||
_width,
|
||||
_max_width,
|
||||
_min_height,
|
||||
_height,
|
||||
_max_height;
|
||||
Size _available_size, _layout_size;
|
||||
Rect _content_area;
|
||||
bool _dirty;
|
||||
|
||||
public Thickness border_thickness { get => _border_thickness; set => _set(ref _border_thickness, value); }
|
||||
public Thickness padding { get => _padding; set => _set(ref _padding, value); }
|
||||
public HorizontalAlignment horizontal_alignment { get => _horizontal_alignment; set => _set(ref _horizontal_alignment, value); }
|
||||
public HorizontalAlignment horizontal_content_alignment { get => _horizontal_content_alignment; set => _set(ref _horizontal_content_alignment, value); }
|
||||
public VerticalAlignment vertical_alignment { get => _vertical_alignment; set => _set(ref _vertical_alignment, value); }
|
||||
public VerticalAlignment vertical_content_alignment { get => _vertical_content_alignment; set => _set(ref _vertical_content_alignment, value); }
|
||||
public double content_width { get => _content_width; set => _set(ref _content_width, value); }
|
||||
public double content_height { get => _content_height; set => _set(ref _content_height, value); }
|
||||
public double min_width { get => _min_width; set => _set(ref _min_width, value); }
|
||||
public double width { get => _width; set => _set(ref _width, value); }
|
||||
public double max_width { get => _max_width; set => _set(ref _max_width, value); }
|
||||
public double min_height { get => _min_height; set => _set(ref _min_height, value); }
|
||||
public double height { get => _height; set => _set(ref _height, value); }
|
||||
public double max_height { get => _max_height; set => _set(ref _max_height, value); }
|
||||
public Size available_size { get => _available_size; set => _set(ref _available_size, value, _size_comparer); }
|
||||
public Size layout_size { get { _ensure_current(); return _layout_size; } }
|
||||
public Rect content_area { get { _ensure_current(); return _content_area; } }
|
||||
|
||||
void _ensure_current() {
|
||||
if (_dirty) {
|
||||
WPFHelper.measure_visual_content(
|
||||
_border_thickness, _padding, _horizontal_alignment, _horizontal_content_alignment, _vertical_alignment, _vertical_content_alignment,
|
||||
_min_width, _width, _max_width, _min_height, _height, _max_height,
|
||||
_content_width, _content_height, _available_size, out _content_area, out _layout_size, true);
|
||||
_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _set<T>(ref T field, T value) =>
|
||||
_set(ref field, value, EqualityComparer<T>.Default);
|
||||
|
||||
void _set<T>(ref T field, T value, IEqualityComparer<T> comparer) {
|
||||
if (!comparer.Equals(field, value)) {
|
||||
field = value;
|
||||
_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly IEqualityComparer<Size> _size_comparer = EqualityComparer.Create<Size>((a, b) => a == b);
|
||||
}
|
||||
|
||||
// this class was designed to organize/contain the littany of properties needed to arrange glyph content
|
||||
// in addition to the usual-suspects (width, horizontalalignment, etc.), it stores the glyph-arrays: glyph_indices, advance_widths, and glyph_offsets
|
||||
class GlyphContentRect : IControlVisualContentRect {
|
||||
readonly VisualContentRect _vcr = new VisualContentRect();
|
||||
ushort[] _glyph_indices;
|
||||
double[] _advance_widths;
|
||||
Point[] _glyph_offsets;
|
||||
bool _arrange_dirty;
|
||||
|
||||
// fields of public properties
|
||||
double _font_size, _line_spacing;
|
||||
IReadOnlyList<string> _text_lines;
|
||||
GlyphContext _glyph_context;
|
||||
GlyphRun _glyph_run;
|
||||
|
||||
public GlyphContext glyph_context { get => _glyph_context; set => _set(ref _glyph_context, value); }
|
||||
public IReadOnlyList<string> text_lines { get => _text_lines; set => _set(ref _text_lines, value); }
|
||||
public double font_size { get => _font_size; set => _set(ref _font_size, value); }
|
||||
public double line_spacing { get => _line_spacing; set => _set(ref _line_spacing, value); }
|
||||
public Thickness border_thickness { get => _vcr.border_thickness; set => _vcr.border_thickness = value; }
|
||||
public Thickness padding { get => _vcr.padding; set => _vcr.padding = value; }
|
||||
public HorizontalAlignment horizontal_alignment { get => _vcr.horizontal_alignment; set => _vcr.horizontal_alignment = value; }
|
||||
public HorizontalAlignment horizontal_content_alignment { get => _vcr.horizontal_content_alignment; set => _vcr.horizontal_content_alignment = value; }
|
||||
public VerticalAlignment vertical_alignment { get => _vcr.vertical_alignment; set => _vcr.vertical_alignment = value; }
|
||||
public VerticalAlignment vertical_content_alignment { get => _vcr.vertical_content_alignment; set => _vcr.vertical_content_alignment = value; }
|
||||
public double content_width { get { _ensure_current(); return _vcr.content_width; } set { /*TODO: maybe add a Clip property and set it here?*/ } }
|
||||
public double content_height { get { _ensure_current(); return _vcr.content_height; } set { /*TODO: maybe add a Clip property and set it here?*/ } }
|
||||
public double min_width { get => _vcr.min_width; set => _vcr.min_width = value; }
|
||||
public double width { get => _vcr.width; set => _vcr.width = value; }
|
||||
public double max_width { get => _vcr.max_width; set => _vcr.max_width = value; }
|
||||
public double min_height { get => _vcr.min_height; set => _vcr.min_height = value; }
|
||||
public double height { get => _vcr.height; set => _vcr.height = value; }
|
||||
public double max_height { get => _vcr.max_height; set => _vcr.max_height = value; }
|
||||
public Size available_size { get => _vcr.available_size; set => _vcr.available_size = value; }
|
||||
public Size layout_size { get { _ensure_current(); return _vcr.layout_size; } }
|
||||
public Rect content_area { get { _ensure_current(); return _vcr.content_area; } }
|
||||
public GlyphRun glyph_run {
|
||||
get {
|
||||
if (_text_lines.is_null_or_empty())
|
||||
return null;
|
||||
_ensure_current();
|
||||
Point pt = _vcr.content_area.BottomLeft;
|
||||
if (_glyph_run == null) {
|
||||
_glyph_run = _glyph_context.from_arranged(_font_size, pt, _glyph_indices, _advance_widths, _glyph_offsets);
|
||||
} else if (_glyph_run.BaselineOrigin != pt) {
|
||||
_glyph_run.BaselineOrigin = pt;
|
||||
}
|
||||
return _glyph_run;
|
||||
}
|
||||
}
|
||||
|
||||
void _ensure_current() {
|
||||
if (_arrange_dirty) {
|
||||
if (_text_lines.is_null_or_empty()) {
|
||||
_vcr.content_width = 0;
|
||||
_vcr.content_height = 0;
|
||||
} else {
|
||||
_glyph_context.arrange_glyph_run(_text_lines, _line_spacing, TextAlignment.Left, _font_size, out _glyph_indices, out _advance_widths, out _glyph_offsets, out double glyph_width, out double glyph_height);
|
||||
_vcr.content_width = glyph_width;
|
||||
_vcr.content_height = glyph_height;
|
||||
}
|
||||
_glyph_run = null;
|
||||
_arrange_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _set<T>(ref T field, T value) =>
|
||||
_set(ref field, value, EqualityComparer<T>.Default);
|
||||
|
||||
void _set<T>(ref T field, T value, IEqualityComparer<T> comparer) {
|
||||
if (!comparer.Equals(field, value)) {
|
||||
field = value;
|
||||
_arrange_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class VisualContentRectExtensions {
|
||||
// since the Margin is an external factor - an amount of space outside of the control's render-region - it is not included directly in the layout_size
|
||||
public static Size layout_size_plus_margin(this IVisualContentRect vcr, Thickness margin) {
|
||||
var sz = vcr.layout_size;
|
||||
return new Size(margin.Left + sz.Width + margin.Right, margin.Top + sz.Height + margin.Bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,9 @@ namespace mbts.ui.utility {
|
||||
public static ITableUpdateCollection<T, TKey> CreateTableUpdate<T, TKey>(IObservable<ITableUpdateTransform<T, TKey>> source, Func<T, bool> collection_filter) where TKey : IEquatable<TKey> =>
|
||||
new TableUpdateTransformCollection<T, TKey>(source, collection_filter ?? new Func<T, bool>(_ => true));
|
||||
|
||||
public static IRXCollection<T> CreateIncrementalUpdate<T, TKey>(IObservable<IIncrementalUpdate<T, TKey>> source) where TKey : IEquatable<TKey> =>
|
||||
new IncrementalUpdateCollection<T, TKey>(source);
|
||||
|
||||
class AnonymousRXCollection<T, TData> : ObservableCollection<T>, IRXCollection<T> {
|
||||
IDisposable _sub;
|
||||
|
||||
@@ -103,5 +106,20 @@ namespace mbts.ui.utility {
|
||||
public bool TryGetValue(TKey key, out T result) => _raw_state.TryGetValue(key, out result);
|
||||
public void Dispose() => Disposable.dispose(ref _sub);
|
||||
}
|
||||
|
||||
class IncrementalUpdateCollection<T, TKey> : ObservableCollection<T>, IRXCollection<T> where TKey : IEquatable<TKey> {
|
||||
IDisposable _sub;
|
||||
|
||||
internal IncrementalUpdateCollection(IObservable<IIncrementalUpdate<T, TKey>> source) {
|
||||
_sub = source.ObserveOnDispatcher().Subscribe(u => {
|
||||
foreach (T val in u.removed.Values)
|
||||
Remove(val);
|
||||
foreach (T val in u.added.Values)
|
||||
Add(val);
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose() => Disposable.dispose(ref _sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using mbts.spark.utilities;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
@@ -222,5 +223,96 @@ namespace mbts.ui {
|
||||
|
||||
public static double control_base_height(Thickness border_thickness, Thickness padding) =>
|
||||
border_thickness.Top + padding.Top + padding.Bottom + border_thickness.Bottom;
|
||||
|
||||
public static void measure_visual_content(
|
||||
Thickness border_thickness,
|
||||
Thickness padding,
|
||||
HorizontalAlignment x_align,
|
||||
HorizontalAlignment x_content_align,
|
||||
VerticalAlignment y_align,
|
||||
VerticalAlignment y_content_align,
|
||||
double min_width,
|
||||
double width,
|
||||
double max_width,
|
||||
double min_height,
|
||||
double height,
|
||||
double max_height,
|
||||
double content_width,
|
||||
double content_height,
|
||||
Size available_size,
|
||||
out Rect content_area,
|
||||
out Size resultant_size,
|
||||
bool use_layout_rounding = false) {
|
||||
//
|
||||
double result_width, content_left;
|
||||
if (!double.IsNaN(width)) {
|
||||
result_width = MathUtils.within_range(min_width, max_width, width);
|
||||
switch (x_content_align) {
|
||||
case HorizontalAlignment.Left: content_left = border_thickness.Left + padding.Left; break;
|
||||
case HorizontalAlignment.Center: content_left = 0.5 * (result_width - content_width); break;
|
||||
case HorizontalAlignment.Right: content_left = result_width - border_thickness.Right - padding.Right; break;
|
||||
default: goto case HorizontalAlignment.Center;
|
||||
}
|
||||
} else {
|
||||
switch (x_align) {
|
||||
case HorizontalAlignment.Left:
|
||||
case HorizontalAlignment.Center:
|
||||
case HorizontalAlignment.Right:
|
||||
result_width = MathUtils.within_range(min_width, max_height, border_thickness.Left + padding.Left + content_width + padding.Right + border_thickness.Right);
|
||||
content_left = border_thickness.Left + padding.Left;
|
||||
break;
|
||||
case HorizontalAlignment.Stretch: {
|
||||
result_width = MathUtils.within_range(min_width, max_height, available_size.Width);
|
||||
switch (x_content_align) {
|
||||
case HorizontalAlignment.Left: content_left = border_thickness.Left + padding.Left; break;
|
||||
case HorizontalAlignment.Center: content_left = 0.5 * (result_width - content_width); break;
|
||||
case HorizontalAlignment.Right: content_left = result_width - content_width - border_thickness.Right - padding.Right; break;
|
||||
default: goto case HorizontalAlignment.Center;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: goto case HorizontalAlignment.Center;
|
||||
}
|
||||
}
|
||||
double result_height, content_top;
|
||||
if (!double.IsNaN(height)) {
|
||||
result_height = MathUtils.within_range(min_height, max_height, height);
|
||||
switch (y_content_align) {
|
||||
case VerticalAlignment.Top: content_top = border_thickness.Top + padding.Top; break;
|
||||
case VerticalAlignment.Center: content_top = 0.5 * (result_height - content_height); break;
|
||||
case VerticalAlignment.Bottom: content_top = result_height - border_thickness.Bottom - padding.Bottom; break;
|
||||
default: goto case VerticalAlignment.Center;
|
||||
}
|
||||
} else {
|
||||
switch (y_align) {
|
||||
case VerticalAlignment.Top:
|
||||
case VerticalAlignment.Center:
|
||||
case VerticalAlignment.Bottom:
|
||||
result_height = border_thickness.Top + padding.Top + content_height + padding.Bottom + border_thickness.Bottom;
|
||||
content_top = border_thickness.Top + padding.Top;
|
||||
break;
|
||||
case VerticalAlignment.Stretch:
|
||||
result_height = available_size.Height;
|
||||
switch (y_content_align) {
|
||||
case VerticalAlignment.Top: content_top = border_thickness.Top + padding.Top; break;
|
||||
case VerticalAlignment.Center: content_top = 0.5 * (result_height - content_height); break;
|
||||
case VerticalAlignment.Bottom: content_top = result_height - content_height - border_thickness.Bottom - padding.Bottom; break;
|
||||
default: goto case VerticalAlignment.Center;
|
||||
}
|
||||
break;
|
||||
default: goto case VerticalAlignment.Center;
|
||||
}
|
||||
}
|
||||
if (use_layout_rounding) {
|
||||
content_left = Math.Round(content_left);
|
||||
content_top = Math.Round(content_top);
|
||||
content_width = Math.Round(content_width);
|
||||
content_height = Math.Round(content_height);
|
||||
result_width = Math.Round(result_width);
|
||||
result_height = Math.Round(result_height);
|
||||
}
|
||||
content_area = new Rect(content_left, content_top, content_width, content_height);
|
||||
resultant_size = new Size(result_width, result_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<Version>1.0.0</Version>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace {{package}} {
|
||||
partial class {{name}} : bts.api.IImmutableMessage {
|
||||
MessageDescriptor bts.api.IImmutableMessage.Descriptor => {{name}}.Descriptor;
|
||||
}
|
||||
public partial struct {{name}}Immutable : bts.api.IImmutableMessage {
|
||||
public readonly partial struct {{name}}Immutable : bts.api.IImmutableMessage {
|
||||
readonly {{name}} _impl;
|
||||
|
||||
public static MessageDescriptor Descriptor => {{name}}.Descriptor;
|
||||
|
||||
@@ -45,7 +45,7 @@ const bts::api::admin::ExchangeConnection* AdminServiceUtils::find_default_accou
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
bts::api::Venue exchange){
|
||||
//todo:get from parent accounts if not overridden
|
||||
if(account.has_connections())
|
||||
if(account.has_connections())
|
||||
for (const std::string& conn_name : account.connections().exchange_connection_names()){
|
||||
const bts::api::admin::ExchangeConnection* conn = find_connection(data, conn_name);
|
||||
if (conn!=nullptr && has_exchange(*conn, exchange))
|
||||
@@ -53,6 +53,44 @@ const bts::api::admin::ExchangeConnection* AdminServiceUtils::find_default_accou
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bts::api::admin::ExchangeConnection*
|
||||
AdminServiceUtils::resolve_cme_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec){
|
||||
if(account.has_connections())
|
||||
for (const std::string& conn_name : account.connections().exchange_connection_names()){
|
||||
const bts::api::admin::ExchangeConnection* conn = find_connection(data, conn_name);
|
||||
if (conn!=nullptr && conn->has_cme_connection())
|
||||
if (conn->cme_connection().has_cgw_connection() ||
|
||||
(sec.has_glbx() && conn->cme_connection().has_msgw_connection() &&
|
||||
conn->cme_connection().msgw_connection().market_segment_id() == sec.glbx().market_segment_id()))
|
||||
return conn;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const bts::api::admin::ExchangeConnection*
|
||||
AdminServiceUtils::resolve_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec){
|
||||
const bts::api::admin::ExchangeConnection* rv = nullptr;
|
||||
switch(sec.exchange()){
|
||||
case api::Venue::VENUE_GLBX:
|
||||
rv = resolve_cme_connection(data, account, sec);
|
||||
break;
|
||||
case api::Venue::VENUE_UNSET:
|
||||
case api::Venue::VENUE_BTS:
|
||||
case api::Venue::VENUE_CBOE:
|
||||
case api::Venue::VENUE_CBOECFE:
|
||||
case api::Venue::VENUE_ICE:
|
||||
case api::Venue::Venue_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case api::Venue::Venue_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
rv = nullptr;
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
////////////////////////////////////////////
|
||||
// connection mutators /////////////////////
|
||||
////////////////////////////////////////////
|
||||
@@ -71,7 +109,10 @@ void AdminServiceUtils::fill_connections_from_config(bts::api::admin::AdminData&
|
||||
uint32_t new_id = find_max_connection_id(admin_data)+1;
|
||||
connection.set_id(new_id);
|
||||
connection.set_name(c.name());
|
||||
connection.mutable_cme_connection()->mutable_msgw_connection();
|
||||
if (c.cme_market_segment_id()==0)
|
||||
connection.mutable_cme_connection()->mutable_cgw_connection();
|
||||
else
|
||||
connection.mutable_cme_connection()->mutable_msgw_connection()->set_market_segment_id(c.cme_market_segment_id());
|
||||
add_connection(admin_data,connection);
|
||||
}
|
||||
for(int i=0;i<admin_data.accounts_size();i++)
|
||||
@@ -267,7 +308,8 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
|
||||
out_result.set_error_text("Could not find user");
|
||||
return rv;
|
||||
}
|
||||
const bts::api::admin::ExchangeConnection* conn_ptr = find_default_account_connection(data, *account_ptr, sec.exchange());
|
||||
const bts::api::admin::ExchangeConnection* conn_ptr = //find_default_account_connection(data, *account_ptr, sec.exchange());
|
||||
resolve_connection(data,*account_ptr,sec);
|
||||
if(conn_ptr == nullptr){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Could not find connection");
|
||||
@@ -285,6 +327,22 @@ bts::api::orderman::RoutingRecord AdminServiceUtils::get_routing_record(const bt
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Could not find CME data for account");
|
||||
return rv;
|
||||
}
|
||||
// check for data corectness
|
||||
if (cme_user_data->operator_id().length() == 0){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Operator ID is not set");
|
||||
return rv;
|
||||
}
|
||||
if (cme_account_data->clearing_account().length() == 0){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("Clearing Account is not set");
|
||||
return rv;
|
||||
}
|
||||
if (cme_account_data->cti()==bts::api::orderman::CTICode::CTI_CODE_UNDEFINED){
|
||||
out_result.set_is_success(false);
|
||||
out_result.set_error_text("CTI Code is not set");
|
||||
return rv;
|
||||
}
|
||||
rv.set_session(conn_ptr->name());
|
||||
rv.set_operator_id(cme_user_data->operator_id());
|
||||
|
||||
@@ -29,6 +29,12 @@ public:
|
||||
static const bts::api::admin::ExchangeConnection* find_default_account_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
bts::api::Venue exchange);
|
||||
static const bts::api::admin::ExchangeConnection* resolve_cme_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec);
|
||||
static const bts::api::admin::ExchangeConnection* resolve_connection(const bts::api::admin::AdminData& data,
|
||||
const bts::api::admin::BTSAccount& account,
|
||||
const bts::api::Security& sec);
|
||||
////////////////////////////////////////////
|
||||
// connection mutators /////////////////////
|
||||
////////////////////////////////////////////
|
||||
|
||||
@@ -235,6 +235,7 @@ message CMEMSGWConnection{
|
||||
string password = 30;
|
||||
string secret_key = 40;
|
||||
string access_key_id = 50;
|
||||
uint32 market_segment_id = 60;
|
||||
}
|
||||
|
||||
message CMEAccountData{
|
||||
|
||||
@@ -64,9 +64,13 @@ 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 {}
|
||||
@@ -76,6 +80,11 @@ 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;
|
||||
@@ -106,6 +115,10 @@ message OrderState {
|
||||
IcebergOrderState iceberg = 13;
|
||||
SpreaderOrderState spreader = 14;
|
||||
TimeBoxOrderState time_box = 15;
|
||||
TickOrderState tick = 17;
|
||||
LeanOrderState lean = 18;
|
||||
JoinOrderState join = 19;
|
||||
TopOrderState top = 20;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +171,10 @@ 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 {
|
||||
|
||||
@@ -199,8 +216,6 @@ 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 {
|
||||
|
||||
@@ -254,6 +254,7 @@ JetFIX {
|
||||
end_time: "07:48:00"
|
||||
start_time: "07:52:00"
|
||||
raw_data: "1JE9X"
|
||||
use_cme_secure_logon: false
|
||||
seconds_to_pause : 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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=false];
|
||||
optional bool use_cme_secure_logon = 341 [default=true];
|
||||
optional bool auto_cert_mode = 342 [default=false];
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ message GLBXExchangeData {
|
||||
uint32 main_fraction = 30;
|
||||
uint32 sub_fraction = 31;
|
||||
uint32 price_display_format = 32;
|
||||
uint32 market_segment_id = 33;
|
||||
}
|
||||
message CBOEExchangeData {
|
||||
int64 exchange_id = 10;
|
||||
|
||||
@@ -97,7 +97,6 @@ void configure_logging(bts::config::Logging const&cfg)
|
||||
{log::persist,"persist"},
|
||||
{log::User,"User"},
|
||||
{log::OrderMan,"OrderMan"},
|
||||
{log::Latency,"Latency"},
|
||||
{log::Assert,"Assert"},
|
||||
{log::Engine,"Engine"},
|
||||
{log::Ui,"UI"},
|
||||
|
||||
@@ -34,8 +34,7 @@ constexpr log::Subsystem Api {md.ordinal+1 };
|
||||
constexpr log::Subsystem persist {Api.ordinal+1 };
|
||||
constexpr log::Subsystem User {persist.ordinal+1 };
|
||||
constexpr log::Subsystem OrderMan {User.ordinal+1 };
|
||||
constexpr log::Subsystem Latency {OrderMan.ordinal+1 };
|
||||
constexpr log::Subsystem Assert {Latency.ordinal+1 };
|
||||
constexpr log::Subsystem Assert {OrderMan.ordinal+1 };
|
||||
constexpr log::Subsystem Engine {Assert.ordinal+1 };
|
||||
constexpr log::Subsystem Ui {Engine.ordinal+1 };
|
||||
constexpr log::Subsystem ExSim {Ui.ordinal+1 };
|
||||
|
||||
@@ -22,11 +22,14 @@ FixedSecurity::FixedSecurity() :
|
||||
type(api::SECURITYTYPE_STOCK),
|
||||
exchange(api::VENUE_UNSET),
|
||||
display_factor(0),
|
||||
cboe_class_key(0),
|
||||
cme_market_segment_id(0),
|
||||
price_break(0),
|
||||
low_tick(0),
|
||||
high_tick(0),
|
||||
cboe_class_key(0),
|
||||
cme_market_segment_id(0),
|
||||
cme_main_fraction(0),
|
||||
cme_sub_fraction(0),
|
||||
cme_price_display_format(0),
|
||||
_symbol(),
|
||||
_cfi_code(),
|
||||
_security_desc(),
|
||||
@@ -157,6 +160,7 @@ void convert_security(const FixedSecurity& source, Security* presult) {
|
||||
dest.mutable_glbx()->set_security_desc(source.security_desc());
|
||||
dest.mutable_glbx()->set_cfi_code(source.cfi_code());
|
||||
dest.mutable_glbx()->set_cme_symbol(source.alt_symbol());
|
||||
if (source.cme_market_segment_id != 0) dest.mutable_glbx()->set_market_segment_id(source.cme_market_segment_id);
|
||||
if (source.cme_main_fraction != 0) dest.mutable_glbx()->set_main_fraction(source.cme_main_fraction);
|
||||
if (source.cme_sub_fraction != 0) dest.mutable_glbx()->set_sub_fraction(source.cme_sub_fraction);
|
||||
if (source.cme_price_display_format) dest.mutable_glbx()->set_price_display_format(source.cme_price_display_format);
|
||||
@@ -257,6 +261,7 @@ void convert_security(const Security& src, FixedSecurity& dest, bool set_securit
|
||||
dest.security_desc(src.glbx().security_desc());
|
||||
dest.cfi_code(src.glbx().cfi_code());
|
||||
dest.alt_symbol(src.glbx().cme_symbol());
|
||||
dest.cme_market_segment_id = src.glbx().market_segment_id();
|
||||
dest.cme_main_fraction = src.glbx().main_fraction();
|
||||
dest.cme_sub_fraction = src.glbx().sub_fraction();
|
||||
dest.cme_price_display_format = src.glbx().price_display_format();
|
||||
|
||||
@@ -45,15 +45,13 @@ public:
|
||||
|
||||
int8_t display_factor;
|
||||
|
||||
uint32_t cboe_class_key;
|
||||
|
||||
uint32_t cme_market_segment_id;
|
||||
|
||||
double price_break;
|
||||
double low_tick;
|
||||
double high_tick;
|
||||
double cash_per_point;
|
||||
|
||||
uint32_t cboe_class_key;
|
||||
uint8_t cme_market_segment_id;
|
||||
uint8_t cme_main_fraction;
|
||||
uint8_t cme_sub_fraction;
|
||||
uint8_t cme_price_display_format;
|
||||
|
||||
@@ -1 +1 @@
|
||||
set(sources cmds/AutoCert.cpp;cmds/Benchmark.cpp;cmds/CmdRegistry.cpp;cmds/ConfigCheck.cpp;cmds/Help.cpp;cmds/Md.cpp;cmds/QuickTest.cpp;cmds/Run.cpp;cmds/RunSim.cpp;cmds/Span.cpp;cmds/Test.cpp;cmds/TestCeloxica.cpp;dspark/mirage.cpp;dspark/mirage/routing/Router.cpp;exchange_sim/ExchangeSim.cpp;exchange_sim/ExchangeSimApplication.cpp;exchange_sim/book/Book.cpp;exchange_sim/book/ExchangeSim.proto;exchange_sim/book/Order.cpp;exchange_sim/book/OrderMatcher.cpp;exchange_sim/book/book_transform.cpp;exchange_sim/tests/test_book_transform.cpp;http/ApiGateway.cpp;http/WebServer.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;main.cpp;md/platforms/celoxica/CXListener.cpp;md/platforms/celoxica/CXMain.cpp;md/platforms/celoxica/tests/cx_tests.cpp;md/platforms/mdp3_onixs/MDP3Listener.cpp;md/platforms/mdp3_onixs/MDP3Main.cpp;md/platforms/mdp3_onixs/MDP3Utils.cpp;md/platforms/mdp3_onixs/tests/mdp3_tests.cpp;md/run_md.cpp;mercury/actors/Actor.cpp;mercury/broker/AsyncEventBroker.cpp;mercury/broker/CoreEventBroker.cpp;mercury/broker/IEventBroker.cpp;mercury/events/TimerEvent.cpp;mercury/mercury_utils/Executor.cpp;mercury/mercury_utils/IPollableActor.cpp;mercury/mercury_utils/mercury_common.cpp;mercury/synopsis/SynopsisBuilder.cpp;mercury/topics/ITopic.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.cpp;orderman/actors/OrderStore.cpp;orderman/actors/SecuritySrc.cpp;orderman/actors/ServiceBridge.cpp;orderman/actors/order_manager/OrderBuilder.cpp;orderman/actors/order_manager/OrderManager.cpp;orderman/actors/order_manager/OrderServicesData.cpp;orderman/events/CancelRejectEvent.cpp;orderman/events/NewExecutionReportEvent.cpp;orderman/limiter/LimitStore.cpp;orderman/limiter/Limiter.cpp;orderman/limiter/ProductLimitNode.cpp;orderman/limiter/ProductLimitTree.cpp;orderman/limiter/SecurityLimitNode.cpp;orderman/limiter/SecurityLimitTree.cpp;orderman/limiter/tests/limit_tests.cpp;orderman/order_factory/OrderFactory.cpp;orderman/order_router/OrderRouter.cpp;orderman/orderman_utils/fix_order_codec.cpp;orderman/orders/IcebergOrder.cpp;orderman/orders/NativeOrder.cpp;orderman/orders/Order.cpp;orderman/orders/TimeBoxOrder.cpp;orderman/orders/spreader_orders/SpreaderOrder.cpp;orderman/orders/spreader_orders/spreader/LegPropertyData.cpp;orderman/orders/spreader_orders/spreader/QuoteLegPriceQuantityState.cpp;orderman/orders/spreader_orders/spreader/SingleFillHedger.cpp;orderman/orders/spreader_orders/spreader/SingleLegQuoter.cpp;orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp;orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.cpp;orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp;orderman/orders/spreader_orders/spreader/SpreaderUtils.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderServices.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderPreActivationOrderServices.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckCancelledStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckCancelledWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForSizeStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderEndStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.cpp;orderman/orders/spreader_orders/spreader_states/SpreaderWaitLine truncated
|
||||
set(sources cmds/AutoCert.cpp;cmds/Benchmark.cpp;cmds/CmdRegistry.cpp;cmds/ConfigCheck.cpp;cmds/Help.cpp;cmds/Md.cpp;cmds/QuickTest.cpp;cmds/Run.cpp;cmds/RunSim.cpp;cmds/Span.cpp;cmds/Test.cpp;cmds/TestCeloxica.cpp;dspark/mirage.cpp;dspark/mirage/routing/Router.cpp;exchange_sim/ExchangeSim.cpp;exchange_sim/ExchangeSimApplication.cpp;exchange_sim/book/Book.cpp;exchange_sim/book/ExchangeSim.proto;exchange_sim/book/Order.cpp;exchange_sim/book/OrderMatcher.cpp;exchange_sim/book/book_transform.cpp;exchange_sim/tests/test_book_transform.cpp;http/ApiGateway.cpp;http/WebServer.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;main.cpp;md/platforms/celoxica/CXListener.cpp;md/platforms/celoxica/CXMain.cpp;md/platforms/celoxica/tests/cx_tests.cpp;md/platforms/mdp3_onixs/MDP3Listener.cpp;md/platforms/mdp3_onixs/MDP3Main.cpp;md/platforms/mdp3_onixs/MDP3Utils.cpp;md/platforms/mdp3_onixs/tests/mdp3_tests.cpp;md/run_md.cpp;mercury/actors/Actor.cpp;mercury/broker/AsyncEventBroker.cpp;mercury/broker/CoreEventBroker.cpp;mercury/broker/IEventBroker.cpp;mercury/events/TimerEvent.cpp;mercury/mercury_utils/Executor.cpp;mercury/mercury_utils/IPollableActor.cpp;mercury/mercury_utils/mercury_common.cpp;mercury/synopsis/SynopsisBuilder.cpp;mercury/topics/ITopic.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.cpp;orderman/actors/OrderStore.cpp;orderman/actors/SecuritySrc.cpp;orderman/actors/ServiceBridge.cpp;orderman/actors/order_manager/OrderBuilder.cpp;orderman/actors/order_manager/OrderManager.cpp;orderman/actors/order_manager/OrderServicesData.cpp;orderman/events/CancelRejectEvent.cpp;orderman/events/NewExecutionReportEvent.cpp;orderman/limiter/LimitStore.cpp;orderman/limiter/Limiter.cpp;orderman/limiter/ProductLimitNode.cpp;orderman/limiter/ProductLimitTree.cpp;orderman/limiter/SecurityLimitNode.cpp;orderman/limiter/SecurityLimitTree.cpp;orderman/limiter/tests/limit_tests.cpp;orderman/order_factory/OrderFactory.cpp;orderman/order_router/OrderRouter.cpp;orderman/orderman_utils/fix_order_codec.cpp;orderman/orders/IcebergOrder.cpp;orderman/orders/NativeOrder.cpp;orderman/orders/Order.cpp;orderman/orders/TimeBoxOrder.cpp;orderman/orders/book_algos/BookAlgo.cpp;orderman/orders/book_algos/JoinOrder.cpp;orderman/orders/book_algos/LeanOrder.cpp;orderman/orders/book_algos/TickOrder.cpp;orderman/orders/book_algos/TopOrder.cpp;orderman/orders/spreader_orders/SpreaderOrder.cpp;orderman/orders/spreader_orders/spreader/LegPropertyData.cpp;orderman/orders/spreader_orders/spreader/QuoteLegPriceQuantityState.cpp;orderman/orders/spreader_orders/spreader/SingleFillHedger.cpp;orderman/orders/spreader_orders/spreader/SingleLegQuoter.cpp;orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.cpp;orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.cpp;orderman/orders/spreader_orders/spreader/SpreaderLegData.cpp;orderman/orders/spreader_orders/spreader/SpreaderUtils.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderServices.cpp;orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderPreActivationOrderServices.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckCancelledStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWaitingForFirstAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/hedger/hedger_states/HedgerWorkingStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterEndStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterStartStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForCancelAckStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckCancelledWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForFirstAckWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForSizeStateWorker.cpp;orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWorkingStateWorker.cpp;orderman/Line truncated
|
||||
@@ -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,9 +283,11 @@ void ExchangeSimApplication::onMessage(
|
||||
try {
|
||||
auto &matcher = me->matchers[sec_desc];
|
||||
auto order = matcher.get_order(clOrdID);
|
||||
if (!order)
|
||||
if (!order) {
|
||||
LOG(ExSim, Info, "Receive cancel/replace for unknown order {}",
|
||||
clOrdID);
|
||||
throw std::logic_error("Unknown order");
|
||||
else {
|
||||
} else {
|
||||
auto order_cpy = *order;
|
||||
order_cpy.set_price(price);
|
||||
order_cpy.set_quantity(orderQty);
|
||||
@@ -318,9 +320,11 @@ void ExchangeSimApplication::onMessage(const FIX42::OrderCancelRequest &message,
|
||||
try {
|
||||
auto &matcher = me->matchers[sec_desc];
|
||||
auto order = matcher.get_order(origClOrdID);
|
||||
if (!order)
|
||||
if (!order) {
|
||||
LOG(ExSim, Info, "Receive cancel for unknown order {}",
|
||||
origClOrdID.getString());
|
||||
throw std::logic_error("Unknown order");
|
||||
else {
|
||||
} else {
|
||||
auto order_cpy = *order;
|
||||
order_cpy.set_open_quantity(0);
|
||||
updateOrder(order_cpy, FIX::OrdStatus_CANCELED);
|
||||
@@ -439,20 +443,32 @@ 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();
|
||||
@@ -588,7 +604,8 @@ 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) {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "server/sparkd/jetfix/store/session_name.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/utils/TimeStamp.h"
|
||||
#include "server/sparkd/tracing/bts_trace.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
@@ -101,6 +103,7 @@ int SocketConnection::read (char* to_buffer,size_t read_count)
|
||||
|
||||
int SocketConnection::write (WriteVec const&vec,size_t vec_size,size_t cid)
|
||||
{
|
||||
UNUSED(cid); // clang
|
||||
bts_assert (_fd>=0);
|
||||
|
||||
struct iovec iv [MaxWriteVecSz];
|
||||
@@ -109,9 +112,9 @@ int SocketConnection::write (WriteVec const&vec,size_t vec_size,size_t cid)
|
||||
iv[i].iov_len = vec[i].second;
|
||||
}
|
||||
|
||||
LOG(Latency,Info,"SocketConnection_write_pre_write {} {}", cid, utils::TimeStamp::now().to_uint64());
|
||||
tracepoint (bts_trace,trace_order,(char*) "SocketConnection.pre_write", 0,cid);
|
||||
auto result = ::writev (_fd,iv,(int) vec_size);
|
||||
LOG(Latency,Info,"SocketConnection_write_post_write {} {}", cid, utils::TimeStamp::now().to_uint64());
|
||||
tracepoint (bts_trace,trace_order,(char*) "SocketConnection.post_write", 0,cid);
|
||||
if (_log && result >0) {
|
||||
for (size_t i = 0; i < vec_size;i++)
|
||||
_out_log.write (vec[i].first, vec[i].second);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "lib/utils/datetime.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
#include "server/sparkd/orderman/orderman_utils/orderman_common.h"
|
||||
#include "server/sparkd/tracing/bts_trace.h"
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
@@ -160,7 +161,7 @@ CoreGateway::~CoreGateway ()
|
||||
|
||||
void CoreGateway::send (orderman::TickToTradeCorID cid, OutFIXPtr const &fix)
|
||||
{
|
||||
LOG(Latency,Info,"CoreGateway_send {} {}", cid, utils::TimeStamp::now().to_uint64());
|
||||
tracepoint (bts_trace,trace_order,(char*) "CoreGateway.send", 0,cid);
|
||||
me->event_queue.enqueue (EventData (&CoreGateway::sync_send,cid,fix));
|
||||
}
|
||||
|
||||
@@ -277,7 +278,7 @@ void CoreGateway::sync_stop (EventData const&)
|
||||
|
||||
void CoreGateway::sync_send (EventData const &ev)
|
||||
{
|
||||
LOG(Latency,Info,"CoreGateway_sync_send {} {}", ev.cid, utils::TimeStamp::now().to_uint64());
|
||||
tracepoint (bts_trace,trace_order,(char*) "CoreGateway.sync_send", 0,ev.cid);
|
||||
switch (me->state)
|
||||
{
|
||||
case State::OFF:
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "server/sparkd/jetfix/connection/IConnection.h"
|
||||
#include "server/sparkd/jetfix/gateway/ICoreGatewayEventSink.h"
|
||||
#include "server/sparkd/jetfix/store/IFIXStore.h"
|
||||
#include "server/sparkd/tracing/bts_trace.h"
|
||||
|
||||
#include "lib/config/protos/jetfix.pb.h"
|
||||
|
||||
@@ -105,6 +106,7 @@ void Session::start() {
|
||||
}
|
||||
|
||||
void Session::send(orderman::TickToTradeCorID cid, OutFIXPtr const &msgs) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "Session.send", 0,cid);
|
||||
switch (me->state) {
|
||||
case State::READY:
|
||||
case State::CERT_MODE:
|
||||
@@ -561,15 +563,12 @@ void Session::internal_send(orderman::TickToTradeCorID cid,
|
||||
data_vec[i].first = s.data();
|
||||
data_vec[i].second = s.size();
|
||||
}
|
||||
|
||||
LOG(Latency, Info, "Session_internal_send_pre_write {} {}", cid,
|
||||
utils::TimeStamp::now().to_uint64());
|
||||
tracepoint (bts_trace,trace_order,(char*) "Session.internal_send.pre_write", 0,cid);
|
||||
if (me->connection.write(data_vec, msgs->fix.size(), cid) < 0) {
|
||||
terminate_session(JetFIXError::CONNECTION_ERROR);
|
||||
return;
|
||||
}
|
||||
LOG(Latency, Info, "Session_internal_send_post_write {} {}", cid,
|
||||
utils::TimeStamp::now().to_uint64());
|
||||
tracepoint (bts_trace,trace_order,(char*) "Session.internal_send.post_write", 0,cid);
|
||||
me->last_sent_msg_timestamp = me->now();
|
||||
me->store.store(msgs);
|
||||
msgs->set_ts(OutFIX::POST_STORE, utils::TimeStamp::now());
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "MDP3Listener.h"
|
||||
#include "lib/utils/datetime.h"
|
||||
#include <vector>
|
||||
#include "server/sparkd/tracing/bts_trace.h"
|
||||
#include "lib/utils/helper_macros.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace bts::co2;
|
||||
@@ -104,15 +106,19 @@ MDP3Listener::~MDP3Listener() {
|
||||
//
|
||||
// Warnings/Errors
|
||||
void MDP3Listener::onError(onixs::Handler&, const onixs::ErrorArgs& error) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onError", 0, _latest_sequence);
|
||||
LOG(md,Warn, "OnixS Error: {} - {}", error.description(), error.source());
|
||||
}
|
||||
void MDP3Listener::onWarning(onixs::Handler&, const onixs::WarningArgs& warning) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onWarning", 0, _latest_sequence);
|
||||
LOG(md,Warn, "OnixS Warn: {} - {}", warning.description(), warning.source());
|
||||
}
|
||||
void MDP3Listener::onInstrumentRecovery(onixs::Handler& h) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onInstrumentRecovery", 0, _latest_sequence);
|
||||
LOG(md,Info, "Beginning security definition recovery for channel {}", h.settings().channel());
|
||||
}
|
||||
void MDP3Listener::onEndOfInstrumentRecovery(onixs::Handler& h, const onixs::RecoveryCompletionArgs&) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onEndOfInstrumentRecovery", 0, _latest_sequence);
|
||||
LOG(md,Info, "Finished Security definition recovery for channel {}", h.settings().channel());
|
||||
}
|
||||
// void MDP3Listener::onGap(onixs::Handler& h, const Security&, unsigned) {
|
||||
@@ -123,18 +129,22 @@ void MDP3Listener::onEndOfInstrumentRecovery(onixs::Handler& h, const onixs::Rec
|
||||
//
|
||||
// Gap Recovery
|
||||
void MDP3Listener::onMarketRecovery(onixs::Handler& h) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onMarketRecovery", 0, _latest_sequence);
|
||||
update_all_status(COCONUTSECURITYSTATUS_PACKET_LOSS);
|
||||
LOG(md,Info, "Beginning market recovery for channel {}", h.settings().channel());
|
||||
}
|
||||
void MDP3Listener::onEndOfMarketRecovery(onixs::Handler& h, const onixs::RecoveryCompletionArgs&) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onEndOfMarketRecovery", 0, _latest_sequence);
|
||||
update_all_status(COCONUTSECURITYSTATUS_OK);
|
||||
LOG(md,Info, "Finished market recovery for channel {}", h.settings().channel());
|
||||
}
|
||||
void MDP3Listener::onTcpRecovery(onixs::Handler& h) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onTcpRecovery", 0, _latest_sequence);
|
||||
update_all_status(COCONUTSECURITYSTATUS_PACKET_LOSS);
|
||||
LOG(md,Info, "Beginning tcp recovery for channel {}", h.settings().channel());
|
||||
}
|
||||
void MDP3Listener::onEndOfTcpRecovery(onixs::Handler& h, const onixs::RecoveryCompletionArgs&) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onEndOfTcpRecovery", 0, _latest_sequence);
|
||||
update_all_status(COCONUTSECURITYSTATUS_OK);
|
||||
LOG(md,Info, "Finished tcp recovery for channel {}", h.settings().channel());
|
||||
}
|
||||
@@ -152,9 +162,21 @@ void MDP3Listener::onPacket(onixs::Handler&, const onixs::PacketArgs& args) {
|
||||
}
|
||||
}
|
||||
_latest_sequence = seq;
|
||||
auto onixs_zero = args.receiveTime().sinceEpoch();
|
||||
auto onixs_now = OnixS::CME::MDH::UtcWatch::now().sinceEpoch();
|
||||
|
||||
// needed for clang compilation
|
||||
UNUSED(onixs_zero);
|
||||
UNUSED(onixs_now);
|
||||
// was needed for clang compilation
|
||||
|
||||
tracepoint (bts_trace,trace_order,(char*) "onPacket",
|
||||
_packet_arrived.to_uint64() - (onixs_now-onixs_zero),_latest_sequence);
|
||||
}
|
||||
void MDP3Listener::onEndOfPacket(onixs::Handler&, const onixs::PacketArgs&) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onEndOfPacket.begin", 0,_latest_sequence);
|
||||
flush_books();
|
||||
tracepoint (bts_trace,trace_order,(char*) "onEndOfPacket.end", 0,_latest_sequence);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -208,6 +230,7 @@ void set_security_common(const Definition& def, FixedSecurity& fsec) {
|
||||
}
|
||||
|
||||
void MDP3Listener::onDefinition(onixs::Handler&, const onixs::Security& osec, const onixs::InstrumentDefinitionFutureArgs& def) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onDefinition.future", 0,_latest_sequence);
|
||||
const auto& sec = def.message();
|
||||
auto exchange_id = sec.securityId();
|
||||
// LOG(Info, MarketData) << "onDefinition future " << exchange_id;
|
||||
@@ -232,6 +255,7 @@ void MDP3Listener::onDefinition(onixs::Handler&, const onixs::Security& osec, co
|
||||
add_security(fsec);
|
||||
}
|
||||
void MDP3Listener::onDefinition(onixs::Handler&, const onixs::Security&, const onixs::InstrumentDefinitionOptionArgs& def) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onDefinition.option", 0,_latest_sequence);
|
||||
const auto& sec = def.message();
|
||||
auto exchange_id = sec.securityId();
|
||||
// LOG(Info, MarketData) << "onDefinition option " << exchange_id;
|
||||
@@ -264,7 +288,7 @@ void MDP3Listener::onDefinition(onixs::Handler&, const onixs::Security&, const o
|
||||
}
|
||||
|
||||
void MDP3Listener::onDefinition(onixs::Handler&, const onixs::Security&, const onixs::InstrumentDefinitionSpreadArgs& spread_args) {
|
||||
// tracepoint (bts_trace,quoter,(char*) "onSecurityDefnition", 0, _latest_sequence);
|
||||
tracepoint (bts_trace,trace_order,(char*) "onDefinition.spread", 0,_latest_sequence);
|
||||
const auto& sec = spread_args.message();
|
||||
auto exchange_id = sec.securityId();
|
||||
// LOG(Info, MarketData) << "onDefinition spread " << exchange_id;
|
||||
@@ -309,14 +333,13 @@ void MDP3Listener::onDefinition(onixs::Handler&, const onixs::Security&, const o
|
||||
}
|
||||
add_security(fsec);
|
||||
_index.add_spread_legs(fsec.numeric_id, leg_exchange_ids);
|
||||
// tracepoint (bts_trace,quoter,(char*) "onSecurityDefnition", 1, _latest_sequence);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Book
|
||||
void MDP3Listener::onBookUpdate(onixs::Handler&, const onixs::Security& sec, const onixs::ConsolidatedBook& book) {
|
||||
// tracepoint (bts_trace,quoter,(char*) "onConsolidatedBookBegin", sec.id(), _latest_sequence);
|
||||
tracepoint (bts_trace,trace_order,(char*) "onBookUpdate", 0,_latest_sequence);
|
||||
|
||||
auto data = _index.get_security(sec.id());
|
||||
if (data == nullptr) return;
|
||||
@@ -354,7 +377,6 @@ void MDP3Listener::onBookUpdate(onixs::Handler&, const onixs::Security& sec, con
|
||||
for (size_t x = asks_size; x < COCONUT_MAX_DEPTH; ++x) {
|
||||
cache.asks[x] = PriceSize::NO_MARKET;
|
||||
}
|
||||
// tracepoint (bts_trace,quoter,(char*) "onConsolidatedBookEnd", sec.id(), _latest_sequence);
|
||||
}
|
||||
//
|
||||
// Trades
|
||||
@@ -367,6 +389,7 @@ void on_trade(SecurityData* data, const onixs::PRICE& price, int size, const oni
|
||||
}
|
||||
|
||||
void MDP3Listener::onTrade(onixs::Handler&, const onixs::Security& sec, const onixs::RecoveryEntryArgs& arg) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onTrade.recovery", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != NULL) {
|
||||
int size = 0;
|
||||
@@ -377,6 +400,7 @@ void MDP3Listener::onTrade(onixs::Handler&, const onixs::Security& sec, const on
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onTrade(onixs::Handler&, const onixs::Security& sec, const onixs::TradeSummaryArgs& arg) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onTrade.summary", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != NULL) {
|
||||
auto size = arg.entry().entrySize();
|
||||
@@ -385,6 +409,7 @@ void MDP3Listener::onTrade(onixs::Handler&, const onixs::Security& sec, const on
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onTrade(onixs::Handler&, const onixs::Security& sec, const onixs::TradeArgs& arg) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onTrade.trade_args", 0,_latest_sequence);
|
||||
if (arg.entry().updateAction() == onixs::UpdateAction::New) {
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
@@ -398,6 +423,7 @@ void MDP3Listener::onTrade(onixs::Handler&, const onixs::Security& sec, const on
|
||||
//
|
||||
// RFQ
|
||||
void MDP3Listener::onQuoteRequest(onixs::Handler&, const onixs::Security& sec, const onixs::QuoteRequestRelatedSymArgs& arg) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onQuoteRequest", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
auto ts = utils::TimeStamp::now();
|
||||
@@ -446,6 +472,7 @@ void MDP3Listener::onQuoteRequest(onixs::Handler&, const onixs::Security& sec, c
|
||||
//
|
||||
// Volume
|
||||
void MDP3Listener::onElectronicVolume(onixs::Handler&, const onixs::Security& sec, const onixs::VolumeArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onElectronicVolume.volume", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
bts_assert_d( args.entry().entryType().value() == 'e');
|
||||
@@ -455,6 +482,7 @@ void MDP3Listener::onElectronicVolume(onixs::Handler&, const onixs::Security& se
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onElectronicVolume(onixs::Handler&, const onixs::Security& sec, const onixs::RecoveryEntryArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onElectronicVolume.recovery", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data) {
|
||||
int32_t size;
|
||||
@@ -467,6 +495,7 @@ void MDP3Listener::onElectronicVolume(onixs::Handler&, const onixs::Security& se
|
||||
//
|
||||
// Misc
|
||||
void MDP3Listener::onSettlementPrice(onixs::Handler&, const onixs::Security& sec, const onixs::DailyStatisticsArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onSettlementPrice.daily", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
onixs::PRICE price;
|
||||
@@ -481,6 +510,7 @@ void MDP3Listener::onSettlementPrice(onixs::Handler&, const onixs::Security& sec
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onSettlementPrice(onixs::Handler&, const onixs::Security& sec, const onixs::RecoveryEntryArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onSettlementPrice.recovery", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
onixs::PRICE price;
|
||||
@@ -495,6 +525,7 @@ void MDP3Listener::onSettlementPrice(onixs::Handler&, const onixs::Security& sec
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onOpenInterest(onixs::Handler&, const onixs::Security& sec, const onixs::DailyStatisticsArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onOpenInterest.daily", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
onixs::PRICE price;
|
||||
@@ -504,6 +535,7 @@ void MDP3Listener::onOpenInterest(onixs::Handler&, const onixs::Security& sec, c
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onOpenInterest(onixs::Handler&, const onixs::Security& sec, const onixs::RecoveryEntryArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onOpenInterest.recovery", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
onixs::PRICE price;
|
||||
@@ -513,6 +545,7 @@ void MDP3Listener::onOpenInterest(onixs::Handler&, const onixs::Security& sec, c
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onOpeningPrice(onixs::Handler&, const onixs::Security& sec, const onixs::SessionStatisticsArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onOpeningPrice.session", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
onixs::PRICE price = args.entry().entryPx();
|
||||
@@ -525,6 +558,7 @@ void MDP3Listener::onOpeningPrice(onixs::Handler&, const onixs::Security& sec, c
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onOpeningPrice(onixs::Handler&, const onixs::Security& sec, const onixs::RecoveryEntryArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onOpeningPrice.recovery", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
onixs::PRICE price;
|
||||
@@ -539,6 +573,7 @@ void MDP3Listener::onOpeningPrice(onixs::Handler&, const onixs::Security& sec, c
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onHighTradePrice(onixs::Handler&, const onixs::Security& sec, const onixs::SessionStatisticsArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onHighTradePrice.session", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
auto display_factor = data->handle.security().display_factor;
|
||||
@@ -548,6 +583,7 @@ void MDP3Listener::onHighTradePrice(onixs::Handler&, const onixs::Security& sec,
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onHighTradePrice(onixs::Handler&, const onixs::Security& sec, const onixs::RecoveryEntryArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onHighTradePrice.recovery", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
auto display_factor = data->handle.security().display_factor;
|
||||
@@ -559,6 +595,7 @@ void MDP3Listener::onHighTradePrice(onixs::Handler&, const onixs::Security& sec,
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onLowTradePrice(onixs::Handler&, const onixs::Security& sec, const onixs::SessionStatisticsArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onLowTradePrice.session", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
auto display_factor = data->handle.security().display_factor;
|
||||
@@ -568,6 +605,7 @@ void MDP3Listener::onLowTradePrice(onixs::Handler&, const onixs::Security& sec,
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onLowTradePrice(onixs::Handler&, const onixs::Security& sec, const onixs::RecoveryEntryArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onLowTradePrice.recovery", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
auto display_factor = data->handle.security().display_factor;
|
||||
@@ -579,6 +617,7 @@ void MDP3Listener::onLowTradePrice(onixs::Handler&, const onixs::Security& sec,
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onHighestBid(onixs::Handler&, const onixs::Security& sec, const onixs::SessionStatisticsArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onHighestBid.session", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
auto display_factor = data->handle.security().display_factor;
|
||||
@@ -588,6 +627,7 @@ void MDP3Listener::onHighestBid(onixs::Handler&, const onixs::Security& sec, con
|
||||
}
|
||||
}
|
||||
void MDP3Listener::onLowestOffer(onixs::Handler&, const onixs::Security& sec, const onixs::SessionStatisticsArgs& args) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "onLowestOffer.session", 0,_latest_sequence);
|
||||
auto* data = _index.get_security(sec.id());
|
||||
if (data != nullptr) {
|
||||
auto display_factor = data->handle.security().display_factor;
|
||||
@@ -650,3 +690,4 @@ md::MDP3Listener::BookData::BookData(MDProducerHandle & handle) : handle(handle)
|
||||
|
||||
}}//namespace
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "server/sparkd/orderman/events/NewExecutionReportEvent.h"
|
||||
#include "server/sparkd/orderman/events/NewSecuritiesEvent.h"
|
||||
#include "server/sparkd/orderman/orderman_utils/fix_order_codec.h"
|
||||
|
||||
#include "server/sparkd/tracing/bts_trace.h"
|
||||
#include "lib/logging/logging.h"
|
||||
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
@@ -265,8 +265,7 @@ void ExecutionGateway::on_outgoing_fix(
|
||||
mercury::Update<OutFIXMsgEvent> const &msg) {
|
||||
if (msg.events)
|
||||
for (auto const &ev : *(msg.events)) {
|
||||
LOG(Latency, Info, "Gateway {} {}", ev.cid,
|
||||
utils::TimeStamp::now().to_uint64());
|
||||
tracepoint (bts_trace,trace_order,(char*) "on_outgoing_fix", 0,ev.cid);
|
||||
me->gw.send(ev.cid, ev.msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "server/sparkd/mercury/mercury_utils/IPollableActor.h"
|
||||
#include "server/sparkd/orderman/events/BookUpdateEvent.h"
|
||||
#include "server/sparkd/orderman/events/MDRequestEvent.h"
|
||||
#include "server/sparkd/tracing/bts_trace.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
@@ -64,8 +65,7 @@ class MDSource : public mercury::Actor, public mercury::IPollableActor {
|
||||
md::HandleReadResult::SUCCESS) {
|
||||
book._security_key = SecurityKey(handle->key());
|
||||
corid = handle->sequence();
|
||||
LOG(Latency, Info, "MDSource {} {}", corid,
|
||||
utils::TimeStamp::now().to_uint64());
|
||||
tracepoint (bts_trace,trace_order,(char*) "MDSource", 0, corid);
|
||||
publish(_book_update, book, corid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,34 +47,34 @@ public:
|
||||
|
||||
bool is_active() const {return _active;}
|
||||
|
||||
void activate(Price p, Quantity q)
|
||||
void activate(Price p, Quantity q, TickToTradeCorID cid = 0)
|
||||
{
|
||||
bts_assert(_order);
|
||||
bts_assert(!_active);
|
||||
|
||||
_order_services->send_activate(*_order,p,q);
|
||||
_order_services->send_activate(*_order,p,q, cid);
|
||||
_active = true;
|
||||
}
|
||||
|
||||
void update(Price p, Quantity q)
|
||||
void update(Price p, Quantity q, TickToTradeCorID cid = 0)
|
||||
{
|
||||
bts_assert(_order);
|
||||
bts_assert(_active);
|
||||
_order_services->send_update(_order->key(),p,q);
|
||||
_order_services->send_update(_order->key(),p,q,cid);
|
||||
}
|
||||
|
||||
void crack()
|
||||
void crack(TickToTradeCorID cid = 0)
|
||||
{
|
||||
bts_assert(_order);
|
||||
bts_assert(_active);
|
||||
_order_services->send_crack(_order->key());
|
||||
_order_services->send_crack(_order->key(),cid);
|
||||
}
|
||||
|
||||
void cancel()
|
||||
void cancel(TickToTradeCorID cid = 0)
|
||||
{
|
||||
bts_assert(_order);
|
||||
bts_assert(_active);
|
||||
_order_services->send_cancel(_order->key());
|
||||
_order_services->send_cancel(_order->key(),cid);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -102,7 +102,8 @@ void OrderManager::on_order_recovery(
|
||||
auto serv_ptr = oi.order_services.get();
|
||||
me->orders[order.key()] = std::move(oi);
|
||||
order.on_activate(Price(ostate.price()),
|
||||
Quantity(ostate.quantity()), *serv_ptr);
|
||||
Quantity(ostate.quantity()), *serv_ptr,
|
||||
TickToTradeCorID(0));
|
||||
}
|
||||
|
||||
// signal that we are ready
|
||||
@@ -133,7 +134,7 @@ void OrderManager::on_execution_report(
|
||||
auto serv_ptr = oi.order_services.get();
|
||||
me->orders[order->key()] = std::move(oi);
|
||||
order->on_activate(report.data->price, report.data->quantity,
|
||||
*serv_ptr);
|
||||
*serv_ptr, TickToTradeCorID(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +149,8 @@ void OrderManager::on_activate_order(
|
||||
OrderInfo oi{*order, me->services_data};
|
||||
auto serv_ptr = oi.order_services.get();
|
||||
me->orders[k] = std::move(oi);
|
||||
order->on_activate(ev.price, ev.quantity, *serv_ptr);
|
||||
order->on_activate(ev.price, ev.quantity, *serv_ptr,
|
||||
ev.correlation_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +160,8 @@ void OrderManager::on_update_order(const mercury::Update<UpdateOrderEvent> &m) {
|
||||
auto k = ev.key();
|
||||
auto it = me->orders.find(k);
|
||||
if (it != me->orders.end())
|
||||
it->second.order->on_update(ev.price, ev.quantity);
|
||||
it->second.order->on_update(ev.price, ev.quantity,
|
||||
ev.correlation_id);
|
||||
else {
|
||||
LOG(OrderMan, Error,
|
||||
"Received an update request for an unknown order {}. "
|
||||
@@ -174,7 +177,7 @@ void OrderManager::on_cancel_order(const mercury::Update<CancelOrderEvent> &m) {
|
||||
auto k = ev.key();
|
||||
auto it = me->orders.find(k);
|
||||
if (it != me->orders.end())
|
||||
it->second.order->on_cancel();
|
||||
it->second.order->on_cancel(ev.correlation_id);
|
||||
else {
|
||||
LOG(OrderMan, Error,
|
||||
"Received a cancel request for an unknown order {}. "
|
||||
@@ -190,7 +193,7 @@ void OrderManager::on_crack_order(const mercury::Update<CrackOrderEvent> &m) {
|
||||
auto k = ev.key();
|
||||
auto it = me->orders.find(k);
|
||||
if (it != me->orders.end())
|
||||
it->second.order->on_crack();
|
||||
it->second.order->on_crack(ev.correlation_id);
|
||||
else {
|
||||
LOG(OrderMan, Error,
|
||||
"Received a crack request for an unknown order {}. "
|
||||
|
||||
@@ -112,17 +112,17 @@ class OrderServices {
|
||||
|
||||
|
||||
friend class ChildOrder;
|
||||
void send_activate(Order &o, Price p, Quantity q) {
|
||||
_data.owner.publish(_data.activate_order_pub, o, p, q);
|
||||
void send_activate(Order &o, Price p, Quantity q, TickToTradeCorID cid = 0) {
|
||||
_data.owner.publish(_data.activate_order_pub, o, p, q,cid);
|
||||
}
|
||||
void send_update(OrderKey k, Price p, Quantity q) {
|
||||
_data.owner.publish(_data.update_order_pub, k, p, q);
|
||||
void send_update(OrderKey k, Price p, Quantity q, TickToTradeCorID cid = 0) {
|
||||
_data.owner.publish(_data.update_order_pub, k, p, q,cid);
|
||||
}
|
||||
void send_crack(OrderKey k) {
|
||||
_data.owner.publish(_data.crack_order_pub, k);
|
||||
void send_crack(OrderKey k, TickToTradeCorID cid = 0) {
|
||||
_data.owner.publish(_data.crack_order_pub, k, cid);
|
||||
}
|
||||
void send_cancel(OrderKey k) {
|
||||
_data.owner.publish(_data.cancel_order_pub, k);
|
||||
void send_cancel(OrderKey k, TickToTradeCorID cid = 0) {
|
||||
_data.owner.publish(_data.cancel_order_pub, k, cid);
|
||||
}
|
||||
|
||||
Order &_owner;
|
||||
|
||||
@@ -15,12 +15,17 @@ public:
|
||||
using SynopsisPolicy = mercury::NoSynopsis;
|
||||
|
||||
ActivateOrderEvent (Order &o,Price p, Quantity q) :
|
||||
order (&o),price(p),quantity(q)
|
||||
order (&o),price(p),quantity(q), correlation_id(0)
|
||||
{}
|
||||
|
||||
ActivateOrderEvent (Order &o,Price p, Quantity q,TickToTradeCorID cid) :
|
||||
order (&o),price(p),quantity(q), correlation_id(cid)
|
||||
{}
|
||||
|
||||
Order* order;
|
||||
Price price;
|
||||
Quantity quantity;
|
||||
TickToTradeCorID correlation_id;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -13,11 +13,13 @@ class CancelOrderEvent : public mercury::Event<CancelOrderEvent>
|
||||
public:
|
||||
using SynopsisPolicy = mercury::NoSynopsis;
|
||||
|
||||
CancelOrderEvent (OrderKey k) : k (k) {}
|
||||
CancelOrderEvent (OrderKey k) : k (k), correlation_id(0) {}
|
||||
CancelOrderEvent (OrderKey k,TickToTradeCorID cid) : k (k), correlation_id(cid) {}
|
||||
|
||||
// used by ContentSubscription
|
||||
OrderKey key() const {return k;}
|
||||
OrderKey k;
|
||||
TickToTradeCorID correlation_id;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -2,23 +2,23 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "server/sparkd/orderman/orderman_utils/orderman_common.h"
|
||||
#include "server/sparkd/mercury/events/Event.h"
|
||||
#include "server/sparkd/orderman/orderman_utils/orderman_common.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class CrackOrderEvent : public mercury::Event<CrackOrderEvent>
|
||||
{
|
||||
public:
|
||||
class CrackOrderEvent : public mercury::Event<CrackOrderEvent> {
|
||||
public:
|
||||
using SynopsisPolicy = mercury::NoSynopsis;
|
||||
|
||||
CrackOrderEvent(OrderKey k) : k (k) {}
|
||||
CrackOrderEvent(OrderKey k, TickToTradeCorID cid = 0)
|
||||
: k(k), correlation_id(cid) {}
|
||||
|
||||
// used by ContentSubscription
|
||||
OrderKey key() const {return k;}
|
||||
// used by ContentSubscription
|
||||
OrderKey key() const { return k; }
|
||||
OrderKey k;
|
||||
TickToTradeCorID correlation_id;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,11 @@ public:
|
||||
using SynopsisPolicy = mercury::NoSynopsis;
|
||||
|
||||
UpdateOrderEvent (OrderKey k, Price p, Quantity q) :
|
||||
k(k), price(p), quantity(q)
|
||||
k(k), price(p), quantity(q), correlation_id(0)
|
||||
{}
|
||||
|
||||
UpdateOrderEvent (OrderKey k, Price p, Quantity q,TickToTradeCorID cid) :
|
||||
k(k), price(p), quantity(q), correlation_id(cid)
|
||||
{}
|
||||
|
||||
// used by ContentSubscription
|
||||
@@ -23,6 +27,7 @@ public:
|
||||
OrderKey k;
|
||||
Price price;
|
||||
Quantity quantity;
|
||||
TickToTradeCorID correlation_id;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
// risk limit checks
|
||||
////////////////////
|
||||
Limiter::LimiterQueryResult can_send_new_order(api::Side side, Quantity quantity) const{
|
||||
Limiter::LimiterQueryResult result;
|
||||
Limiter::LimiterQueryResult result;result.ok=true;
|
||||
if (quantity.value() > _clip_size){
|
||||
result.ok = false;
|
||||
result.error_msg = fmt::format("Security Clip Size (Max {} Qty {})", _clip_size, quantity.value());
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
return result;
|
||||
}
|
||||
Limiter::LimiterQueryResult can_update_order(api::Side side, Quantity from, Quantity to) const{
|
||||
Limiter::LimiterQueryResult result;
|
||||
Limiter::LimiterQueryResult result;result.ok=true;
|
||||
if (to.value() > _clip_size){
|
||||
result.ok = false;
|
||||
result.error_msg = fmt::format("Security Clip Size (Max {} Qty {})", _clip_size, to.value());
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#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>
|
||||
@@ -120,6 +124,18 @@ 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;
|
||||
|
||||
@@ -88,7 +88,7 @@ void IcebergOrder::on_init_recovered(api::orderman::OrderRecord const &rec,
|
||||
}
|
||||
}
|
||||
|
||||
void IcebergOrder::on_activate(Price p, Quantity q, OrderServices &services) {
|
||||
void IcebergOrder::on_activate(Price p, Quantity q, OrderServices &services, TickToTradeCorID) {
|
||||
bts_assert(!me->services);
|
||||
me->services = &services;
|
||||
me->child_builder.activate(services);
|
||||
@@ -125,7 +125,7 @@ void IcebergOrder::on_activate(Price p, Quantity q, OrderServices &services) {
|
||||
report_state();
|
||||
}
|
||||
|
||||
void IcebergOrder::on_update(Price p, Quantity q) {
|
||||
void IcebergOrder::on_update(Price p, Quantity q, TickToTradeCorID) {
|
||||
switch (me->state.status()) {
|
||||
case ORDER_STATUS_WORKING:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
@@ -151,9 +151,9 @@ void IcebergOrder::on_update(Price p, Quantity q) {
|
||||
report_state();
|
||||
}
|
||||
|
||||
void IcebergOrder::on_crack() {}
|
||||
void IcebergOrder::on_crack(TickToTradeCorID) {}
|
||||
|
||||
void IcebergOrder::on_cancel() {
|
||||
void IcebergOrder::on_cancel(TickToTradeCorID) {
|
||||
switch (me->state.status()) {
|
||||
case ORDER_STATUS_WORKING:
|
||||
LOG(OrderMan, Info, "Canceling Iceberg Order {}", key().value());
|
||||
|
||||
@@ -19,10 +19,10 @@ public:
|
||||
const PreActivationOrderServices&) override;
|
||||
void on_init_recovered(api::orderman::OrderRecord const&,
|
||||
const PreActivationOrderServices &) override;
|
||||
void on_activate(Price,Quantity,OrderServices &) override ;
|
||||
void on_update(Price , Quantity ) override ;
|
||||
void on_crack() override ;
|
||||
void on_cancel() override ;
|
||||
void on_activate(Price,Quantity,OrderServices &, TickToTradeCorID) override ;
|
||||
void on_update(Price , Quantity,TickToTradeCorID ) override ;
|
||||
void on_crack(TickToTradeCorID) override ;
|
||||
void on_cancel(TickToTradeCorID) override ;
|
||||
|
||||
private:
|
||||
void report_state();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "server/sparkd/orderman/orderman_utils/fix_order_codec.h"
|
||||
#include "server/sparkd/orderman/orders/PreActivationOrderServices.h"
|
||||
#include "server/sparkd/securities/ISecurityDB.h"
|
||||
#include "server/sparkd/tracing/bts_trace.h"
|
||||
|
||||
#include "server/sparkd/orderman/actors/order_manager/OrderServices.h"
|
||||
|
||||
@@ -78,10 +79,7 @@ void NativeOrder::on_init_new(OrderDefinition const &def,
|
||||
case CustomOrderDefinition::kStopLimit:
|
||||
me->state.mutable_stop_limit();
|
||||
break;
|
||||
case CustomOrderDefinition::kIceberg:
|
||||
case CustomOrderDefinition::kSpreader:
|
||||
case CustomOrderDefinition::kTimeBox:
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
default:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
@@ -266,7 +264,9 @@ void NativeOrder::on_init_from_exe_report(
|
||||
NativeOrder::~NativeOrder() {}
|
||||
|
||||
void NativeOrder::on_activate(Price p, Quantity q,
|
||||
bts::orderman::OrderServices &services) {
|
||||
bts::orderman::OrderServices &services,
|
||||
TickToTradeCorID cid) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "on_activate", 0,cid);
|
||||
bts_assert(!me->services);
|
||||
me->services = &services;
|
||||
|
||||
@@ -343,10 +343,7 @@ void NativeOrder::on_activate(Price p, Quantity q,
|
||||
case CustomOrderDefinition::kStop:
|
||||
case CustomOrderDefinition::kMarket:
|
||||
break;
|
||||
case CustomOrderDefinition::kIceberg:
|
||||
case CustomOrderDefinition::kSpreader:
|
||||
case CustomOrderDefinition::kTimeBox:
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
default:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
@@ -361,7 +358,7 @@ void NativeOrder::on_activate(Price p, Quantity q,
|
||||
auto &f = stream.new_msg(jetfix::MessageTypes::NewOrderSingle);
|
||||
f.append(me->create_encoding);
|
||||
stream.close();
|
||||
me->session_services->send_fix_buffer(0, buffer);
|
||||
me->session_services->send_fix_buffer(cid, buffer);
|
||||
me->state.set_status(ORDER_STATUS_PENDING_WORKING);
|
||||
me->state.set_price(p.value());
|
||||
me->state.set_quantity(q.value());
|
||||
@@ -428,7 +425,8 @@ void NativeOrder::on_activate(Price p, Quantity q,
|
||||
report_state();
|
||||
}
|
||||
|
||||
void NativeOrder::on_update(Price p, Quantity q) {
|
||||
void NativeOrder::on_update(Price p, Quantity q, TickToTradeCorID cid) {
|
||||
tracepoint (bts_trace,trace_order,(char*) "on_update", 0,cid);
|
||||
switch (me->state.status()) {
|
||||
|
||||
case ORDER_STATUS_WORKING: {
|
||||
@@ -458,7 +456,7 @@ void NativeOrder::on_update(Price p, Quantity q) {
|
||||
transition(me->state.status(), msg);
|
||||
else {
|
||||
me->state.set_last_msg(msg);
|
||||
on_cancel();
|
||||
on_cancel(cid);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -478,7 +476,7 @@ void NativeOrder::on_update(Price p, Quantity q) {
|
||||
auto &f = stream.new_msg(jetfix::MessageTypes::CancelReplace);
|
||||
f.append(me->update_encoding);
|
||||
stream.close();
|
||||
me->session_services->send_fix_buffer(0, buffer);
|
||||
me->session_services->send_fix_buffer(cid, buffer);
|
||||
if (me->state.last_msg().size())
|
||||
me->state.set_last_msg("");
|
||||
}
|
||||
@@ -503,9 +501,9 @@ void NativeOrder::on_update(Price p, Quantity q) {
|
||||
report_state();
|
||||
}
|
||||
|
||||
void NativeOrder::on_crack() {}
|
||||
void NativeOrder::on_crack(TickToTradeCorID) {}
|
||||
|
||||
void NativeOrder::on_cancel() {
|
||||
void NativeOrder::on_cancel(TickToTradeCorID cid) {
|
||||
switch (me->state.status()) {
|
||||
|
||||
case ORDER_STATUS_WORKING: {
|
||||
@@ -518,7 +516,7 @@ void NativeOrder::on_cancel() {
|
||||
auto &f = stream.new_msg(jetfix::MessageTypes::Cancel);
|
||||
f.append(me->cancel_encoding);
|
||||
stream.close();
|
||||
me->session_services->send_fix_buffer(0, buffer);
|
||||
me->session_services->send_fix_buffer(cid, buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -829,10 +827,7 @@ void NativeOrder::init_fix() {
|
||||
me->update_price_bm = update_encoding.add<fix::Price>(0, price_sz());
|
||||
break;
|
||||
}
|
||||
case CustomOrderDefinition::kIceberg:
|
||||
case CustomOrderDefinition::kSpreader:
|
||||
case CustomOrderDefinition::kTimeBox:
|
||||
case CustomOrderDefinition::CUSTOM_NOT_SET:
|
||||
default:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
@@ -975,10 +970,7 @@ 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();
|
||||
case OrderState::kIceberg:
|
||||
case OrderState::kSpreader:
|
||||
case OrderState::kTimeBox:
|
||||
case OrderState::CUSTOM_NOT_SET:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ public:
|
||||
const PreActivationOrderServices&) override;
|
||||
void on_init_recovered(api::orderman::OrderRecord const&,
|
||||
const PreActivationOrderServices &) override;
|
||||
void on_activate(Price,Quantity,OrderServices &) override ;
|
||||
void on_update(Price , Quantity ) override ;
|
||||
void on_crack() override ;
|
||||
void on_cancel() override ;
|
||||
void on_activate(Price,Quantity,OrderServices &, TickToTradeCorID) override ;
|
||||
void on_update(Price , Quantity,TickToTradeCorID ) override ;
|
||||
void on_crack(TickToTradeCorID) override ;
|
||||
void on_cancel(TickToTradeCorID) override ;
|
||||
|
||||
void on_init_from_exe_report(ExecutionReport const&,const PreActivationOrderServices &);
|
||||
private:
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "server/sparkd/orderman/orderman_utils/orderman_common.h"
|
||||
#include <memory>
|
||||
|
||||
// fwd
|
||||
namespace bts {
|
||||
namespace securities {class ISecurityDB;}
|
||||
namespace orderman {class IOrderFactory;}
|
||||
namespace securities {
|
||||
class ISecurityDB;
|
||||
}
|
||||
namespace orderman {
|
||||
class IOrderFactory;
|
||||
}
|
||||
namespace api {
|
||||
namespace orderman {
|
||||
class OrderDefinition;
|
||||
class OrderRecord;
|
||||
class OrderState;
|
||||
}}}
|
||||
}
|
||||
}
|
||||
}
|
||||
// end fwd
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
@@ -20,32 +27,33 @@ namespace orderman {
|
||||
class PreActivationOrderServices;
|
||||
class OrderServices;
|
||||
|
||||
class Order
|
||||
{
|
||||
public:
|
||||
class Order {
|
||||
public:
|
||||
virtual ~Order();
|
||||
|
||||
OrderKey key() const {return _key;}
|
||||
OrderKey parent_key() const {return _parent_key;}
|
||||
OrderKey key() const { return _key; }
|
||||
OrderKey parent_key() const { return _parent_key; }
|
||||
|
||||
virtual api::orderman::OrderDefinition const&definition() const = 0;
|
||||
virtual api::orderman::OrderDefinition const &definition() const = 0;
|
||||
|
||||
// construction - can be slow
|
||||
virtual void on_init_new(api::orderman::OrderDefinition const&, const PreActivationOrderServices&) = 0;
|
||||
virtual void on_init_recovered(api::orderman::OrderRecord const&, const PreActivationOrderServices &) = 0;
|
||||
virtual void on_init_new(api::orderman::OrderDefinition const &,
|
||||
const PreActivationOrderServices &) = 0;
|
||||
virtual void on_init_recovered(api::orderman::OrderRecord const &,
|
||||
const PreActivationOrderServices &) = 0;
|
||||
|
||||
// activation - cannot be slow
|
||||
virtual void on_activate(Price, Quantity,OrderServices &) = 0;
|
||||
virtual void on_activate(Price, Quantity, OrderServices &, TickToTradeCorID) = 0;
|
||||
|
||||
// order is running, maintenance
|
||||
virtual void on_update(Price , Quantity ) = 0;
|
||||
virtual void on_crack() = 0;
|
||||
virtual void on_cancel() = 0;
|
||||
virtual void on_update(Price, Quantity, TickToTradeCorID) = 0;
|
||||
virtual void on_crack(TickToTradeCorID) = 0;
|
||||
virtual void on_cancel(TickToTradeCorID) = 0;
|
||||
|
||||
private:
|
||||
private:
|
||||
OrderKey _key;
|
||||
OrderKey _parent_key;
|
||||
friend class OrderFactory;
|
||||
};
|
||||
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ void TimeBoxOrder::on_init_recovered(api::orderman::OrderRecord const &rec,
|
||||
}
|
||||
}
|
||||
|
||||
void TimeBoxOrder::on_activate(Price p, Quantity q, OrderServices &services) {
|
||||
void TimeBoxOrder::on_activate(Price p, Quantity q, OrderServices &services,TickToTradeCorID) {
|
||||
bts_assert(!me->services);
|
||||
me->services = &services;
|
||||
me->child_builder.activate(services);
|
||||
@@ -121,7 +121,7 @@ void TimeBoxOrder::on_activate(Price p, Quantity q, OrderServices &services) {
|
||||
report_state();
|
||||
}
|
||||
|
||||
void TimeBoxOrder::on_update(Price p, Quantity q) {
|
||||
void TimeBoxOrder::on_update(Price p, Quantity q,TickToTradeCorID) {
|
||||
LOG(OrderMan, Info, "Updating TimeBox Order {}", key().value());
|
||||
|
||||
switch (me->state.status()) {
|
||||
@@ -181,9 +181,9 @@ void TimeBoxOrder::on_update(Price p, Quantity q) {
|
||||
report_state();
|
||||
}
|
||||
|
||||
void TimeBoxOrder::on_crack() {}
|
||||
void TimeBoxOrder::on_crack(TickToTradeCorID) {}
|
||||
|
||||
void TimeBoxOrder::on_cancel() {
|
||||
void TimeBoxOrder::on_cancel(TickToTradeCorID) {
|
||||
LOG(OrderMan, Info, "Canceling TimeBox Order {}", key().value());
|
||||
|
||||
switch (me->state.status()) {
|
||||
|
||||
@@ -19,10 +19,10 @@ public:
|
||||
const PreActivationOrderServices&) override;
|
||||
void on_init_recovered(api::orderman::OrderRecord const&,
|
||||
const PreActivationOrderServices &) override;
|
||||
void on_activate(Price,Quantity,OrderServices &) override ;
|
||||
void on_update(Price , Quantity ) override ;
|
||||
void on_crack() override ;
|
||||
void on_cancel() override ;
|
||||
void on_activate(Price,Quantity,OrderServices &, TickToTradeCorID) override ;
|
||||
void on_update(Price , Quantity,TickToTradeCorID ) override ;
|
||||
void on_crack(TickToTradeCorID) override ;
|
||||
void on_cancel(TickToTradeCorID) override ;
|
||||
|
||||
private:
|
||||
void report_state();
|
||||
|
||||
@@ -0,0 +1,442 @@
|
||||
#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,TickToTradeCorID)
|
||||
{
|
||||
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,TickToTradeCorID)
|
||||
{
|
||||
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(TickToTradeCorID) {}
|
||||
|
||||
void BookAlgo::on_cancel(TickToTradeCorID)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#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 &, TickToTradeCorID) override ;
|
||||
void on_update(Price , Quantity,TickToTradeCorID ) override ;
|
||||
void on_crack(TickToTradeCorID) override ;
|
||||
void on_cancel(TickToTradeCorID) 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;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,185 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,153 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,192 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,154 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -6,98 +6,88 @@
|
||||
#include "server/sparkd/orderman/orders/spreader_orders/spreader/LegPropertyData.h"
|
||||
#include "server/sparkd/orderman/orders/spreader_orders/spreader/SpreaderLegData.h"
|
||||
#include "server/sparkd/orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.h"
|
||||
#include "server/sparkd/tracing/bts_trace.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
/**************************Cons/Dest*******************************/
|
||||
SpreaderOrder::SpreaderOrder()
|
||||
:
|
||||
_cancelled (false),
|
||||
_cancel_timeout (5000),// TODO:get it from config
|
||||
_active(true),
|
||||
_bookkeeping_data_ptr(nullptr),
|
||||
_order_services(nullptr),
|
||||
_reported_state(),
|
||||
_state_worker(nullptr),
|
||||
_start_state_worker(this), _working_state_worker(this), _waiting_for_cancel_all_state_worker(this), _end_state_worker(this)
|
||||
{
|
||||
:
|
||||
_cancelled (false),
|
||||
_cancel_timeout (5000),// TODO:get it from config
|
||||
_active(true),
|
||||
_bookkeeping_data_ptr(),
|
||||
_order_services(nullptr),
|
||||
_reported_state(),
|
||||
_state_worker(nullptr),
|
||||
_start_state_worker(this), _working_state_worker(this), _waiting_for_cancel_all_state_worker(this), _end_state_worker(this){
|
||||
_transform_to_START();
|
||||
}
|
||||
SpreaderOrder::~SpreaderOrder () {}
|
||||
/***********************************************************************/
|
||||
/******************************Order Derived****************************/
|
||||
/***********************************************************************/
|
||||
const api::orderman::OrderDefinition& SpreaderOrder::definition() const
|
||||
{
|
||||
// Order Derived ////////////////////
|
||||
/////////////////////////////////////
|
||||
const api::orderman::OrderDefinition& SpreaderOrder::definition() const{
|
||||
bts_assert(_inited());
|
||||
return _bookkeeping_data().order_def();
|
||||
}
|
||||
void SpreaderOrder::on_init_new(const api::orderman::OrderDefinition& order_def, const PreActivationOrderServices& pao_services)
|
||||
{
|
||||
if (!_active) return;
|
||||
void SpreaderOrder::on_init_new(const api::orderman::OrderDefinition& order_def,
|
||||
const PreActivationOrderServices& pao_services){
|
||||
if (!_active) return;
|
||||
_on_init_new(order_def, SpreaderPreActivationOrderServices(&pao_services));
|
||||
_report_state ();
|
||||
}
|
||||
void SpreaderOrder::on_init_recovered(const bts::api::orderman::OrderRecord& order_record, const bts::orderman::PreActivationOrderServices& pao_services)
|
||||
{
|
||||
void SpreaderOrder::on_init_recovered(const bts::api::orderman::OrderRecord& order_record,
|
||||
const PreActivationOrderServices& pao_services){
|
||||
LOG(OrderMan, Info, "SpreaderOrder.on_init_recovered order_key: {}", key().value());
|
||||
_state_worker->on_init_recovered(order_record, SpreaderPreActivationOrderServices(&pao_services));
|
||||
_report_state();
|
||||
}
|
||||
void SpreaderOrder::on_activate(Price price, Quantity quantity, OrderServices& services)
|
||||
{
|
||||
if (!_active) return;
|
||||
void SpreaderOrder::on_activate(Price price, Quantity quantity,
|
||||
OrderServices& services, TickToTradeCorID){
|
||||
if (!_active) return;
|
||||
SpreaderOrderServices spreader_order_services (&services);
|
||||
_on_activate(price, quantity, spreader_order_services);
|
||||
_report_state();
|
||||
}
|
||||
void SpreaderOrder::on_update(Price price, Quantity quantity)
|
||||
{
|
||||
void SpreaderOrder::on_update(Price price, Quantity quantity,TickToTradeCorID){
|
||||
if (!_active) return;
|
||||
LOG(OrderMan, Info, "SpreaderOrder.on_update key: {} price: {} quantity: {}", key().value(), price.value(), quantity.value());
|
||||
_state_worker->on_update(price, quantity);
|
||||
_report_state();
|
||||
}
|
||||
void SpreaderOrder::on_cancel()
|
||||
{
|
||||
void SpreaderOrder::on_cancel(TickToTradeCorID){
|
||||
if (!_active) return;
|
||||
LOG(OrderMan, Info, "SpreaderOrder.on_cancel key: {}", key().value());
|
||||
_state_worker->on_cancel();
|
||||
_report_state();
|
||||
}
|
||||
void SpreaderOrder::on_crack()
|
||||
{
|
||||
void SpreaderOrder::on_crack(TickToTradeCorID){
|
||||
if (!_active) return;
|
||||
LOG(OrderMan, Info, "SpreaderOrder.on_crack key: {}", key().value());
|
||||
_state_worker->on_crack();
|
||||
_report_state();
|
||||
}
|
||||
/***********************************************************************/
|
||||
/****************************ISpreaderManager***************************/
|
||||
/***********************************************************************/
|
||||
void SpreaderOrder::setup_new_quoter_order(size_t leg_index)
|
||||
{
|
||||
// ISpreaderManager ////////////////////
|
||||
////////////////////////////////////////
|
||||
void SpreaderOrder::setup_new_quoter_order(size_t leg_index){
|
||||
LOG(OrderMan, Info, "SpreaderOrder.setup_new_quoter_order key: {} leg: {}", key().value(), leg_index);
|
||||
SpreaderLegData& leg_data = _bookkeeping_data().get_leg_data(leg_index);
|
||||
bts::orderman::ChildOrder child_order = leg_data.quoter_order_builder().build();
|
||||
leg_data.order_update_message().re_init(child_order);
|
||||
}
|
||||
/***********************************************************************/
|
||||
/******************************Event Handlers***************************/
|
||||
/***********************************************************************/
|
||||
void SpreaderOrder::_on_time(const bts::mercury::TimerEvent& t_event)
|
||||
{
|
||||
// events from orders system ////////////////////
|
||||
/////////////////////////////////////////////////
|
||||
void SpreaderOrder::_on_time(const bts::mercury::TimerEvent& t_event){
|
||||
_curr_timestamp = t_event.ts;
|
||||
if (!_active) return;
|
||||
_state_worker->on_time (_curr_timestamp);
|
||||
}
|
||||
void SpreaderOrder::_on_book_update(const bts::orderman::BookUpdateEvent& book_event)
|
||||
{
|
||||
void SpreaderOrder::_on_book_update(const BookUpdateEvent& book_event){
|
||||
if (!_active) return;
|
||||
tracepoint (bts_trace,trace_order,(char*) "_on_book_update", 0,book_event.correlation_id);
|
||||
_state_worker->on_book (book_event.correlation_id, book_event.book);
|
||||
}
|
||||
void SpreaderOrder::_on_child_order_update(const bts::orderman::OrderStateUpdateEvent& ord_ev)
|
||||
{
|
||||
void SpreaderOrder::_on_child_order_update(const OrderStateUpdateEvent& ord_ev){
|
||||
if (!_active) return;
|
||||
SecurityKey sec_key = SecurityKey(ord_ev.definition->security_key());
|
||||
OrderKey order_key = ord_ev.key;
|
||||
@@ -105,21 +95,19 @@ void SpreaderOrder::_on_child_order_update(const bts::orderman::OrderStateUpdate
|
||||
Quantity filled_qty = Quantity (ord_ev.state.filled_quantity());
|
||||
Price last_price = Price(ord_ev.state.last_price());
|
||||
LOG(OrderMan, Diag, "SpreaderOrder::_on_child_order_update key: {} sec_key: {} order_key: {} status: {} filled_qty: {} last_price: {} msg: {}", key(), sec_key, order_key, status, filled_qty, last_price, ord_ev.state.last_msg ());
|
||||
if (!SpreaderUtils::order_acknowledged(status))
|
||||
{
|
||||
if (!SpreaderUtils::order_acknowledged(status)){
|
||||
LOG(OrderMan, Diag, "SpreaderOrder::_on_child_order_update, unack'ed callback for key: {} sec_key: {} order_key: {} status: {} ignoring", key(), sec_key, order_key, status);
|
||||
bts_assert(filled_qty.value() == 0);
|
||||
return;
|
||||
}
|
||||
SpreaderLegData* leg_data_ptr = _bookkeeping_data().find_leg_from_quoter_orderkey(order_key); // can also be found through sec_key
|
||||
if (leg_data_ptr != nullptr)
|
||||
{
|
||||
if (leg_data_ptr != nullptr){
|
||||
SpreaderLegData& leg_data = *leg_data_ptr;
|
||||
ChildOrderError order_error = _get_order_error (ord_ev, leg_data.quoter().expecting_cancel_update());
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_on_child_order_update quoter order key: {} sec_key: {} order_key: {} status: {} filled_qty: {} last_price: {} msg: {} child_order_error: {}", key(), sec_key, order_key, status, filled_qty, last_price, ord_ev.state.last_msg (), static_cast<int>(order_error));
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_on_child_order_update quoter order key: {} sec_key: {} order_key: {} status: {} filled_qty: {} last_price: {} msg: {} child_order_error: {}",
|
||||
key(), sec_key, order_key, status, filled_qty, last_price, ord_ev.state.last_msg (), static_cast<int>(order_error));
|
||||
leg_data.order_update_message().on_order_update(ord_ev);
|
||||
if (order_error == ChildOrderError::NONE)
|
||||
{
|
||||
if (order_error == ChildOrderError::NONE){
|
||||
if (leg_data.order_update_message().last_update_filled_quantity().value() > 0)
|
||||
_state_worker->on_quoter_order_fill (leg_data);
|
||||
else
|
||||
@@ -128,98 +116,87 @@ void SpreaderOrder::_on_child_order_update(const bts::orderman::OrderStateUpdate
|
||||
else
|
||||
_state_worker->on_quoter_order_error (leg_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
else{
|
||||
// should be a hedge order then
|
||||
SingleFillHedger* hedger = _bookkeeping_data().find_hedger_from_orderkey(order_key);
|
||||
if (hedger != nullptr)
|
||||
{
|
||||
ChildOrderError order_error = _get_order_error (ord_ev, hedger->expecting_cancel_update());
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_on_child_order_update hedger order: key: {} sec_key: {} order_key: {} status: {} filled_qty: {} last_price: {} msg: {} child_order_error: {}", key(), sec_key, order_key, status, filled_qty, last_price, ord_ev.state.last_msg (), static_cast<int>(order_error));
|
||||
if (hedger != nullptr){
|
||||
ChildOrderError order_error = _get_order_error (ord_ev, hedger->expecting_cancel_update());
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_on_child_order_update hedger order: key: {} sec_key: {} order_key: {} status: {} filled_qty: {} last_price: {} msg: {} child_order_error: {}",
|
||||
key(), sec_key, order_key, status, filled_qty, last_price, ord_ev.state.last_msg (), static_cast<int>(order_error));
|
||||
hedger->order_state().on_order_update(ord_ev);
|
||||
if (order_error == ChildOrderError::NONE)
|
||||
_state_worker->on_hedger_order_update (*hedger);
|
||||
_state_worker->on_hedger_order_update (*hedger);
|
||||
else
|
||||
_state_worker->on_hedger_order_error (*hedger);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(OrderMan, Error, "SpreaderOrder::_on_child_order_update unknown order key: {} sec_key: {} order_key: {} status: {} filled_qty: {} last_price: {} msg: {}", key(), sec_key, order_key, status, filled_qty, last_price, ord_ev.state.last_msg ());
|
||||
}
|
||||
LOG(OrderMan, Error, "SpreaderOrder::_on_child_order_update unknown order key: {} sec_key: {} order_key: {} status: {} filled_qty: {} last_price: {} msg: {}",
|
||||
key(), sec_key, order_key, status, filled_qty, last_price, ord_ev.state.last_msg ());
|
||||
}
|
||||
_state_worker->on_check_termination ();
|
||||
_report_state();
|
||||
}
|
||||
/***********************************************************************/
|
||||
/*************************State Transformations*************************/
|
||||
/***********************************************************************/
|
||||
void SpreaderOrder::_transform_to_START()
|
||||
{
|
||||
// State Transformations ////////////////////
|
||||
/////////////////////////////////////////////
|
||||
void SpreaderOrder::_transform_to_START(){
|
||||
bts_assert (_state_worker == nullptr);
|
||||
_switch_state (&_start_state_worker);
|
||||
}
|
||||
void SpreaderOrder::_transform_to_WORKING()
|
||||
{
|
||||
void SpreaderOrder::_transform_to_WORKING(){
|
||||
bts_assert(_state_worker->state() == SpreaderState::START);
|
||||
_switch_state(&_working_state_worker);
|
||||
}
|
||||
void SpreaderOrder::_transform_to_WAITING_FOR_CANCEL_ALL()
|
||||
{
|
||||
void SpreaderOrder::_transform_to_WAITING_FOR_CANCEL_ALL(){
|
||||
bts_assert(_state_worker->state() == SpreaderState::WORKING);
|
||||
_switch_state(&_waiting_for_cancel_all_state_worker);
|
||||
}
|
||||
void SpreaderOrder::_transform_to_END(std::string*)
|
||||
{
|
||||
void SpreaderOrder::_transform_to_END(std::string*){
|
||||
bts_assert(_state_worker->state() != SpreaderState::END);
|
||||
_switch_state(&_end_state_worker);
|
||||
}
|
||||
void SpreaderOrder::_switch_state (ISpreaderStateWorker* new_state)
|
||||
{
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_switch_state key: {}, {} -> {} ", key().value(), (_state_worker == nullptr ? -1 : (int)_state_worker->state()), (int)new_state->state());
|
||||
void SpreaderOrder::_switch_state (ISpreaderStateWorker* new_state){
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_switch_state key: {}, {} -> {} ",
|
||||
key().value(),
|
||||
(_state_worker == nullptr ? -1 : (int)_state_worker->state()),
|
||||
(int)new_state->state());
|
||||
_state_worker = new_state;
|
||||
_state_worker->on_transition();
|
||||
_report_state();
|
||||
}
|
||||
/***********************************************************************/
|
||||
/*********************************Misc**********************************/
|
||||
/***********************************************************************/
|
||||
void SpreaderOrder::_on_init_new(const bts::api::orderman::OrderDefinition& order_def, const SpreaderPreActivationOrderServices& spreader_pao_services)
|
||||
{
|
||||
LOG(OrderMan, Info, "SpreaderOrder.on_init_new key: {} order_def: {}", key().value(), bts::utils::pb::to_string(order_def));
|
||||
|
||||
void SpreaderOrder::_on_init_new(const bts::api::orderman::OrderDefinition& order_def,
|
||||
const SpreaderPreActivationOrderServices& spreader_pao_services){
|
||||
LOG(OrderMan, Info, "SpreaderOrder.on_init_new key: {} order_def: {}",
|
||||
key().value(), bts::utils::pb::to_string(order_def));
|
||||
bts_assert(!_inited());
|
||||
_state_worker->on_init_new(order_def, spreader_pao_services);
|
||||
}
|
||||
void SpreaderOrder::_on_activate(Price price, Quantity quantity, SpreaderOrderServices& order_services)
|
||||
{
|
||||
void SpreaderOrder::_on_activate(Price price, Quantity quantity, SpreaderOrderServices& order_services){
|
||||
bts_assert(_inited());
|
||||
bts_assert(!_activated());
|
||||
LOG(OrderMan, Info, "SpreaderOrder.on_activate key: {} price: {} quantity: {}", key().value(), price.value(), quantity.value());
|
||||
LOG(OrderMan, Info, "SpreaderOrder.on_activate key: {} price: {} quantity: {}",
|
||||
key().value(), price.value(), quantity.value());
|
||||
_order_services = new SpreaderOrderServices(order_services);
|
||||
_state_worker->on_activate(price, quantity, *_order_services);
|
||||
}
|
||||
void SpreaderOrder::_cancel_orders(bool cancel_quotes, bool cancel_hedges)
|
||||
{
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_cancel_orders key: {}, cancel_quotes: {} cancel_hedges: {}", key().value(), cancel_quotes, cancel_hedges);
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++)
|
||||
{
|
||||
void SpreaderOrder::_cancel_orders(bool cancel_quotes, bool cancel_hedges){
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_cancel_orders key: {}, cancel_quotes: {} cancel_hedges: {}",
|
||||
key().value(), cancel_quotes, cancel_hedges);
|
||||
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 (cancel_quotes)
|
||||
{
|
||||
if (cancel_quotes){
|
||||
if (leg_data.leg_property_data().is_active_quoting())
|
||||
leg_data.quoter().cancel_order();
|
||||
}
|
||||
if (cancel_hedges)
|
||||
for (size_t h_idx = 0; h_idx < leg_data.n_hedgers(); h_idx++)
|
||||
{
|
||||
for (size_t h_idx = 0; h_idx < leg_data.n_hedgers(); h_idx++){
|
||||
SingleFillHedger& hedger = leg_data.get_hedger(h_idx);
|
||||
hedger.cancel_hedge();
|
||||
}
|
||||
}
|
||||
}
|
||||
bool SpreaderOrder::_all_quoter_orders_in_terminal_state()
|
||||
{
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++)
|
||||
{
|
||||
bool SpreaderOrder::_all_quoter_orders_in_terminal_state(){
|
||||
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())
|
||||
if (!leg_data.quoter().is_terminal_state())
|
||||
@@ -227,14 +204,10 @@ bool SpreaderOrder::_all_quoter_orders_in_terminal_state()
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// same as _all_quoter_orders_in_terminal_state, for hedgers
|
||||
bool SpreaderOrder::_all_hedger_orders_in_terminal_state()
|
||||
{
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++)
|
||||
{
|
||||
bool SpreaderOrder::_all_hedger_orders_in_terminal_state(){
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++){
|
||||
SpreaderLegData& leg_data = _bookkeeping_data().get_leg_data(q_idx);
|
||||
for (size_t h_idx = 0; h_idx < leg_data.n_hedgers(); h_idx++)
|
||||
{
|
||||
for (size_t h_idx = 0; h_idx < leg_data.n_hedgers(); h_idx++){
|
||||
SingleFillHedger& hedger = leg_data.get_hedger(h_idx);
|
||||
if (!hedger.is_terminal_state())
|
||||
return false;
|
||||
@@ -242,30 +215,23 @@ bool SpreaderOrder::_all_hedger_orders_in_terminal_state()
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void SpreaderOrder::_terminate_quoters ()
|
||||
{
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++)
|
||||
{
|
||||
void SpreaderOrder::_terminate_quoters (){
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++){
|
||||
SpreaderLegData& leg_data = _bookkeeping_data().get_leg_data(q_idx);
|
||||
leg_data.quoter().terminate();
|
||||
leg_data.quoter().terminate();
|
||||
}
|
||||
}
|
||||
void SpreaderOrder::_terminate_hedgers ()
|
||||
{
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++)
|
||||
{
|
||||
void SpreaderOrder::_terminate_hedgers (){
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++){
|
||||
SpreaderLegData& leg_data = _bookkeeping_data().get_leg_data(q_idx);
|
||||
for (size_t h_idx = 0; h_idx < leg_data.n_hedgers(); h_idx++)
|
||||
{
|
||||
for (size_t h_idx = 0; h_idx < leg_data.n_hedgers(); h_idx++){
|
||||
SingleFillHedger& hedger = leg_data.get_hedger(h_idx);
|
||||
hedger.terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
bool SpreaderOrder::_quoters_can_accept_modification ()
|
||||
{
|
||||
for (size_t q_idx = 0; q_idx < _bookkeeping_data().n_legs(); q_idx++)
|
||||
{
|
||||
bool SpreaderOrder::_quoters_can_accept_modification (){
|
||||
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())
|
||||
if (!leg_data.quoter().accept_modifications())
|
||||
@@ -273,46 +239,48 @@ bool SpreaderOrder::_quoters_can_accept_modification ()
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void SpreaderOrder::_send_hedge(size_t filled_leg_index, Price filled_price, Quantity filled_quantity,
|
||||
size_t hedge_leg_index)
|
||||
{
|
||||
void SpreaderOrder::_send_hedge(size_t filled_leg_index,
|
||||
Price filled_price,
|
||||
Quantity filled_quantity,
|
||||
size_t hedge_leg_index){
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_send_hedge key: {}, filled_leg_index: {} filled_price: {} filled_quantity: {} hedge_leg_index: {}",
|
||||
key().value(), filled_leg_index, filled_price.value(), filled_quantity.value(), hedge_leg_index);
|
||||
Price hedge_price;
|
||||
Quantity hedge_quantity;
|
||||
_compute_hedge_price_quantity (filled_leg_index, filled_price, filled_quantity, hedge_leg_index,
|
||||
_compute_hedge_price_quantity (filled_leg_index, filled_price,
|
||||
filled_quantity, hedge_leg_index,
|
||||
hedge_price, hedge_quantity);
|
||||
SingleFillHedger& hedger = _bookkeeping_data().add_new_hedger (*this, hedge_leg_index, hedge_price, hedge_quantity);
|
||||
SingleFillHedger& hedger = _bookkeeping_data().add_new_hedger (*this,
|
||||
hedge_leg_index,
|
||||
hedge_price,
|
||||
hedge_quantity);
|
||||
hedger.init ();
|
||||
}
|
||||
void SpreaderOrder::_compute_hedge_price_quantity(size_t filled_leg_index,
|
||||
Price fill_price, Quantity fill_quantity,
|
||||
size_t hedge_leg_index,
|
||||
Price& hedge_price, Quantity& hedge_quantity)
|
||||
{
|
||||
Price& hedge_price, Quantity& hedge_quantity){
|
||||
Price computed_hedge_price;
|
||||
SpreaderLegData& filled_leg_data = _bookkeeping_data().get_leg_data(filled_leg_index);
|
||||
SpreaderLegData& hedged_leg_data = _bookkeeping_data().get_leg_data(hedge_leg_index);
|
||||
if (_bookkeeping_data().n_legs() == 2)
|
||||
{
|
||||
SpreaderUtils::compute_hedge_price_size_2_legs (_bookkeeping_data().price(),
|
||||
filled_leg_data.leg_property_data(), fill_price, fill_quantity,
|
||||
hedged_leg_data.leg_property_data(), computed_hedge_price, hedge_quantity);
|
||||
}
|
||||
else
|
||||
{
|
||||
else{
|
||||
const QuoterLegDriverData& driver_data = filled_leg_data.quoter().driver_data(hedge_leg_index);
|
||||
SpreaderUtils::compute_hedge_price_size_n_legs (filled_leg_data.leg_property_data(), fill_price, fill_quantity,
|
||||
hedged_leg_data.leg_property_data(), driver_data,
|
||||
computed_hedge_price, hedge_quantity);
|
||||
}
|
||||
hedge_price = SpreaderUtils::apply_pre_hedge_parameters (hedged_leg_data.leg_property_data().leg_params().pre_hedge_params(), hedged_leg_data.book(), hedged_leg_data.leg_property_data().trading_side(), hedged_leg_data.leg_property_data().tick_size(),
|
||||
hedge_price = SpreaderUtils::apply_pre_hedge_parameters (hedged_leg_data.leg_property_data().leg_params().pre_hedge_params(),
|
||||
hedged_leg_data.book(),
|
||||
hedged_leg_data.leg_property_data().trading_side(),
|
||||
hedged_leg_data.leg_property_data().tick_size(),
|
||||
computed_hedge_price, hedge_quantity);
|
||||
}
|
||||
void SpreaderOrder::_report_state ()
|
||||
{
|
||||
if (_activated())
|
||||
{
|
||||
void SpreaderOrder::_report_state (){
|
||||
if (_activated()){
|
||||
SpreaderBookkeepingData& bookkeeping_data = _bookkeeping_data();
|
||||
api::orderman::OrderStatus status = _get_order_status ();
|
||||
Quantity filled_qty; Price avg_filled_price;
|
||||
@@ -322,17 +290,15 @@ void SpreaderOrder::_report_state ()
|
||||
_reported_state.set_quantity(bookkeeping_data.quantity().value());
|
||||
_reported_state.set_filled_quantity(filled_qty.value());
|
||||
_reported_state.set_avg_price(avg_filled_price.value());
|
||||
// TODO fill in filled_qty etc
|
||||
// TODO fill in filled_qty etc
|
||||
LOG(OrderMan, Info, "SpreaderOrder::_report_state key: {} status: {} price: {} qty: {} filled: {} abg_price: {}",
|
||||
key(), status, _reported_state.price(), _reported_state.quantity(), _reported_state.filled_quantity(), _reported_state.avg_price());
|
||||
_order_services->report_state(_reported_state);
|
||||
}
|
||||
}
|
||||
bts::api::orderman::OrderStatus SpreaderOrder::_get_order_status () const
|
||||
{
|
||||
SpreaderState curr_state = _state_worker == nullptr ? SpreaderState::START : _state_worker->state();
|
||||
switch (curr_state)
|
||||
{
|
||||
bts::api::orderman::OrderStatus SpreaderOrder::_get_order_status () const{
|
||||
SpreaderState curr_state = _state_worker == nullptr ? SpreaderState::START : _state_worker->state();
|
||||
switch (curr_state){
|
||||
case SpreaderState::WORKING:
|
||||
return bts::api::orderman::OrderStatus::ORDER_STATUS_WORKING;
|
||||
case SpreaderState::WAITING_FOR_CANCEL_ALL:
|
||||
@@ -340,7 +306,7 @@ bts::api::orderman::OrderStatus SpreaderOrder::_get_order_status () const
|
||||
case SpreaderState::END:
|
||||
return _cancelled ?
|
||||
bts::api::orderman::OrderStatus::ORDER_STATUS_CANCELED :
|
||||
bts::api::orderman::OrderStatus::ORDER_STATUS_COMPLETED;
|
||||
bts::api::orderman::OrderStatus::ORDER_STATUS_COMPLETED;
|
||||
case SpreaderState::START:
|
||||
return bts::api::orderman::OrderStatus::ORDER_STATUS_INACTIVE;
|
||||
default:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "server/sparkd/orderman/events/BookUpdateEvent.h"
|
||||
#include "server/sparkd/mercury/events/TimerEvent.h"
|
||||
|
||||
using namespace bts::orderman;
|
||||
//using namespace bts::orderman;
|
||||
|
||||
namespace bts {
|
||||
namespace api {namespace orderman {class OrderState; class CreateSpreaderOrder;}}
|
||||
@@ -52,10 +52,11 @@ private:
|
||||
// is false if created from disk
|
||||
const bool _active;
|
||||
// this is the main data
|
||||
SpreaderBookkeepingData* _bookkeeping_data_ptr;
|
||||
std::unique_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)
|
||||
// state thats sent out in report (its kept here amd modified
|
||||
// vs creating a new one everytime)
|
||||
bts::api::orderman::OrderState _reported_state;
|
||||
// timestamp of last the on_time call
|
||||
bts::TimeStamp _curr_timestamp;
|
||||
@@ -65,56 +66,86 @@ private:
|
||||
SpreaderWorkingStateWorker _working_state_worker;
|
||||
SpreaderWaitingForCancelAllStateWorker _waiting_for_cancel_all_state_worker;
|
||||
SpreaderEndStateWorker _end_state_worker;
|
||||
/**************************Cons/Dest*******************************/
|
||||
// const/dest ////////////////////
|
||||
// (can not be copy/mv cons'd or
|
||||
// copied either way)
|
||||
//////////////////////////////////
|
||||
public:
|
||||
SpreaderOrder();
|
||||
SpreaderOrder(const SpreaderOrder&)=delete;
|
||||
SpreaderOrder(SpreaderOrder&&)=delete;
|
||||
SpreaderOrder& operator=(const SpreaderOrder&)=delete;
|
||||
SpreaderOrder& operator=(SpreaderOrder&&)=delete;
|
||||
virtual ~SpreaderOrder();
|
||||
/**************************Order Derived***************************/
|
||||
// Order Derived ////////////////////
|
||||
/////////////////////////////////////
|
||||
public:
|
||||
// inherited function to return the OrderDefinition. asserts
|
||||
// if on_init_new has not been called yet
|
||||
const api::orderman::OrderDefinition& definition() const override;
|
||||
// this is the construction, can be slow
|
||||
void on_init_new(const bts::api::orderman::OrderDefinition& order_def, const bts::orderman::PreActivationOrderServices&) override;
|
||||
void on_init_new(const bts::api::orderman::OrderDefinition& order_def,
|
||||
const PreActivationOrderServices&) override;
|
||||
// called when the order is recovered from disk (not dealing with it now)
|
||||
void on_init_recovered(bts::api::orderman::OrderRecord const&, const bts::orderman::PreActivationOrderServices&) override;
|
||||
void on_init_recovered(bts::api::orderman::OrderRecord const&,
|
||||
const PreActivationOrderServices&) override;
|
||||
// this is in the critical path, orders can be submitted now
|
||||
// (this is only called once and after the on_init_new)
|
||||
void on_activate(Price, Quantity, OrderServices&) override;
|
||||
void on_activate(Price, Quantity, OrderServices&,TickToTradeCorID) override;
|
||||
// called when the user (or a higher level order)
|
||||
// updates the order's price or quantity
|
||||
void on_update(Price, Quantity) override;
|
||||
void on_update(Price, Quantity,TickToTradeCorID) override;
|
||||
// called when the user (or higher level order) cancels an order
|
||||
void on_cancel() override;
|
||||
void on_cancel(TickToTradeCorID) override;
|
||||
// crack means to let the lower level orders lose from the higher
|
||||
// level order and cancel the higher level order
|
||||
void on_crack() override;
|
||||
/*****************************ISpreaderManager*******************************/
|
||||
void on_crack(TickToTradeCorID) override;
|
||||
// ISpreaderManager ////////////////////
|
||||
////////////////////////////////////////
|
||||
public:
|
||||
bts::orderman::OrderKey spreader_order_key() const override {return key();}
|
||||
bts::TimeStamp current_time() const override {return _curr_timestamp;}
|
||||
const bts::orderman::Book& get_book(size_t leg_index) const override {return _bookkeeping_data().get_leg_data(leg_index).book();}
|
||||
const LegPropertyData& get_leg_data(size_t leg_index) const override {return _bookkeeping_data().get_leg_data(leg_index).leg_property_data();}
|
||||
bts::orderman::OrderKey spreader_order_key() const override {
|
||||
return key();
|
||||
}
|
||||
bts::TimeStamp current_time() const override {
|
||||
return _curr_timestamp;
|
||||
}
|
||||
const bts::orderman::Book& get_book(size_t leg_index) const override {
|
||||
return _bookkeeping_data().get_leg_data(leg_index).book();
|
||||
}
|
||||
const LegPropertyData& get_leg_data(size_t leg_index) const override {
|
||||
return _bookkeeping_data().get_leg_data(leg_index).leg_property_data();
|
||||
}
|
||||
void setup_new_quoter_order (size_t leg_index) override;
|
||||
private:
|
||||
/**************************Event Handlers***********************************/
|
||||
// events
|
||||
// events from orders system ////////////////////
|
||||
/////////////////////////////////////////////////
|
||||
void _on_time (const bts::mercury::TimerEvent&);
|
||||
void _on_book_update(const bts::orderman::BookUpdateEvent&);
|
||||
void _on_child_order_update(const bts::orderman::OrderStateUpdateEvent&);
|
||||
/***************************State Transformations***************************/
|
||||
void _on_book_update(const BookUpdateEvent&);
|
||||
void _on_child_order_update(const OrderStateUpdateEvent&);
|
||||
// State Transformations ////////////////////
|
||||
/////////////////////////////////////////////
|
||||
void _transform_to_START();
|
||||
void _transform_to_WORKING();
|
||||
void _transform_to_WAITING_FOR_CANCEL_ALL();
|
||||
// optionally parameter to state the end reason
|
||||
void _transform_to_END(std::string* m = nullptr);
|
||||
// swtches to the state passed in the arg
|
||||
void _switch_state (ISpreaderStateWorker* new_state);
|
||||
/***************************Misc***************************/
|
||||
bool _inited () const {return _bookkeeping_data_ptr != nullptr;}
|
||||
bool _activated () {return _order_services != nullptr;}
|
||||
void _on_init_new(const bts::api::orderman::OrderDefinition&, const SpreaderPreActivationOrderServices&);
|
||||
// this means on_init_new has been called and we have the
|
||||
// the structure built
|
||||
// misc ////////////////////
|
||||
////////////////////////////
|
||||
bool _inited () const {
|
||||
return _bookkeeping_data_ptr != nullptr;
|
||||
}
|
||||
// this means on_activate has been called
|
||||
bool _activated () {
|
||||
return _order_services != nullptr;
|
||||
}
|
||||
void _on_init_new(const bts::api::orderman::OrderDefinition&,
|
||||
const SpreaderPreActivationOrderServices&);
|
||||
void _on_activate(Price, Quantity, SpreaderOrderServices&);
|
||||
// this calls the cancel on quoter and hedger orders
|
||||
// this calls the cancel on quoters and hedgers
|
||||
void _cancel_orders(bool cancel_quotes, bool cancel_hedges);
|
||||
// iterates through all quoters and returns true if they
|
||||
// are all in terminal state
|
||||
@@ -126,12 +157,15 @@ private:
|
||||
void _terminate_quoters ();
|
||||
// same as the above for hedgers
|
||||
void _terminate_hedgers();
|
||||
// this returns true is all quoters can accept modification
|
||||
bool _quoters_can_accept_modification();
|
||||
// this sends a hedge (creates a hedger and inits it)
|
||||
// on leg 'leg_to_hedge_index' in response to a fill
|
||||
// in leg 'filled_leg_index' with price 'filled_price'
|
||||
// on leg 'filled_leg_index' with price 'filled_price'
|
||||
// quantity 'filled_quantity'
|
||||
void _send_hedge(size_t filled_leg_index, Price filled_price, Quantity filled_quantity, size_t leg_to_hedge_index);
|
||||
void _send_hedge(size_t filled_leg_index, Price filled_price,
|
||||
Quantity filled_quantity,
|
||||
size_t leg_to_hedge_index);
|
||||
// purely a computating function that computes the
|
||||
// hedge price and quantity (last 2 args) for leg
|
||||
// 'hedged_leg_index' for a fill on leg 'filled_leg_index'
|
||||
@@ -142,32 +176,38 @@ private:
|
||||
Price& hedge_price, Quantity& hedge_quantity);
|
||||
// sends the report out
|
||||
void _report_state ();
|
||||
SpreaderBookkeepingData& _bookkeeping_data()
|
||||
{
|
||||
SpreaderBookkeepingData& _bookkeeping_data(){
|
||||
bts_assert (_bookkeeping_data_ptr != nullptr);
|
||||
return *_bookkeeping_data_ptr;
|
||||
}
|
||||
const SpreaderBookkeepingData& _bookkeeping_data() const
|
||||
{
|
||||
const SpreaderBookkeepingData& _bookkeeping_data() const {
|
||||
bts_assert (_bookkeeping_data_ptr != nullptr);
|
||||
return *_bookkeeping_data_ptr;
|
||||
}
|
||||
// just sets the price and qty in the bookkeeping data
|
||||
void _set_price_quantity (Price price, Quantity quantity) {_bookkeeping_data().update_order (price, quantity);}
|
||||
void _set_price_quantity (Price price, Quantity quantity) {
|
||||
_bookkeeping_data().update_order (price, quantity);
|
||||
}
|
||||
// returns the equivelant order status of the spreader order
|
||||
bts::api::orderman::OrderStatus _get_order_status () const;
|
||||
static ChildOrderError _get_order_error (const bts::orderman::OrderStateUpdateEvent& ord_ev, bool expecting_cancel)
|
||||
{
|
||||
|
||||
static ChildOrderError _get_order_error (const OrderStateUpdateEvent& ord_ev,
|
||||
bool expecting_cancel){
|
||||
bts::api::orderman::OrderStatus order_status = ord_ev.state.status();
|
||||
if (SpreaderUtils::order_in_error_state(order_status))
|
||||
return ChildOrderError::ORDER_STATE_ERROR;
|
||||
if (SpreaderUtils::SpreaderUtils::order_in_cancelled_state(order_status) && !expecting_cancel)
|
||||
return ChildOrderError::UNEXPECTED_CANCEL_ERROR;
|
||||
// this is an inelegant way to detect modify errors
|
||||
if (!SpreaderUtils::order_in_terminal_state(order_status) && ord_ev.state.last_msg().length() > 0)
|
||||
return ChildOrderError::MODIFY_ERROR;
|
||||
return ChildOrderError::NONE;
|
||||
}
|
||||
/**************************Testing only***********************************/
|
||||
// testing only //////////////////
|
||||
// this provides direct way to inject
|
||||
// data into the spreader, as well as
|
||||
// access to the internal state
|
||||
//////////////////////////////////
|
||||
public:
|
||||
SpreaderState test_only_func_spreader_state () {return _state_worker->state();}
|
||||
SpreaderLegData& test_only_func_spreader_leg_data (size_t index) {return _bookkeeping_data().get_leg_data (index);}
|
||||
|
||||
@@ -73,21 +73,23 @@ public:
|
||||
SingleFillHedger (SingleFillHedger&&);
|
||||
SingleFillHedger& operator=(const SingleFillHedger&) = delete;
|
||||
SingleFillHedger& operator=(SingleFillHedger&&) = delete;
|
||||
//1. leave the start state and submit the order
|
||||
// the a-f actions are associated with the state machine
|
||||
// side effect actions
|
||||
//a. leave the start state and submit the order
|
||||
void init();
|
||||
//2. called by the spreader when there is an order update
|
||||
//b. called by the spreader when there is an order update
|
||||
// reported by the exchange
|
||||
void on_order_update(const SpreaderChildOrderState& order_update_message);
|
||||
//3. called when the market data associated
|
||||
//c. called when the market data associated
|
||||
// with this security is changed
|
||||
void on_book(bts::orderman::TickToTradeCorID corid, const bts::orderman::Book& book);
|
||||
//4. called when the hedge order is cancelled, either
|
||||
//d. called when the hedge order is cancelled, either
|
||||
// manually or automatically
|
||||
void cancel_hedge();
|
||||
//5. immediatly terminates this hedger, without any attempts
|
||||
//e. immediatly terminates this hedger, without any attempts
|
||||
// to cancel the order
|
||||
void terminate ();
|
||||
//6. on the engine infrastructure time
|
||||
//f. 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,
|
||||
LegPropertyData this_leg_property,
|
||||
const 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,41 +60,43 @@ private:
|
||||
QuoterEndStateWorker _end_state_worker;
|
||||
public:
|
||||
SingleLegQuoter(ISpreaderManager& spreader,
|
||||
LegPropertyData this_leg_property,
|
||||
const LegPropertyData& this_leg_property,
|
||||
size_t n_spreader_legs,
|
||||
SpreaderChildOrderState& child_order_state);
|
||||
public:
|
||||
SingleLegQuoter() = delete;
|
||||
SingleLegQuoter (SingleLegQuoter&&);
|
||||
SingleLegQuoter (const SingleLegQuoter&) = delete;
|
||||
SingleLegQuoter(SingleLegQuoter&&); // implementation has an assert since it will not be called
|
||||
SingleLegQuoter(const SingleLegQuoter&) = delete;
|
||||
SingleLegQuoter& operator=(const SingleLegQuoter&) = delete;
|
||||
SingleLegQuoter& operator=(SingleLegQuoter&&) = delete;
|
||||
public:
|
||||
//1. this is called only once. this all could have been passed in
|
||||
// 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
|
||||
// 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);
|
||||
//2. called by the spreader a change in
|
||||
//b. 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);
|
||||
//3. called when the market data associated
|
||||
//c. 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&);
|
||||
//4. called when the order is cancelled, either
|
||||
//d. called when the order is cancelled, either
|
||||
// manually or automatically
|
||||
void cancel_order();
|
||||
//5. the quoter forgets everything and goes to the end state
|
||||
//e. the quoter forgets everything and goes to the end state
|
||||
void terminate ();
|
||||
//6. time update
|
||||
//f. time update
|
||||
void on_time(const bts::TimeStamp&);
|
||||
//7. this is called by the spreader when a quoting fill
|
||||
//g. 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);
|
||||
//8. called when the user modified the spreader order price or quantity
|
||||
//h. 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
|
||||
@@ -125,8 +127,8 @@ public:
|
||||
private:
|
||||
bts::orderman::OrderKey _key() {return _child_order_state.order_key();}
|
||||
TimeStamp _current_time () const {return _spreader.current_time();}
|
||||
std::string _log_prefix()
|
||||
{
|
||||
// TODO: state out and move the string to a const
|
||||
std::string _log_prefix(){
|
||||
std::ostringstream oss;
|
||||
oss << " spreader_order_key: " << _spreader.spreader_order_key();
|
||||
oss << " sec_key: " << _this_leg_data.security_key();
|
||||
@@ -142,8 +144,12 @@ private:
|
||||
bool _target_reached(){
|
||||
return _price_quantity_state.target_reached();
|
||||
}
|
||||
void _place_order (Price price, Quantity quantity) {_child_order_state.place_order(price, quantity);}
|
||||
void _modify_order (Price price, Quantity quantity) {_child_order_state.modify_order(price, quantity);}
|
||||
void _place_order (Price price, Quantity quantity, TickToTradeCorID cid = 0) {
|
||||
_child_order_state.place_order(price, quantity, cid);
|
||||
}
|
||||
void _modify_order (Price price, Quantity quantity, TickToTradeCorID cid = 0) {
|
||||
_child_order_state.modify_order(price, quantity, cid);
|
||||
}
|
||||
void _cancel_order () {_child_order_state.cancel_order();}
|
||||
// this updates the hedge reduction in _price_quantity_state
|
||||
void _incorporate_other_leg_fill (size_t leg_index, Quantity filled_quantity);
|
||||
|
||||
+6
-4
@@ -114,7 +114,8 @@ public:
|
||||
}
|
||||
// order actions ////////////////////
|
||||
/////////////////////////////////////
|
||||
void place_order(Price price, Quantity quantity){
|
||||
void place_order(Price price, Quantity quantity,
|
||||
TickToTradeCorID cid = 0){
|
||||
_out_price = price;
|
||||
_out_quantity = quantity;
|
||||
bts_assert(_n_place_orders == 0);
|
||||
@@ -122,16 +123,17 @@ public:
|
||||
LOG(OrderMan, Info, "SpreaderChildOrderState::place_order order_key: {} is_quoting: {} sec_key: {} live: {} price: {} quantity: {}",
|
||||
order_key().value(), _is_quoting, _sec_key, _is_live, price.value(), quantity.value());
|
||||
if (_is_live)
|
||||
_child_order.activate(price, quantity);
|
||||
_child_order.activate(price, quantity, cid);
|
||||
}
|
||||
void modify_order(Price price, Quantity quantity){
|
||||
void modify_order(Price price, Quantity quantity,
|
||||
TickToTradeCorID cid = 0){
|
||||
_out_price = price;
|
||||
_out_quantity = quantity;
|
||||
_n_modify_orders++;
|
||||
LOG(OrderMan, Info, "SpreaderChildOrderState::modify_order order_key: {} is_quoting: {} sec_key: {} live: {} price: {} quantity: {} mod_no: {}",
|
||||
order_key().value(), _is_quoting, _sec_key, _is_live, price.value(), quantity.value(), _n_modify_orders);
|
||||
if (_is_live)
|
||||
_child_order.update(price, quantity);
|
||||
_child_order.update(price, quantity, cid);
|
||||
}
|
||||
void cancel_order(){
|
||||
bts_assert(_n_cancel_orders == 0);
|
||||
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
if (price_change_ticks != 0){
|
||||
bool price_increasing = price_change_ticks > 0;
|
||||
uint32_t abs_price_change_ticks = static_cast<uint32_t>(std::abs(price_change_ticks));
|
||||
LOG(OrderMan, Info, "SpreaderUtils::reprice_quote_mute_zone current_price: {} new_price: {} price_change_ticks: {} abs_price_change_ticks: {} inside_limit_ticks: {} outside_limit_ticks: {}",
|
||||
LOG(OrderMan, Diag, "SpreaderUtils::reprice_quote_mute_zone current_price: {} new_price: {} price_change_ticks: {} abs_price_change_ticks: {} inside_limit_ticks: {} outside_limit_ticks: {}",
|
||||
current_price, new_price, price_change_ticks, abs_price_change_ticks, quote_mute_zone.inside_ticks(), quote_mute_zone.outside_ticks());
|
||||
if (quote_side == api::SIDE_BUY){
|
||||
if (price_increasing) // moving toward inside market
|
||||
|
||||
+4
-2
@@ -7,8 +7,10 @@
|
||||
namespace bts{
|
||||
namespace orderman{
|
||||
|
||||
QuoterState QuoterEndStateWorker::state () const {return QuoterState::END;}
|
||||
void QuoterEndStateWorker::on_transition (){
|
||||
QuoterState QuoterEndStateWorker::state () const {
|
||||
return QuoterState::END;
|
||||
}
|
||||
void QuoterEndStateWorker::on_transition (){
|
||||
bool is_final = _quoter->_is_permenantly_cancelled || _quoter->_price_quantity_state.target_reached ();
|
||||
if (!is_final){
|
||||
// getting new order
|
||||
|
||||
+8
-1
@@ -23,7 +23,8 @@ void QuoterWaitingForFirstAckWorker::on_order_update (const SpreaderChildOrderSt
|
||||
}
|
||||
void QuoterWaitingForFirstAckWorker::on_book (bts::orderman::TickToTradeCorID,
|
||||
size_t,
|
||||
const bts::orderman::Book&){
|
||||
const bts::orderman::Book&){
|
||||
_analyze_and_act();
|
||||
}
|
||||
void QuoterWaitingForFirstAckWorker::cancel(){
|
||||
_quoter->_transform_to_WAITING_FOR_FIRST_ACK_CANCELLED ();
|
||||
@@ -46,5 +47,11 @@ void QuoterWaitingForFirstAckWorker::update_spreader_order (Price price,
|
||||
void QuoterWaitingForFirstAckWorker::_analyze_and_act (){
|
||||
if (_quoter->_price_quantity_state.target_reached())
|
||||
_quoter->_transform_to_WAITING_FOR_FIRST_ACK_CANCELLED();
|
||||
else{
|
||||
Price computed_price; Quantity computed_quantity;
|
||||
_quoter->_compute_price_quantity (computed_price, computed_quantity);
|
||||
if (computed_quantity.value() == 0)
|
||||
_quoter->_transform_to_WAITING_FOR_FIRST_ACK_CANCELLED();
|
||||
}
|
||||
}
|
||||
}}
|
||||
Loaded 100 of 142 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user