Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff461d42b8 | ||
|
|
12dd3909bf | ||
|
|
0af5b78318 | ||
|
|
c652032ddf | ||
|
|
7bd868b753 | ||
|
|
1bc4510862 | ||
|
|
b1fb964648 | ||
|
|
ee19f7c7da | ||
|
|
cd41632b53 | ||
|
|
ecd384501e | ||
|
|
0483460ac9 | ||
|
|
0bc24fa7b7 | ||
|
|
ac60a1447e | ||
|
|
445bbb10f5 | ||
|
|
70c006f797 | ||
|
|
3a5810d8ac | ||
|
|
29c772cbb6 | ||
|
|
0b03180c4d | ||
|
|
1518906b7f | ||
|
|
31fe708907 | ||
|
|
4b2f39c536 | ||
|
|
040960d33b | ||
|
|
dcd8984b33 |
No files matched your search
@@ -18,6 +18,9 @@ insert_anchor_links = "right"
|
||||
- Fixed a number of problems with filtering on the Fills form
|
||||
- On the Lean algo, OnToward and Away are now calculated with respect to the opposite inside market at time of the order launch
|
||||
- Iceberg Spreaders with queue holders are not erroneously cancelled after the first fill
|
||||
- Added security symbol column to fills window
|
||||
- Re-organized ladder settings dialog to make it clearer and easier to use
|
||||
|
||||
## 2.1.4
|
||||
### User Visible Changes
|
||||
- Fixed handling of cancel button in security picker
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#/bin/bash
|
||||
#python -m mkdocs build -d site
|
||||
|
||||
cp -r ../../src/sdk/doc/* ./content/bts_spark_sdk/Server/
|
||||
./run_zola.sh build
|
||||
|
||||
scp -r ./public bts@bluetradesys.com:/home/bts/doc/
|
||||
@@ -14,7 +13,3 @@ sudo mv ./public/* /var/www/documentation/
|
||||
EOF
|
||||
|
||||
rm -r public
|
||||
cd ./content/bts_spark_sdk/server/
|
||||
find . -not -name '_index.md' -delete
|
||||
cd ../..
|
||||
|
||||
@@ -61,6 +61,14 @@ namespace mbts.spark {
|
||||
equality_comparer: SecurityDataHelper.EqualityComparer.IDComparer,
|
||||
comparer: Comparer<SecurityImmutable>.Default
|
||||
), DataGridFilterKnownTemplates.DiscreteValues, DefaultColumnWidths.Security);
|
||||
// symbol
|
||||
add_col(Columns.Symbol, DefaultColumnHeaders.Symbol, r => r.security.Symbol,
|
||||
DiscreteValuesFilterVM.Create<FillTuple, string>(
|
||||
co2.UserSymbol.get_all().Select(u => u.symbol).Distinct().ToList(),
|
||||
ft => ft.security?.Symbol ?? string.Empty,
|
||||
nameof(OrderBookRowVM.symbol)),
|
||||
DataGridFilterKnownTemplates.DiscreteValues,
|
||||
DefaultColumnWidths.Symbol);
|
||||
// order type
|
||||
var order_type_filter = dv_filter(OrderDataHelper.all_order_types, r => r.order?.order_type() ?? OrderType.None);
|
||||
add_col_expl(Columns.OrderType,
|
||||
@@ -190,6 +198,7 @@ namespace mbts.spark {
|
||||
case Columns.SecurityKey: return wrap(ft => ft.fill.SecurityKey);
|
||||
case Columns.Side: return wrap_default(ft => ft.fill.Side);
|
||||
case Columns.Size: return wrap(ft => ft.fill.LastShares);
|
||||
case Columns.Symbol: return wrap(ft => ft.security?.Symbol ?? string.Empty);
|
||||
case Columns.TimeInForce: return wrap_default(ft => ft.order?.time_in_force() ?? TimeInForce.Undefined);
|
||||
case Columns.User: return wrap(ft => ft.order?.user() ?? string.Empty);
|
||||
default: goto case Columns.FillTime;
|
||||
|
||||
@@ -7,7 +7,23 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for FillsView.xaml
|
||||
/// </summary>
|
||||
partial class FillsView : UserControl {
|
||||
public enum Columns { FillTime, User, Account, Exchange, Security, TimeInForce, OrderType, Side, Price, Size, FillKind, FillKey, OrderKey, SecurityKey }
|
||||
public enum Columns {
|
||||
FillTime,
|
||||
User,
|
||||
Account,
|
||||
Exchange,
|
||||
Security,
|
||||
Symbol,
|
||||
TimeInForce,
|
||||
OrderType,
|
||||
Side,
|
||||
Price,
|
||||
Size,
|
||||
FillKind,
|
||||
FillKey,
|
||||
OrderKey,
|
||||
SecurityKey
|
||||
}
|
||||
|
||||
public FillsView() =>
|
||||
InitializeComponent();
|
||||
|
||||
@@ -66,6 +66,10 @@ namespace bts.shell.fills {
|
||||
[ProtoMember(14)]
|
||||
[DIYGridColumnID(nameof(Columns.FillKind))]
|
||||
public Fill.Types.FillKind[] fill_kind { get; set; }
|
||||
|
||||
[ProtoMember(15)]
|
||||
[DIYGridColumnID(nameof(Columns.Symbol))]
|
||||
public string[] symbol { get; set; }
|
||||
}
|
||||
|
||||
static class FillsFilterStatesExtensions {
|
||||
@@ -86,6 +90,7 @@ namespace bts.shell.fills {
|
||||
pb.add_discrete_values(fs.order_type, ft => ft.order_type());
|
||||
pb.add_discrete_values(fs.side, ft => ft.fill.Side);
|
||||
pb.add_discrete_values(fs.fill_kind, ft => ft.fill.FillKind);
|
||||
pb.add_discrete_values(fs.symbol, ft => ft.security?.Symbol ?? string.Empty);
|
||||
// numeric
|
||||
pb.add_numeric(fs.size, ft => ft.fill.LastShares);
|
||||
pb.add_numeric(fs.price, ft => ft.fill.LastPrice);
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace bts.shell.fills {
|
||||
|
||||
static class DefaultColumns {
|
||||
public static GridColumnSettings security => new GridColumnSettings(col_id(Columns.Security), DefaultColumnHeaders.Security, width: DefaultColumnWidths.Security);
|
||||
public static GridColumnSettings symbol => new GridColumnSettings(col_id(Columns.Symbol), DefaultColumnHeaders.Symbol, width: DefaultColumnWidths.Symbol);
|
||||
public static GridColumnSettings fill_time => new GridColumnSettings(col_id(Columns.FillTime), DefaultColumnHeaders.FillTime, width: DefaultColumnWidths.Time);
|
||||
public static GridColumnSettings user => new GridColumnSettings(col_id(Columns.User), DefaultColumnHeaders.User, width: DefaultColumnWidths.User);
|
||||
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
|
||||
@@ -62,6 +63,7 @@ namespace bts.shell.fills {
|
||||
public static IEnumerable<GridColumnSettings> all {
|
||||
get {
|
||||
yield return security;
|
||||
yield return symbol;
|
||||
yield return fill_time;
|
||||
yield return user;
|
||||
yield return account;
|
||||
|
||||
@@ -327,7 +327,15 @@ namespace mbts.spark {
|
||||
|
||||
public enum BadgeStyle { Normal, Alternate }
|
||||
|
||||
public enum NetChangeTerms { None, Dollars, Ticks }
|
||||
// TODO: rename to PriceDisplayFormat (today is merge monday, since this isn't a functional change, it is not worth the potential merge headaches right now)
|
||||
public enum NetChangeTerms {
|
||||
[Description("Standard Ticks")]
|
||||
None,
|
||||
[Description("Net Change Dollars")]
|
||||
Dollars,
|
||||
[Description("Net Change Ticks")]
|
||||
Ticks
|
||||
}
|
||||
|
||||
[ProtoMember(1)]
|
||||
public double row_height { get; set; }
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace mbts.spark {
|
||||
readonly Property<LadderSettings.BadgeStyle> _badge_style;
|
||||
readonly Property<uint> _alternate_order_size;
|
||||
readonly DelegateProperty<double> _row_height;
|
||||
LadderSettings.NetChangeTerms _price_format;
|
||||
Func<double> _get_order_entry_panel_width, _get_zoom_factor;
|
||||
LadderSettings _current;
|
||||
LadderColumnSettingsVM
|
||||
@@ -30,8 +31,8 @@ namespace mbts.spark {
|
||||
_bid = new LadderColumnSettingsVM("Bid"),
|
||||
_price = new LadderColumnSettingsVM("Price"),
|
||||
_ask = new LadderColumnSettingsVM("Ask"),
|
||||
_last = new LadderColumnSettingsVM("Last", true),
|
||||
_piq = new LadderColumnSettingsVM("PIQ", true);
|
||||
_last = new LadderColumnSettingsVM("Last", LadderSettingsView.LastColumnTemplateKey, true),
|
||||
_piq = new LadderColumnSettingsVM("PIQ", can_toggle_show: true);
|
||||
VolumeColumnVM _volume = new VolumeColumnVM();
|
||||
LadderMarketBreakLineVM
|
||||
_bid_mkt_line = new LadderMarketBreakLineVM("Bid"),
|
||||
@@ -59,10 +60,6 @@ namespace mbts.spark {
|
||||
_cancel_buys_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Buys"),
|
||||
_cancel_sells_visibility = new OrderEntryPanelItemVisibilityVM("Cancel Sells"),
|
||||
_cancel_all_visibility = new OrderEntryPanelItemVisibilityVM("Cancel All");
|
||||
readonly Property<bool>
|
||||
_show_in_net_change,
|
||||
_show_in_net_change_dollars,
|
||||
_show_in_net_change_ticks;
|
||||
|
||||
public LadderSettingsVM(OrdersModel model, bool is_synthetic, Func<double> get_left_panel_width, Func<double> get_zoom_factor) {
|
||||
this.is_synthetic = is_synthetic;
|
||||
@@ -74,10 +71,6 @@ namespace mbts.spark {
|
||||
_alternate_order_size = backing<uint>(nameof(alternate_order_size));
|
||||
_get_order_entry_panel_width = get_left_panel_width;
|
||||
_get_zoom_factor = get_zoom_factor;
|
||||
_show_in_net_change = backing<bool>(nameof(show_in_net_change));
|
||||
_show_in_net_change_dollars = backing<bool>(nameof(show_in_net_change_dollars));
|
||||
_show_in_net_change_ticks = backing<bool>(nameof(show_in_net_change_ticks));
|
||||
|
||||
// be sure to initialize backings before this measure_row_height call
|
||||
_row_height = backing(nameof(row_height), () => LadderRender.measure_row_height(get_result()));
|
||||
_orders.font_size_changed += on_font_size_changed;
|
||||
@@ -110,6 +103,8 @@ namespace mbts.spark {
|
||||
}
|
||||
|
||||
public MarketSizeDisplayVM size_display { get; } = new MarketSizeDisplayVM();
|
||||
public LadderSettings.NetChangeTerms price_format { get => _price_format; set => Set(ref _price_format, value); }
|
||||
public IEnumerable<LadderSettings.NetChangeTerms> price_format_options => EnumHelper.get_values<LadderSettings.NetChangeTerms>();
|
||||
public FavoriteAlgosVM favorite_algos_vm { get; }
|
||||
|
||||
public double row_height => _row_height.Value;
|
||||
@@ -175,32 +170,6 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public bool show_in_net_change {
|
||||
get => _show_in_net_change.Value;
|
||||
set {
|
||||
// when unchecking, clear out dollars and ticks
|
||||
if (!value) {
|
||||
show_in_net_change_dollars = false;
|
||||
show_in_net_change_ticks = false;
|
||||
}
|
||||
// when checking, by defalt check dollars
|
||||
if (value) {
|
||||
show_in_net_change_dollars = true;
|
||||
}
|
||||
_show_in_net_change.Value = value;
|
||||
}
|
||||
}
|
||||
public bool show_in_net_change_dollars {
|
||||
get => _show_in_net_change_dollars.Value;
|
||||
set => _show_in_net_change_dollars.Value = value;
|
||||
}
|
||||
|
||||
public bool show_in_net_change_ticks {
|
||||
get => _show_in_net_change_ticks.Value;
|
||||
set => _show_in_net_change_ticks.Value = value;
|
||||
}
|
||||
|
||||
|
||||
public string key => GridSettingsTemplateKeys.Ladder;
|
||||
|
||||
public void load(LadderSettings settings) {
|
||||
@@ -241,13 +210,11 @@ namespace mbts.spark {
|
||||
_cancel_buys_visibility.show = settings.cancel_buys_visible;
|
||||
_cancel_sells_visibility.show = settings.cancel_sells_visible;
|
||||
_cancel_all_visibility.show = settings.cancel_all_visible;
|
||||
price_format = settings.net_change;
|
||||
// do not read the order entry panel width here from settings
|
||||
// because it could have changed from the UI
|
||||
// there might be a better way of doing this, like how
|
||||
// does the widths of the ladder grid column pull this off?
|
||||
show_in_net_change = settings.net_change != LadderSettings.NetChangeTerms.None;
|
||||
show_in_net_change_dollars = settings.net_change == LadderSettings.NetChangeTerms.Dollars;
|
||||
show_in_net_change_ticks = settings.net_change == LadderSettings.NetChangeTerms.Ticks;
|
||||
}
|
||||
|
||||
public LadderSettings get_result() {
|
||||
@@ -291,19 +258,8 @@ namespace mbts.spark {
|
||||
clone.order_entry_panel_initial_width = _get_order_entry_panel_width();
|
||||
clone.zoom_slider_visible = _zoom_slider_visbility.show;
|
||||
clone.zoom_factor = _get_zoom_factor();
|
||||
clone.net_change = _create_net_change_terms(show_in_net_change_dollars, show_in_net_change_ticks);
|
||||
clone.net_change = price_format;
|
||||
return clone;
|
||||
|
||||
LadderSettings.NetChangeTerms _create_net_change_terms(bool dollars, bool ticks) {
|
||||
if (dollars) {
|
||||
return LadderSettings.NetChangeTerms.Dollars;
|
||||
}
|
||||
if (ticks) {
|
||||
return LadderSettings.NetChangeTerms.Ticks;
|
||||
}
|
||||
return LadderSettings.NetChangeTerms.None;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Dispose() => favorite_algos_vm.Dispose();
|
||||
@@ -323,11 +279,12 @@ namespace mbts.spark {
|
||||
readonly DelegateProperty<FontWeight> _font_weight;
|
||||
readonly DelegateProperty<string> _font_display_name;
|
||||
readonly Property<bool> _show;
|
||||
readonly bool _can_toggle_visibility;
|
||||
readonly string _template_key;
|
||||
|
||||
public LadderColumnSettingsVM(string name, bool can_toggle_visibility = false) {
|
||||
public LadderColumnSettingsVM(string name, string template_key = null, bool can_toggle_show = false) {
|
||||
this.name = name;
|
||||
_can_toggle_visibility = can_toggle_visibility;
|
||||
_template_key = template_key;
|
||||
this.can_toggle_show = can_toggle_show;
|
||||
foreground_palettes = new ObservableCollection<ColorPaletteVM>(ColorPalette.all_presets().Select(p => new ColorPaletteVM(p)));
|
||||
background_palettes = new ObservableCollection<ColorPaletteVM>(ColorPalette.all_presets().Select(p => new ColorPaletteVM(p)));
|
||||
_show = backing(nameof(show), true);
|
||||
@@ -340,8 +297,14 @@ namespace mbts.spark {
|
||||
_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);
|
||||
toggle_show_details_cmd = new DelegateCommand(() => {
|
||||
show_details = !show_details;
|
||||
RaisePropertyChanged(nameof(show_details));
|
||||
});
|
||||
}
|
||||
|
||||
public event Action<double, double> font_size_changed;
|
||||
|
||||
public string name { get; }
|
||||
public Typeface typeface { get => _typeface.Value; set => _typeface.Value = value; }
|
||||
public FontFamily font_family => _font_family.Value;
|
||||
@@ -350,14 +313,17 @@ namespace mbts.spark {
|
||||
public double font_size { get => _font_size.Value; set => _font_size.Value = value; }
|
||||
public uint width { get; set; }
|
||||
public bool show { get => _show.Value; set => _show.Value = value; }
|
||||
public bool can_toggle_show { get; }
|
||||
public bool show_details { get; private set; }
|
||||
public Color foreground { get => _foreground.Value; set => _foreground.Value = value; }
|
||||
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";
|
||||
string ITemplateSelectorKey.key => _can_toggle_visibility ? ui.spark.LadderSettingsView.ColumnVisibilityToggleTemplateKey : null;
|
||||
|
||||
public ICommand toggle_show_details_cmd { get; }
|
||||
|
||||
string ITemplateSelectorKey.key => _template_key;
|
||||
|
||||
public void assign_from(LadderColumnSettings settings) {
|
||||
typeface = new Typeface(
|
||||
@@ -395,7 +361,7 @@ namespace mbts.spark {
|
||||
public Brush badge_border { get; } = new SolidColorBrush(BTSColors.MarketData.ask);
|
||||
public Brush badge_background { get; } = Brushes.White;
|
||||
|
||||
string ITemplateSelectorKey.key => ui.spark.LadderSettingsView.OrdersColumnSampleTemplateKey;
|
||||
string ITemplateSelectorKey.key => ui.spark.LadderSettingsView.OrdersColumnTemplateKey;
|
||||
}
|
||||
|
||||
public class LadderMarketBreakLineVM : ViewModelBase {
|
||||
@@ -463,7 +429,7 @@ namespace mbts.spark {
|
||||
}
|
||||
}
|
||||
|
||||
public LadderColumnSettingsVM general { get; } = new LadderColumnSettingsVM("Volume", true);
|
||||
public LadderColumnSettingsVM general { get; } = new LadderColumnSettingsVM("Volume", LadderSettingsView.VolumeColumnTemplateKey, true);
|
||||
public VolumeAtPriceAnimationVM buy { get; }
|
||||
public VolumeAtPriceAnimationVM sell { get; }
|
||||
public VolumeAtPriceAnimationVM passive { get; }
|
||||
|
||||
@@ -337,6 +337,7 @@ namespace mbts.spark {
|
||||
if (e.ChangedButton == MouseButton.Middle) {
|
||||
if (bs.HasValue) {
|
||||
_send_parameterized_order(point, bs.Value);
|
||||
return;
|
||||
} else if (column.column_type == ColumnType.Price) {
|
||||
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown (Key.RightShift) ||
|
||||
Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) {
|
||||
@@ -344,6 +345,7 @@ namespace mbts.spark {
|
||||
} else {
|
||||
_scroll_to_center();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
var pbs = (price, bs, e.ChangedButton);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -10,10 +10,15 @@ namespace mbts.ui.spark {
|
||||
/// </summary>
|
||||
partial class LadderSettingsView : UserControl {
|
||||
public const string
|
||||
OrdersColumnSampleTemplateKey = nameof(OrdersColumnSampleTemplateKey),
|
||||
ColumnVisibilityToggleTemplateKey = nameof(ColumnVisibilityToggleTemplateKey),
|
||||
OrdersColumnTemplateKey = nameof(OrdersColumnTemplateKey),
|
||||
VolumeColumnTemplateKey = nameof(VolumeColumnTemplateKey),
|
||||
LastColumnTemplateKey = nameof(LastColumnTemplateKey),
|
||||
FavoriteAlgoAddButtonTemplateKey = nameof(FavoriteAlgoAddButtonTemplateKey);
|
||||
|
||||
public static readonly IMultiValueConverter LadderColumnToggleShowToolTipConverter =
|
||||
ValueConverter.create<bool, bool, string, string>((can_toggle_show, show, col_name) =>
|
||||
can_toggle_show ? $"{(show ? "Hide" : "Show")} {col_name}" : $"{col_name} cannot be {(show ? "hidden" : "shown")}.");
|
||||
|
||||
public LadderSettingsView() =>
|
||||
InitializeComponent();
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
</u:TemplateSelectorKeyValue>
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:GridSettingsTemplateKeys.Ladder}">
|
||||
<DataTemplate>
|
||||
<usp:LadderSettingsView Margin="0,4,0,0"/>
|
||||
<usp:LadderSettingsView Margin="0,-16,0,0"/>
|
||||
</DataTemplate>
|
||||
</u:TemplateSelectorKeyValue>
|
||||
<u:TemplateSelectorKeyValue Key="{x:Static usp:GridSettingsTemplateKeys.OrderBook}">
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace bts.tests
|
||||
);
|
||||
|
||||
var spreader_order1_response = await OrderUtilities.send_order(_order_service, order1_def, -0.30, 1);
|
||||
Assert.IsTrue(spreader_order1_response.success, "Failed to send spreader order");
|
||||
Assert.IsTrue(spreader_order1_response.success, "Failed to send first spreader order");
|
||||
|
||||
var (spreader_order_is_working, working_spreader_order1) = await verify_order_status(spreader_order1_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(spreader_order_is_working, "Spreader order is not Working");
|
||||
@@ -120,156 +120,6 @@ namespace bts.tests
|
||||
|
||||
}
|
||||
|
||||
[Test(Timeout = 300000)]
|
||||
public async Task spreader_lean_qty_factor() {
|
||||
await setup_spreader_tests();
|
||||
|
||||
string test_symbol = "SI";
|
||||
var test_user = (name: "bts", id: 1u);
|
||||
var test_account = (name: "ACC A", id: 1u);
|
||||
|
||||
(SecurityImmutable s1, SecurityImmutable s2) test_securities = await SpreaderUtilities.get_securities_for_common_setup_one(_md_relay, test_symbol);
|
||||
|
||||
var setup_product = await SpreaderUtilities.common_setup_one(_order_service, _synthetic_service, test_securities, test_account, test_user);
|
||||
|
||||
var order1_def = OrderDefinitionUtilities.create_spreader_order_def(
|
||||
setup_product.synthetic.security,
|
||||
Side.Buy,
|
||||
setup_product.synthetic.strategy,
|
||||
test_account,
|
||||
test_user
|
||||
);
|
||||
|
||||
var spreader_order1_response = await OrderUtilities.send_order(_order_service, order1_def, -0.30, 1);
|
||||
Assert.IsTrue(spreader_order1_response.success, "Failed to send spreader order");
|
||||
|
||||
var (spreader_order_is_working, working_spreader_order1) = await verify_order_status(spreader_order1_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(spreader_order_is_working, "Spreader order is not Working");
|
||||
|
||||
var (quoting_order_is_working, working_quoting_order) = await verify_child_order_status(spreader_order1_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(quoting_order_is_working, "Order is not Working");
|
||||
Assert.IsTrue(working_quoting_order.Value.State.Price == 18.6, "Quoting order has the wrong price");
|
||||
|
||||
Assert.IsTrue(await OrderUtilities.cancel_orders(_order_service, new List<OrderRecordImmutable> {working_spreader_order1.Value}), "Failed to cancel order");
|
||||
|
||||
var (quoting_order_is_canceled, canceled_quoting_order) = await verify_child_order_status(spreader_order1_response.value.OrderKey, OrderStatus.Canceled);
|
||||
Assert.IsTrue(quoting_order_is_canceled, "Quoting order may not have been canceled");
|
||||
|
||||
// change the synthetic
|
||||
var hedge_leg = setup_product.synthetic.strategy.LegParamsMap.Where(m => !m.Value.IsActiveQuoting).TryFirst();
|
||||
Assert.IsTrue(hedge_leg.HasValue, "you need more legs");
|
||||
|
||||
foreach(var key in setup_product.synthetic.strategy.LegParamsMap.Keys) {
|
||||
if(!setup_product.synthetic.strategy.LegParamsMap[key].IsActiveQuoting) {
|
||||
setup_product.synthetic.strategy.LegParamsMap[key].QuoteParams.LeanQuantityRatio = 2d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(await SpreaderUtilities.save_strategy(_synthetic_service, setup_product.synthetic.security.Id, (ExecutionStrategyImmutable) setup_product.synthetic.strategy), "Failed to save strategy");
|
||||
|
||||
var order2_def = OrderDefinitionUtilities.create_spreader_order_def(
|
||||
setup_product.synthetic.security,
|
||||
Side.Buy,
|
||||
setup_product.synthetic.strategy,
|
||||
test_account,
|
||||
test_user
|
||||
);
|
||||
|
||||
var spreader_order2_response = await OrderUtilities.send_order(_order_service, order2_def, -0.30, 1);
|
||||
Assert.IsTrue(spreader_order2_response.success, "Failed to send spreader order");
|
||||
|
||||
var (spreader_order2_is_working, working_spreader_order2) = await verify_order_status(spreader_order2_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(spreader_order2_is_working, "Spreader order is not Working");
|
||||
|
||||
var (quoting_order2_is_working, working_quoting_order2) = await verify_child_order_status(spreader_order2_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(quoting_order2_is_working, "Order is not Working");
|
||||
Assert.IsTrue(working_quoting_order2.Value.State.Price == 18.61, "Quoting order has the wrong price");
|
||||
|
||||
Assert.IsTrue(await OrderUtilities.cancel_orders(_order_service, new List<OrderRecordImmutable> {working_spreader_order2.Value}), "Failed to cancel order");
|
||||
|
||||
var (quoting_order2_is_canceled, canceled_quoting_order2) = await verify_child_order_status(spreader_order2_response.value.OrderKey, OrderStatus.Canceled);
|
||||
Assert.IsTrue(quoting_order2_is_canceled, "Quoting order may not have been canceled");
|
||||
|
||||
|
||||
// Order 3
|
||||
foreach(var key in setup_product.synthetic.strategy.LegParamsMap.Keys) {
|
||||
if(!setup_product.synthetic.strategy.LegParamsMap[key].IsActiveQuoting) {
|
||||
setup_product.synthetic.strategy.LegParamsMap[key].QuoteParams.LeanQuantityRatio = 4d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(await SpreaderUtilities.save_strategy(_synthetic_service, setup_product.synthetic.security.Id, (ExecutionStrategyImmutable) setup_product.synthetic.strategy), "Failed to save strategy");
|
||||
|
||||
var order3_def = OrderDefinitionUtilities.create_spreader_order_def(
|
||||
setup_product.synthetic.security,
|
||||
Side.Buy,
|
||||
setup_product.synthetic.strategy,
|
||||
test_account,
|
||||
test_user
|
||||
);
|
||||
|
||||
var spreader_order3_response = await OrderUtilities.send_order(_order_service, order3_def, -0.30, 1);
|
||||
Assert.IsTrue(spreader_order3_response.success, "Failed to send spreader order");
|
||||
|
||||
var (spreader_order3_is_working, working_spreader_order3) = await verify_order_status(spreader_order3_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(spreader_order3_is_working, "Spreader order is not Working");
|
||||
|
||||
var (quoting_order3_is_working, working_quoting_order3) = await verify_child_order_status(spreader_order3_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(quoting_order3_is_working, "Order is not Working");
|
||||
Assert.IsTrue(working_quoting_order3.Value.State.Price == 18.63, "Quoting order has the wrong price");
|
||||
|
||||
Assert.IsTrue(await OrderUtilities.cancel_orders(_order_service, new List<OrderRecordImmutable> {working_spreader_order3.Value}), "Failed to cancel order");
|
||||
|
||||
var (quoting_order3_is_canceled, canceled_quoting_order3) = await verify_child_order_status(spreader_order3_response.value.OrderKey, OrderStatus.Canceled);
|
||||
Assert.IsTrue(quoting_order3_is_canceled, "Quoting order may not have been canceled");
|
||||
|
||||
// Order 4
|
||||
foreach(var key in setup_product.synthetic.strategy.LegParamsMap.Keys) {
|
||||
if(!setup_product.synthetic.strategy.LegParamsMap[key].IsActiveQuoting) {
|
||||
setup_product.synthetic.strategy.LegParamsMap[key].QuoteParams.LeanQuantityRatio = 5d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(await SpreaderUtilities.save_strategy(_synthetic_service, setup_product.synthetic.security.Id, (ExecutionStrategyImmutable) setup_product.synthetic.strategy), "Failed to save strategy");
|
||||
|
||||
var order4_def = OrderDefinitionUtilities.create_spreader_order_def(
|
||||
setup_product.synthetic.security,
|
||||
Side.Buy,
|
||||
setup_product.synthetic.strategy,
|
||||
test_account,
|
||||
test_user
|
||||
);
|
||||
|
||||
var spreader_order4_response = await OrderUtilities.send_order(_order_service, order4_def, -0.30, 1);
|
||||
Assert.IsTrue(spreader_order4_response.success, "Failed to send spreader order");
|
||||
|
||||
var (spreader_order4_is_working, working_spreader_order4) = await verify_order_status(spreader_order4_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(spreader_order4_is_working, "Spreader order is not Working");
|
||||
|
||||
var no_quoting_order4 = await get_order(ord => ord.ParentKey == spreader_order4_response.value.OrderKey, TimeSpan.FromSeconds(10));
|
||||
Assert.IsFalse(no_quoting_order4.HasValue, "There should not have been a quoting order here...");
|
||||
|
||||
OrderRecordImmutable order_from_book = setup_product.book.Where(ordi => ordi.State.Price == 18.30).First();
|
||||
var mod_resp = await OrderUtilities.modify_order(_order_service, order_from_book, 2);
|
||||
Assert.IsTrue(mod_resp.success, "Order size was not changed");
|
||||
|
||||
var (quoting_order4_is_working, working_quoting_order4) = await verify_child_order_status(spreader_order4_response.value.OrderKey, OrderStatus.Working);
|
||||
Assert.IsTrue(quoting_order4_is_working, "Order is not Working");
|
||||
Assert.IsTrue(working_quoting_order4.Value.State.Price == 18.63, "Quoting order has the wrong price");
|
||||
|
||||
var mod_resp2 = await OrderUtilities.modify_order(_order_service, order_from_book, 1);
|
||||
Assert.IsTrue(mod_resp.success, "Order size was not changed");
|
||||
|
||||
var (quoting_order5_is_canceled, canceled_quoting_order5) = await verify_child_order_status(spreader_order4_response.value.OrderKey, OrderStatus.Canceled);
|
||||
Assert.IsTrue(quoting_order5_is_canceled, "Order was not canceld");
|
||||
|
||||
Assert.IsTrue(await OrderUtilities.cancel_orders(_order_service, new List<OrderRecordImmutable> {working_spreader_order4.Value}), "Failed to cancel order");
|
||||
|
||||
}
|
||||
|
||||
private async Task<(Boolean, OrderRecordImmutable?)> verify_child_order_status(uint parent_order_key, OrderStatus expected_status) {
|
||||
var order = await get_order(ordi => ordi.ParentKey == parent_order_key && ordi.State.Status == expected_status);
|
||||
return (order.HasValue && order.Value.State.Status == expected_status, order);
|
||||
@@ -290,22 +140,13 @@ namespace bts.tests
|
||||
return order.HasValue && (Math.Truncate(order.Value.State.Price * precision) == precision * expected_price);
|
||||
}
|
||||
|
||||
private async Task<OrderRecordImmutable?> get_order(Func<OrderRecordImmutable, bool> predicate, TimeSpan? timeout = null)
|
||||
private async Task<OrderRecordImmutable?> get_order(Func<OrderRecordImmutable, bool> predicate)
|
||||
{
|
||||
if(timeout == null){
|
||||
timeout = TimeSpan.FromSeconds(60);
|
||||
}
|
||||
var orders = await OrderUtilities.wait_for_filtered_orders(_order_service,
|
||||
o => o.Where(predicate).Count() > 0,
|
||||
ord => ord.Where(predicate),
|
||||
timeout);
|
||||
if(orders.result != null) {
|
||||
return orders.result.TryFirst();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
TimeSpan.FromSeconds(30));
|
||||
return orders.result.TryFirst();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,10 +75,6 @@ namespace bts.tests
|
||||
});
|
||||
var tr = await tcs.Task.timeout(timeout.Value);
|
||||
sub.Dispose();
|
||||
if(tr.result == null) {
|
||||
Console.WriteLine("Timed out while retreving orders.");
|
||||
}
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
@@ -128,7 +124,7 @@ namespace bts.tests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We should replace with something like the OrderDataHelper from the Spark project.
|
||||
/// We should replace with something like the OrderDataHeler from the Spark project.
|
||||
/// </summary>
|
||||
public static class OrderDefinitionUtilities {
|
||||
|
||||
|
||||
@@ -27,24 +27,8 @@ namespace bts.tests
|
||||
Assert.IsNotNull(exps);
|
||||
Assert.IsTrue(exps.Count() > 2, "Need at least two expirations");
|
||||
|
||||
(uint, uint) preferred_expirations = (07,12);
|
||||
|
||||
FutureContract exp1;
|
||||
FutureContract exp2;
|
||||
|
||||
if(exps.Exists(expir => expir.ExpirationMonth == preferred_expirations.Item1)) {
|
||||
exp1 = exps.Where(ex => ex.ExpirationMonth == preferred_expirations.Item1).First();
|
||||
}
|
||||
else {
|
||||
exp1 = exps[0];
|
||||
}
|
||||
|
||||
if(exps.Exists(expir => expir.ExpirationMonth == preferred_expirations.Item2 && expir.ExpirationYear == exp1.ExpirationYear)) {
|
||||
exp2 = exps.Where(ex => ex.ExpirationMonth == preferred_expirations.Item2 && ex.ExpirationYear == exp1.ExpirationYear).First();
|
||||
}
|
||||
else {
|
||||
exp2 = exps[1];
|
||||
}
|
||||
var exp1 = exps[0];
|
||||
var exp2 = exps[1];
|
||||
|
||||
Func<ChannelUpdate, bool> sec1_match = ch =>
|
||||
ch.Security.Future.ExpirationYear == exp1.ExpirationYear &&
|
||||
@@ -119,15 +103,13 @@ namespace bts.tests
|
||||
new_synth.Synthetic.Legs.Add(legs.Select(l => l.contract));
|
||||
new_synth.Symbol = deduce_symbol(new_synth);
|
||||
new_synth.ContractSpec = new ContractSpecification();
|
||||
new_synth.ContractSpec.LowTick = 0.005;
|
||||
new_synth.ContractSpec.HighTick = 0.005;
|
||||
new_synth.ContractSpec.LowTick = 0.01;
|
||||
new_synth.ContractSpec.HighTick = 0.01;
|
||||
new_synth.ContractSpec.PriceBreak = double.MaxValue;
|
||||
new_synth.ContractSpec.MainFraction = 1u;
|
||||
new_synth.ContractSpec.PriceDisplayFormat = 3u;
|
||||
new_synth.ContractSpec.CashPerPoint = 1u;
|
||||
new_synth.ContractSpec.PriceMultiplier = 1u;
|
||||
new_synth.Exchange = Venue.Bts;
|
||||
new_synth.Synthetic.Name = "TEST_SYNTH" + DateTime.Now.TimeOfDay.ToString();
|
||||
new_synth.Synthetic.Name = "TEST_SYNTH" + DateTime.Now.ToString();
|
||||
|
||||
var result = await service.create_synthetic_async(new_synth);
|
||||
Assert.IsTrue(result.success, "Unable to create synthetic");
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -d ~/sparkdata/ ]
|
||||
then
|
||||
sparkdata_dir=~/sparkdata
|
||||
else
|
||||
sparkdata_dir=/sparkdata
|
||||
fi
|
||||
|
||||
./delete_dbs.sh
|
||||
|
||||
config=$sparkdata_dir/conf/spark.conf
|
||||
bak=$sparkdata_dir/conf/spark.conf.bak
|
||||
|
||||
[ ! -d "$sparkdata_dir/conf" ] && mkdir $sparkdata_dir/conf/
|
||||
[ -f "$conf" ] && mv $conf $bak
|
||||
|
||||
cat << EOF > $sparkdata_dir/conf/spark.conf
|
||||
ExchangeSim {
|
||||
value {
|
||||
merge_with_md:false
|
||||
}
|
||||
}
|
||||
Logging {
|
||||
value {
|
||||
console { default_level: "Error" }
|
||||
syslog { default_level: "Error" }
|
||||
file { default_level: "Error" }
|
||||
}
|
||||
}
|
||||
|
||||
SymbolList {
|
||||
value {
|
||||
symbol: "SI"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
|
||||
nohup ./sparkd run &
|
||||
pid=$!
|
||||
echo "sparkd pid: " $pid
|
||||
|
||||
dotnet sparkcmd.dll test spreader_lean_qty_factor
|
||||
|
||||
exit_status=$?
|
||||
|
||||
pkill -P $$
|
||||
|
||||
[ -f "$bak" ] && mv $bak $conf
|
||||
[ $exit_status -eq 0 ] && echo "Test Finished" || echo "Test failed"
|
||||
exit $exit_status
|
||||
@@ -58,7 +58,7 @@ namespace interop {
|
||||
// Functions used for interop to other languages
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
std::string getbuffer_serialized_buffer;
|
||||
extern "C" BTS_EXPORT const char* get_buffer(const char* key_name, int& out_length)
|
||||
{
|
||||
@@ -69,7 +69,7 @@ extern "C" BTS_EXPORT const char* get_buffer(const char* key_name, int& out_leng
|
||||
|
||||
getbuffer_serialized_buffer.clear();
|
||||
message.SerializePartialToString(&getbuffer_serialized_buffer);
|
||||
|
||||
|
||||
out_length = getbuffer_serialized_buffer.size();
|
||||
return getbuffer_serialized_buffer.c_str();
|
||||
}
|
||||
@@ -89,14 +89,10 @@ namespace internal {
|
||||
// Functions used in testing
|
||||
//////////////////////////////////////////
|
||||
|
||||
extern "C" BTS_EXPORT void initialize_for_test(const char* values, bool print_errors)
|
||||
extern "C" BTS_EXPORT void initialize_for_test(const std::string& values, bool print_errors)
|
||||
{
|
||||
config_state.reset();
|
||||
config_state.load_string(std::string(values), print_errors);
|
||||
}
|
||||
void initialize_for_test(const std::string& value, bool print_errors) {
|
||||
config_state.reset();
|
||||
config_state.load_string(value, print_errors);
|
||||
config_state.load_string(values, print_errors);
|
||||
}
|
||||
|
||||
extern "C" BTS_EXPORT void reset()
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace config {
|
||||
// Load and initialize config.
|
||||
// This will be automatically called on the first key request but since loading config
|
||||
// can result in network requests this function is available for applications that want to
|
||||
// manage pauses in execution.
|
||||
// manage pauses in execution.
|
||||
// Ex: initialize() on a background thread so that config::get<T>() is fast on a UI thread
|
||||
extern "C" BTS_EXPORT void initialize();
|
||||
|
||||
@@ -56,8 +56,7 @@ namespace config {
|
||||
{
|
||||
// Initialize configuration state from a string instead of from the filesystem. defaults.conf will still be loaded.
|
||||
// Calling this a second time clears all values from previous initialization
|
||||
extern "C" BTS_EXPORT void initialize_for_test(const char* configuration, bool print_errors=true);
|
||||
void initialize_for_test(const std::string& configuration, bool print_errors=true);
|
||||
extern "C" BTS_EXPORT void initialize_for_test(const std::string& configuration, bool print_errors=true);
|
||||
|
||||
// Releases all resources and returns config to factory defaults
|
||||
extern "C" BTS_EXPORT void reset();
|
||||
|
||||
@@ -2,9 +2,9 @@ This test verifies that fractional hedges work. That is, when a fill in a quotin
|
||||
not equate to one full contract in the hedging leg, we accumulate the unhedged
|
||||
delta, until we can send one or more hedging contracts.
|
||||
|
||||
1. Create a new synthetic spread. For the legs choose SIF9 and SIH9 (if you don't
|
||||
1. Create a new synthetic spread. For the legs choose SIF9 and SIH9 (if you dont
|
||||
have these contracts, other contracts can be chosen too) and for the quantities choose 1 and -1.5
|
||||
respectively (and same for the Price multiplier).
|
||||
respectively (and same for the Price multiplier).
|
||||
Enable quoting for SIH9, and disable all quoting, pre-hedge and post-hedge parameters.
|
||||
Name the synthetic SIF9-1.5SIH9.
|
||||
|
||||
@@ -18,13 +18,13 @@ to enter orders). The quoting order will lean against this.
|
||||
4. Verify that a 15 lot quoting order is sent for SIH9 at 19.935.
|
||||
|
||||
5. Now we will start filling the quoting order and verify the hedges. A 1 lot fill in the
|
||||
quoting leg means a 2/3 contract in the hedging leg. Since we can not send 2/3 contracts,
|
||||
quoting leg means a 2/3 contract in the hedging leg. Since we can not send 2/3 constracts,
|
||||
we will add it to the "unhedged delta" till the unhedged delta > 1, and then we will send one
|
||||
or more orders to get the unhedged delta < 1.
|
||||
|
||||
Force quoting fills based on the left most column (by lifting the quoting offer)
|
||||
and verify that hedge orders of size "hedge_order_sent" (3rd column) are sent as a result.
|
||||
The "unhedged_delta" column shows the unhedged delta immediately after the quoting fill
|
||||
The "unhedged_delta" column shows the unhedged delta immediatly after the quoting fill
|
||||
and the "remaining_unhedged_delta" column is the unhedged delta after the hedge order
|
||||
has been sent.
|
||||
You can check the hedge orders being sent easily by looking at the order reduction
|
||||
|
||||
@@ -10,7 +10,7 @@ ExchangeSim {
|
||||
}
|
||||
}
|
||||
|
||||
1. Open up the mview and find a security with with both direct and implied markets, on the ask side.
|
||||
1. Open up the mview and find a security with with both direct and implied markets, on the ask side.
|
||||
Make sure those markets are stable and you can rely on them.
|
||||
|
||||
2. Create a synthetic with the security in 1. being the hedge leg (buy) and choose a quoting
|
||||
@@ -19,11 +19,11 @@ use "Merged"
|
||||
|
||||
3. Disable all spreader params.
|
||||
|
||||
4. Now enter a spreader order so that it doesn't get filled immediately. Note the quoting order.
|
||||
4. Now enter a spreader order so that it doesnt get filled immediatly. Note the quoting order.
|
||||
quoting leg price = hedge_price - spreader_price.
|
||||
hedge_price is where it can buy the hedge order (which since its a buy will be on the ask side of
|
||||
the hedging market).
|
||||
Make sure that its correct and its using the merged book in the hedging market.
|
||||
Make sure thta its correct and its using the merged book in the hedging market.
|
||||
|
||||
5. Cancel the order and change the spreader so its using Direct prices.
|
||||
Repeat 4, and make sure its using direct price on the hedging market.
|
||||
@@ -5,8 +5,8 @@ with 1 quoting leg.
|
||||
|
||||
1. Create a 4 legged synthetic spread with legs SIF9, SIH9, SIK9, SIN9, with leg quantities and
|
||||
price multipliers of 1,1,1 and -1 respectively. Leg SIN9 is the only quoting leg.
|
||||
Disable all Quote, Pre-Hedge and Post-Hedge parameters (leg contracts are arbitrary, so if you
|
||||
don't have those contracts, choose any set of 4 contracts).
|
||||
Disable all Quote, Pre-Hedge and Post-Hedge paramaters (leg contracts are arbitrary, so if you
|
||||
dont have those contracts, choose any set of 4 contracts).
|
||||
|
||||
2. Enter a synthetic buy order of 10 lots at 40.00. The ladders will most likely be empty, so
|
||||
use the market view to enter orders.
|
||||
@@ -16,7 +16,7 @@ Now enter:
|
||||
SIH9 10 lot sell @ 19.950
|
||||
SIK9 10 lot sell @ 20.200
|
||||
|
||||
There should still be no quoting order for SIN9 since the SIF9 hedge leg still doesn't have a market.
|
||||
There should still be no quoting order for SIN9 since the SIF9 hedge leg still doesnt have a market.
|
||||
Now Enter:
|
||||
SIF9 1 lot sell @ 20.000
|
||||
|
||||
|
||||
Reference in new issue
Block a user