Compare commits
171
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0fdf74a73 | ||
|
|
ebedd17129 | ||
|
|
0f8ed5dacb | ||
|
|
8553229119 | ||
|
|
4903c12533 | ||
|
|
88f23530c6 | ||
|
|
14f4452599 | ||
|
|
22002c43cf | ||
|
|
7551e23877 | ||
|
|
22c92c3e17 | ||
|
|
613823e7c5 | ||
|
|
f0a9a2415d | ||
|
|
708e63e36e | ||
|
|
76ff36e514 | ||
|
|
740c14c261 | ||
|
|
53990297e8 | ||
|
|
9570be8bfa | ||
|
|
0b15570d00 | ||
|
|
33a71f5f47 | ||
|
|
2940601f4b | ||
|
|
1f7939f49a | ||
|
|
48e273a607 | ||
|
|
e35634020d | ||
|
|
1ede053f59 | ||
|
|
93824786c7 | ||
|
|
25b8c0a728 | ||
|
|
0e4fe1b47e | ||
|
|
cd744963ff | ||
|
|
fb9d877938 | ||
|
|
087b001510 | ||
|
|
02a7c0ddca | ||
|
|
6afa1d581d | ||
|
|
5e04abe573 | ||
|
|
3422a44c87 | ||
|
|
d5fdcc6796 | ||
|
|
f12217d76c | ||
|
|
d85a914046 | ||
|
|
4fdc890cb5 | ||
|
|
3ed0bd9052 | ||
|
|
dbdc915ff2 | ||
|
|
10476c6662 | ||
|
|
5fbc1397b5 | ||
|
|
05e09cb977 | ||
|
|
38bd2ef321 | ||
|
|
dab4f5acbb | ||
|
|
a20c53b23d | ||
|
|
8206f457b5 | ||
|
|
d4e6d6293d | ||
|
|
d1a2197ba4 | ||
|
|
369fb55e90 | ||
|
|
37dacff74d | ||
|
|
d297b703c7 | ||
|
|
59c00203a9 | ||
|
|
33f533af9b | ||
|
|
085d6402f4 | ||
|
|
f4395ef833 | ||
|
|
e7a9412124 | ||
|
|
da4ee38abd | ||
|
|
706e5bc73b | ||
|
|
af39fac368 | ||
|
|
4f997505f3 | ||
|
|
fcbb66361d | ||
|
|
fa16321691 | ||
|
|
ceb3542e15 | ||
|
|
32e55d07ee | ||
|
|
e079acf91f | ||
|
|
fecedb203a | ||
|
|
6ac05a47e1 | ||
|
|
36ed80dc87 | ||
|
|
6cf395199e | ||
|
|
1707c3268f | ||
|
|
f5bbc4245f | ||
|
|
96fc54ff0f | ||
|
|
25b69e4f7d | ||
|
|
d871ffca6d | ||
|
|
213567bcdb | ||
|
|
b7e5c3a374 | ||
|
|
ae4a8bbf46 | ||
|
|
850386c1d4 | ||
|
|
2d4e94a636 | ||
|
|
9b73ad2486 | ||
|
|
e447096733 | ||
|
|
cd68b95a3e | ||
|
|
205eebb7da | ||
|
|
ef4dc1fc65 | ||
|
|
ebad5b389f | ||
|
|
160c49514a | ||
|
|
4f7fcc5634 | ||
|
|
500e5b5fe4 | ||
|
|
9d924fa4ba | ||
|
|
092e63e300 | ||
|
|
4654cd4739 | ||
|
|
f2f06d76de | ||
|
|
6bc293a58e | ||
|
|
fdb462eda4 | ||
|
|
cc579034b2 | ||
|
|
4b76e91757 | ||
|
|
8d34279ab0 | ||
|
|
7973b927a2 | ||
|
|
6997aed9bd | ||
|
|
b5d7d98098 | ||
|
|
8f22a326ab | ||
|
|
b2c3488c5e | ||
|
|
23f10eac70 | ||
|
|
cb31269a70 | ||
|
|
5b75b8718a | ||
|
|
5a256d4634 | ||
|
|
db2de3e8c4 | ||
|
|
b9dcad9549 | ||
|
|
ce104479e1 | ||
|
|
2d56eedf3e | ||
|
|
049381908c | ||
|
|
0067e52155 | ||
|
|
8ed6d297a9 | ||
|
|
474401cddc | ||
|
|
a06df5099a | ||
|
|
8ddd5bdbb5 | ||
|
|
5cbe00beab | ||
|
|
3ebcf9a270 | ||
|
|
40cd826260 | ||
|
|
8bd63f11b1 | ||
|
|
2198a8d6ed | ||
|
|
8023dc7aa8 | ||
|
|
7ce8f1ed97 | ||
|
|
5d53492ab5 | ||
|
|
90fd5bd382 | ||
|
|
beff060a4f | ||
|
|
bc3418734f | ||
|
|
ab255c8777 | ||
|
|
cc2bde1064 | ||
|
|
94d66a8e99 | ||
|
|
92f08b89cc | ||
|
|
02769479ea | ||
|
|
4ada322c56 | ||
|
|
7de7f6e496 | ||
|
|
0700e9e80d | ||
|
|
ccca85d1c5 | ||
|
|
60e933fca9 | ||
|
|
e2aa6034ab | ||
|
|
8b4c64e8f4 | ||
|
|
29d14404e6 | ||
|
|
60b7966f2f | ||
|
|
1059351334 | ||
|
|
179ee000a3 | ||
|
|
43129427f6 | ||
|
|
7afe1e103d | ||
|
|
7c6a26ebd9 | ||
|
|
b2766c283d | ||
|
|
6b5b4ea2b1 | ||
|
|
6f7c6db918 | ||
|
|
a1ead3d1c1 | ||
|
|
1fc35ee3cd | ||
|
|
a014dcc984 | ||
|
|
6e07a0c109 | ||
|
|
e936b80dc6 | ||
|
|
60ab67213f | ||
|
|
4055a06601 | ||
|
|
e47af3d7bd | ||
|
|
c3c427742d | ||
|
|
83c432d8e2 | ||
|
|
6a4930767b | ||
|
|
685e69adcc | ||
|
|
e9f61dc3c0 | ||
|
|
f329470165 | ||
|
|
d7d319f1e3 | ||
|
|
cddadcb278 | ||
|
|
419833d398 | ||
|
|
9883553fb5 | ||
|
|
be5d037ca9 | ||
|
|
21f5fd919d | ||
|
|
159e8fba23 |
No files matched your search
@@ -32,3 +32,4 @@ trace_data/
|
||||
/src/lib/orderman/perf_analysis/last_pretty_log.csv
|
||||
/src/lib/orderman/perf_analysis/log.csv
|
||||
/doc/user/public/*
|
||||
.ionide/*
|
||||
Vendored
+1
-1
@@ -104,7 +104,7 @@
|
||||
"request": "launch",
|
||||
"program": "C:/Program Files/Microsoft Office/Office14/EXCEL.EXE",
|
||||
"args": [
|
||||
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64-packed.xll"
|
||||
"${workspaceRoot}/build/Debug/bin/net471/spark_excel-addin64.xll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
|
||||
@@ -4,11 +4,31 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
|
||||
## 2.6.1
|
||||
### User Visible Changes
|
||||
- Fixed an issue in the ladder where an error could occur when trying to change ladder settings for the first time
|
||||
|
||||
## 2.6.0
|
||||
### User Visible Changes
|
||||
- In the ladder made last trade more distinctive when overlayed on the volumn column
|
||||
- In the ladder, corrected display of volume tool tip
|
||||
- In the ladder, changed the consolidation of last trade to consider only the price ignoring aggressor changes
|
||||
- Added an option for the Top Algo to delete itself if within N ticks from top of book
|
||||
- Address a rare issue that could lead to an error report on UI exit
|
||||
- Added margin risk limits
|
||||
- Added the "Offset" algo. Offset prices a child algo at a parameterizable distance from the top of the book
|
||||
- Avoided switching the current account and order type on open ladders when limits are changed
|
||||
### Internal Changes
|
||||
- Ensure that admin logins are always allowed to login, regardless of keycode status.
|
||||
|
||||
## 2.5.0
|
||||
### User Visible Settings
|
||||
### User Visible Changes
|
||||
- Added loss limit risk check
|
||||
- Made algos, synthetics and user names private so that multiple users can share a single system privately.
|
||||
- Allowed the mini order book defaults for column widths to be changed from the mini order book
|
||||
- Allowed the mini order book to shrink to the minimum size that will be needed to display the selected columns
|
||||
- Improved accuracy of the directional arrow when displaying last trades in the volumn column
|
||||
## 2.4.0
|
||||
### User Visible Settings
|
||||
- In the ladder, changed the last trade column to accumulate successive trades at the same price
|
||||
@@ -396,7 +416,8 @@ insert_anchor_links = "right"
|
||||
- This is configurable from a ladder's settings as well
|
||||
### Customized settings in all windows
|
||||
- All windows now contain a settings page where grid settings (font color, size, etc) are applied
|
||||
- These settings can be saved on a per-window basis or as a Default for newly-launched windows␍
|
||||
- These settings can be saved on a per-window basis or as a Default for newly-launched windows
|
||||
|
||||
|
||||
## 1.3.1 Through 1.3.4
|
||||
|
||||
|
||||
@@ -339,6 +339,7 @@ namespace mbts.spark {
|
||||
zoom_slider_visible = false,
|
||||
zoom_factor = _default_zoom_factor,
|
||||
net_change = NetChangeTerms.None,
|
||||
mini_ob = new LadderMiniOrderBookSettings(bts.shell.orderbook.OrderBookStartupData.default_mini_grid.general),
|
||||
};
|
||||
rv.row_height = LadderRender.measure_row_height(rv);
|
||||
return rv;
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace bts.shell {
|
||||
});
|
||||
});
|
||||
// vm
|
||||
vm = LadderVM.create(_bus, model, last_value ?? _settings_hub.current.ladder, settings_source);
|
||||
vm = LadderVM.create(_bus, model, LadderSettings.sanitize(last_value ?? _settings_hub.current.ladder), settings_source);
|
||||
vm.settings_invalidated += on_settings_invalidated;
|
||||
vm.order_properties_vm.open_algo_popup_on_select = _ladder_conf.open_algo_popup_on_select;
|
||||
vm.order_properties_vm.use_algo_param_grid = _ladder_conf.use_algo_param_grid;
|
||||
|
||||
@@ -363,7 +363,9 @@ namespace mbts.spark {
|
||||
} else {
|
||||
clone = SparkUtils.clone_proto(_current);
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
clone.mini_ob = new LadderMiniOrderBookSettings(mini_ob.create().with(sorting_criterion: _current.mini_ob.value.sorting_criterion));
|
||||
clone.mini_ob = new LadderMiniOrderBookSettings(_current.mini_ob.value != null ?
|
||||
mini_ob.create().with(sorting_criterion: _current.mini_ob.value.sorting_criterion) :
|
||||
mini_ob.create());
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
}
|
||||
clone.badge_style = badge_style;
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace mbts.spark {
|
||||
LadderVM.create_readonly(_bus,
|
||||
new LadderModel(security_md.Value, _model, _model.mbo_service,
|
||||
new Func<OrderSecurity, bool>(osec => _selected_order_tree.Contains(osec.order.Key))),
|
||||
Observable.Return(LadderSettings.Default)) : null);
|
||||
Observable.Return(LadderSettings.sanitize(LadderSettings.Default))) : null);
|
||||
RaisePropertyChanged(nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@ namespace mbts.spark {
|
||||
NativePriceSize _last_trade;
|
||||
Func<NativePriceSize, string> _fmt_size = MDDisplayHelper.get_formatter_nps(MarketSizeDisplay.get_default());
|
||||
int _old_tooltip_row = int.MaxValue;
|
||||
double _old_last_trade_price = 0.0;
|
||||
NativePriceSize? _old_last_trade;
|
||||
string _direction_arrow = string.Empty;
|
||||
bool _tooltips_are_hooked_up = false;
|
||||
ToolTip tooltip_view = new ToolTip();
|
||||
IDisposable _parameterized_order_popup;
|
||||
@@ -693,12 +694,14 @@ namespace mbts.spark {
|
||||
// At any rate, we need this part hiding the tooltip so that we don't leave random tooltips around
|
||||
// and we have to reset to old row so that when we go back to what could be our preivous row, the tooltip gets drawn again
|
||||
//System.Diagnostics.Debug.WriteLine(" Tooltip - Volume is 0");
|
||||
_old_tooltip_row = int.MaxValue;
|
||||
_hide_tool_tip();
|
||||
//_old_tooltip_row = Int32.MaxValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//System.Diagnostics.Debug.WriteLine(" Tooltip - not over Volume column");
|
||||
_old_tooltip_row = int.MaxValue;
|
||||
_hide_tool_tip();
|
||||
}
|
||||
}
|
||||
@@ -861,21 +864,43 @@ namespace mbts.spark {
|
||||
void _refresh_volume_at_price() {
|
||||
if (!_settings.volume_column.general.show)
|
||||
return;
|
||||
|
||||
// if the last trade has a new size then we need to explicitly
|
||||
// mark the volume container as dirty because there is a chance
|
||||
// we have arrived here off a size change and not a volume change
|
||||
// or a random change, like applying new settings
|
||||
bool last_trade_size_changed = _old_last_trade.HasValue ? _last_trade.size != _old_last_trade.Value.size : true;
|
||||
if (last_trade_size_changed && _settings.volume_column.show_ltq) {
|
||||
_volume_container.mark_dirty();
|
||||
}
|
||||
var vapls = _volume_container.read_new_value();
|
||||
if (vapls != null) {
|
||||
Log.assert_d(_price_list != null);
|
||||
var ltp = _last_trade.price;
|
||||
var last_index = LadderRender.index_of(_price_list, ltp, _price_comparer);
|
||||
string direction_arrow = string.Empty;
|
||||
if (MathUtils.epsilon_equals(ltp, _old_last_trade_price)) {
|
||||
// do nothing if in here b/c direction_arrow already string.empty
|
||||
} else if (ltp > _old_last_trade_price) {
|
||||
direction_arrow = "▲";
|
||||
} else {
|
||||
direction_arrow = "▼";
|
||||
}
|
||||
LadderRender.render_volumes(_volume_vis_tup.visual, _volume_vis_tup.glyph_context, _price_list, vapls, _settings.row_height, security.ContractSpec, _settings.volume_column, last_index, _last_trade, direction_arrow);
|
||||
_old_last_trade_price = ltp;
|
||||
var last_index = LadderRender.index_of(_price_list, _last_trade.price, _price_comparer);
|
||||
LadderRender.render_volumes(_volume_vis_tup.visual, _volume_vis_tup.glyph_context, _price_list, vapls, _settings.row_height, security.ContractSpec, _settings.volume_column, last_index, _get_last_arrow_string());
|
||||
_old_last_trade = _last_trade;
|
||||
}
|
||||
|
||||
string _get_last_arrow_string() {
|
||||
// if _old_last_trade doesn't have a value, this is the first time the ladder has been launched
|
||||
// and so the direction should be drawn without an arrow since there is no basis for comparison
|
||||
if (!_old_last_trade.HasValue) {
|
||||
_direction_arrow = string.Empty;
|
||||
// if the Lasts are different then we need to reformat the direction arrow in some fashion
|
||||
} else if (_old_last_trade != _last_trade) {
|
||||
var ltp = _last_trade.price;
|
||||
if (MathUtils.epsilon_equals(ltp, _old_last_trade.Value.price)) {
|
||||
// update on the same price, so size must be different
|
||||
_direction_arrow = string.Empty;
|
||||
} else if (ltp > _old_last_trade.Value.price) {
|
||||
_direction_arrow = "▲";
|
||||
} else {
|
||||
_direction_arrow = "▼";
|
||||
}
|
||||
}
|
||||
// regardless of if we've arrived here via a new Last (and new direction arrow)
|
||||
// or we're reusing the old one, this format bit is the same
|
||||
return $"{_direction_arrow} {_last_trade.size} ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -196,8 +196,7 @@ namespace mbts.spark {
|
||||
ContractSpecificationImmutable spec,
|
||||
VolumeColumnSettings settings,
|
||||
int last_index,
|
||||
NativePriceSize last_trade,
|
||||
string direction_arrow
|
||||
string last_size_and_direction
|
||||
) {
|
||||
if (!data.Any()) {
|
||||
_clear_visual(dv);
|
||||
@@ -216,16 +215,16 @@ namespace mbts.spark {
|
||||
Brush total_brush = null;
|
||||
if (settings.total.bars.result.HasValue) {
|
||||
total_brush = new SolidColorBrush(settings.total.bars.result.Value);
|
||||
} else if (components.Count == 0) {
|
||||
} else if (components.Count == 0 && !settings.show_ltq) {
|
||||
_clear_visual(dv);
|
||||
return;
|
||||
}
|
||||
// inflate the max so that the graph never fills the available space
|
||||
double max = data.Max(v => v.Volume) * 1.1;
|
||||
Brush text_brush = settings.general.foreground.rgb_brush();
|
||||
_render_cells(dv, row_height, data.Select(d => (d.Price, d)), settings.general, prices, spec, render_cell);
|
||||
/// local methods
|
||||
void render_cell(DrawingContext dc, int r, VolumeAtPriceLevelImmutable vap) {
|
||||
Brush text_brush = settings.general.foreground.rgb_brush();
|
||||
double y = row_height * r;
|
||||
var last_trade_here = r == last_index;
|
||||
|
||||
@@ -259,7 +258,8 @@ namespace mbts.spark {
|
||||
}
|
||||
if (settings.show_ltq && last_trade_here) {
|
||||
numbers_text.Clear();
|
||||
numbers_text.Add($"{direction_arrow} {last_trade.size} ");
|
||||
numbers_text.Add(last_size_and_direction);
|
||||
text_brush = BTSCoreColors.white;
|
||||
}
|
||||
if (numbers_text.Count > 0) {
|
||||
string str = string.Join("/", numbers_text);
|
||||
|
||||
@@ -994,11 +994,13 @@ namespace mbts.spark {
|
||||
var join = config.Configuration.get<bts.config.JoinAttachUIOptions>();
|
||||
var lean = config.Configuration.get<bts.config.LeanAttachUIOptions>();
|
||||
var top = config.Configuration.get<bts.config.TopAttachUIOptions>();
|
||||
var offset = config.Configuration.get<bts.config.OffsetAttachUIOptions>();
|
||||
_algo_type_conf = new Dictionary<CustomOrderDefinition.CustomOneofCase, (bool, bool)> {
|
||||
{ CustomOrderDefinition.CustomOneofCase.Cover, (cover.show_price, cover.show_quantity) },
|
||||
{ CustomOrderDefinition.CustomOneofCase.Join, (join.show_price, join.show_quantity) },
|
||||
{ CustomOrderDefinition.CustomOneofCase.Lean, (lean.show_price, lean.show_quantity) },
|
||||
{ CustomOrderDefinition.CustomOneofCase.Top, (top.show_price, top.show_quantity) }
|
||||
{ CustomOrderDefinition.CustomOneofCase.Top, (top.show_price, top.show_quantity) },
|
||||
{ CustomOrderDefinition.CustomOneofCase.Offset, (offset.show_price, offset.show_quantity) }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -602,7 +602,8 @@ namespace mbts.spark.utilities {
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset
|
||||
},
|
||||
// The following spreader types are not yet ready for production use:
|
||||
// CustomOrderDefinition.CustomOneofCase.Dynamic,
|
||||
@@ -619,7 +620,8 @@ namespace mbts.spark.utilities {
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset
|
||||
},
|
||||
outright_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Market,
|
||||
@@ -715,7 +717,7 @@ namespace mbts.spark.utilities {
|
||||
!b.HasChild);
|
||||
|
||||
public static bool top_equals(TopOrderDefinitionImmutable a, TopOrderDefinitionImmutable b) =>
|
||||
a.WatchLevelTickOffset == b.WatchLevelTickOffset &&
|
||||
a.TicksOff == b.TicksOff &&
|
||||
(a.HasTopThreshold ?
|
||||
b.HasTopThreshold && quantity_measure_equals(a.TopThreshold, b.TopThreshold) :
|
||||
!b.HasTopThreshold) &&
|
||||
@@ -906,7 +908,7 @@ namespace mbts.spark.utilities {
|
||||
add_component(_get_hash(_get_tick_child(tick)));
|
||||
break;
|
||||
case CustomOrderDefinition.CustomOneofCase.Top:
|
||||
add_component(def.Top.WatchLevelTickOffset.GetHashCode());
|
||||
add_component(def.Top.TicksOff.GetHashCode());
|
||||
add_component(def.Top.HasTopThreshold ? def.Top.TopThreshold.GetHashCode() : 0);
|
||||
add_component(_get_top_child(def.Top).GetHashCode());
|
||||
break;
|
||||
|
||||
@@ -105,13 +105,18 @@ namespace mbts.spark {
|
||||
var inline_account_src = order_properties.AggregateLatest(
|
||||
model.factory.subscribe_accounts(),
|
||||
(last, change, cache) => convert_inline_account(change.New.account, cache),
|
||||
(last, props, change) => convert_inline_account(props.account, change.New),
|
||||
(last, props, change) => convert_inline_account(props.account, change.New, false),
|
||||
() => new InlineUserProperty<AccountTuple>(),
|
||||
new InlineOrderProperties(),
|
||||
DictionaryHelper.empty<uint, string>());
|
||||
// Note that with the .Where on the account_tuples, we will ignore any
|
||||
// updates for which all accounts have been removed.
|
||||
// This was a safer change than changing SelectorProperty's IEnumberable.OnNext
|
||||
// so that it would not reset the selected item on server reboots; i.e. this
|
||||
// change only affects this IObservable.
|
||||
_accounts = backing(
|
||||
nameof(selected_account),
|
||||
model.factory.subscribe_account_tuples(),
|
||||
model.factory.subscribe_account_tuples().Where(items => items.Any()),
|
||||
inline_account_src,
|
||||
a => new AccountTuple?(a),
|
||||
() => accounts.Cast<AccountTuple?>().FirstOrDefault(),
|
||||
@@ -263,9 +268,18 @@ namespace mbts.spark {
|
||||
break;
|
||||
}
|
||||
}
|
||||
InlineUserProperty<AccountTuple> convert_inline_account(InlineUserProperty<uint> account_id, IReadOnlyDictionary<uint, string> account_map) =>
|
||||
//
|
||||
// Regarding the 3rd parameter, force_override_from_inline_property:
|
||||
// When using an InlineUserProperty, in the context of assigning a default vs selected item,
|
||||
// there is somewhat of a conflict between the IObserver<InlineUserProperty<TItem>> and the
|
||||
// IObserver<IEnumerable<TItem>> OnNext methods. This 3rd paramter provides yet another override
|
||||
// to say if the force_override should come from the inline property or if the force_override should
|
||||
// be disbled, by way of the immediate return in ViewModelBase._try_auto_select when force_override
|
||||
// is false. Our intent is then accomplished because we are no longer attempting to set a default/
|
||||
// selected item when the account_tuples potentially changes
|
||||
InlineUserProperty<AccountTuple> convert_inline_account(InlineUserProperty<uint> account_id, IReadOnlyDictionary<uint, string> account_map, bool force_override_from_inline_property = true) =>
|
||||
account_id.has_value && account_map.TryGetValue(account_id.value, out string name) ?
|
||||
new InlineUserProperty<AccountTuple>(new AccountTuple(account_id.value, name), account_id.force_override) :
|
||||
new InlineUserProperty<AccountTuple>(new AccountTuple(account_id.value, name), force_override_from_inline_property ? account_id.force_override : false) :
|
||||
new InlineUserProperty<AccountTuple>();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,16 @@ using TopDefi = bts.api.orderman.TopOrderDefinitionImmutable;
|
||||
namespace mbts.spark {
|
||||
static partial class OrderTypeVMFactory {
|
||||
class TopOrderVM : OrderTypeVMBase<TopDef, TopDefi> {
|
||||
readonly Property<uint?> _ticks_off;
|
||||
readonly Property<QuantityMeasureImmutable?> _top_threshold;
|
||||
|
||||
public TopOrderVM(AlgoKey key, string display_name, AlgoEditingPermissions editing_permissions)
|
||||
: base(key, display_name, editing_permissions) {
|
||||
_ticks_off = add_nullable_property<uint>(nameof(ticks_off), TopDef.TicksOffFieldNumber);
|
||||
_top_threshold = add_property<QuantityMeasure, QuantityMeasureImmutable>(nameof(top_threshold), TopDef.TopThresholdFieldNumber, new QuantityMeasureImmutable(new QuantityMeasure { Ratio = 1.0, Absolute = 1 }));
|
||||
}
|
||||
|
||||
public uint? ticks_off { get => _ticks_off.Value; set => _ticks_off.set(value); }
|
||||
public QuantityMeasureImmutable? top_threshold { get => _top_threshold.Value; set => _top_threshold.set(value); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,6 +820,11 @@ namespace mbts.ui.shell {
|
||||
|
||||
void _try_auto_select() {
|
||||
if (_is_user_selected_item) {
|
||||
// if we have a user-selected item and the _desired_item for which we might want to set as the selected item
|
||||
// isn't force_override, then no change
|
||||
if (!_desired_item.force_override) {
|
||||
return;
|
||||
}
|
||||
if (desired_item_valid() && _desired_item.force_override && _try_match_item(_desired_item.value, out TValue match)) {
|
||||
_set_selected_item(match, SelectionReason.SettingsMandatedSelection);
|
||||
return;
|
||||
|
||||
@@ -489,6 +489,8 @@
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush3}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
@@ -503,9 +505,11 @@
|
||||
<c:HelpButton Margin="{StaticResource Margin.LP1}" ToolTip="If the order is at the top of book, the quantity remaining below which the order will be canceled."/>
|
||||
</StackPanel>
|
||||
<uag:QuantityMeasureControl x:Name="PART_TopThreshold_QuantityMeasureControl" Grid.Column="2" QuantityMeasure="{Binding top_threshold}"/>
|
||||
<TextBlock Grid.Row="2">Ticks Off</TextBlock>
|
||||
<c:TickUpDownUInt Grid.Row="2" Grid.Column="2" Value="{Binding ticks_off}"/>
|
||||
<uag:AlgoPickerNodeView
|
||||
x:Name="PART_Child_AlgoPickerNodeView"
|
||||
Grid.Row="2"
|
||||
Grid.Row="4"
|
||||
Grid.ColumnSpan="3"
|
||||
ItemsSource="{Binding children_vm.child.algos_view}"
|
||||
SelectedItem="{Binding children_vm.child.selected_algo}"
|
||||
|
||||
@@ -253,14 +253,13 @@ namespace mbts.co2 {
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 712)]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 704)]
|
||||
internal struct ColdEntry {
|
||||
public int exchange_status;
|
||||
// Last trade
|
||||
public AtomicPriceSize last_trade;
|
||||
public ulong total_volume;
|
||||
public utils.LLDateTime last_trade_time;
|
||||
private AggressorSide last_side;
|
||||
|
||||
//// Settlement
|
||||
public UInt64 misc_timestamp;
|
||||
|
||||
@@ -3,4 +3,5 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("spark_core")]
|
||||
[assembly: InternalsVisibleTo("sparkcmd")]
|
||||
[assembly: InternalsVisibleTo("spark_test")]
|
||||
[assembly: InternalsVisibleTo("spark_excel")]
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace bts.md
|
||||
{
|
||||
if (!timeout.HasValue)
|
||||
{
|
||||
timeout = TimeSpan.FromMilliseconds(10000);
|
||||
timeout = TimeSpan.FromHours(24);
|
||||
}
|
||||
CancellationTokenSource src = new CancellationTokenSource(timeout.Value);
|
||||
SecurityImmutable? security = await Task.Run<SecurityImmutable?>(async () =>
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,297 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using bts.excel.extensions;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a reference to some Cell instance, whether it exists or not.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Static methods are provided for conversion of column names etc.
|
||||
/// </remarks>
|
||||
public sealed class CellRef : IComparable, IComparable<CellRef>
|
||||
{
|
||||
public CellRef(ExcelDna.Integration.ExcelReference rref) : this(rref.GetSheetName(),
|
||||
(rref.ColumnFirst + 1, rref.RowFirst + 1) )
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for the class: CellRef
|
||||
/// </summary>
|
||||
/// <param name="sheetName">Name of the sheet where the cell belongs.</param>
|
||||
/// <param name="coordinate">Coordinate (column, row) for the cell in the sheet.</param>
|
||||
public CellRef(string sheetName, (int col1, int row1) coordinate)
|
||||
{
|
||||
SheetName = sheetName;
|
||||
SimpleSheetName = simple_sheet_name(SheetName);
|
||||
Coordinate = coordinate;
|
||||
LocalAddress = ConvertCoordToAddress(coordinate);
|
||||
}
|
||||
|
||||
static string simple_sheet_name(string sn)
|
||||
{
|
||||
int open = sn.IndexOf('[');
|
||||
int close = sn.IndexOf(']', open + 1);
|
||||
var ssn = sn.Substring(0, open) + sn.Substring(close + 1);
|
||||
ssn = ssn.Replace("[", "").Replace("]", "_").Replace(".", "_");
|
||||
return ssn;
|
||||
}
|
||||
|
||||
public string named_range { get; set;}
|
||||
|
||||
public bool in_vector {get; set;} = false;
|
||||
public (int col, int row) vector_coordinates {get; set;}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of rows, to e compatible with Excel.
|
||||
/// </summary>
|
||||
private const int MaxRows = 1048576;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of columns, to be compatible with Excel.
|
||||
/// </summary>
|
||||
private const int MaxColumns = 16384;
|
||||
|
||||
// NOTE: Worksheet and workbook specifications and limits
|
||||
// http://office.microsoft.com/en-gb/excel-help/excel-specifications-and-limits-HP010342495.aspx
|
||||
//
|
||||
// Worksheet size: 1,048,576 rows by 16,384 columns.
|
||||
// * 1048575 is the highest number representable in 20 bits.
|
||||
// * 16383 is the highest number representable in 14 bits. That leaves 2 spare bits in a 16-bit word.
|
||||
// Column width: 255 characters (I'm not sure what the real significance of this is).
|
||||
// Total number of characters that a cell can contain: 32,767 characters
|
||||
// * 32766 is the highest number representable in 15 bits. That leaves 1 spare bit in a 16-bit word.
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Name of the sheet where the cell belongs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All cells belong to some Sheet instance, so this name must never be null or empty.
|
||||
/// </remarks>
|
||||
public string SheetName { get; }
|
||||
public string SimpleSheetName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Coordinate (column, row) for the cell in the sheet.
|
||||
/// </summary>
|
||||
public (int col1, int row1) Coordinate { get; }
|
||||
|
||||
public CellRef Offset(int columnOffset, int rowOffset)
|
||||
{
|
||||
return new CellRef(SheetName, (Column + columnOffset, Row + rowOffset));
|
||||
}
|
||||
|
||||
public int Column => Coordinate.col1;
|
||||
|
||||
public int Row => Coordinate.row1;
|
||||
|
||||
public string LocalAddress { get; internal set; }
|
||||
|
||||
#region Address conversion
|
||||
|
||||
/// <summary>
|
||||
/// Converts a column identifier, using letters, to a column number.
|
||||
/// </summary>
|
||||
/// <param name="columnName">Column identifier using letters.</param>
|
||||
/// <returns>Column number.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">When column number exceeds maximum.</exception>
|
||||
public static int ConvertToColumnNumber(string columnName)
|
||||
{
|
||||
var number = 0;
|
||||
var pow = 1;
|
||||
for (var i = columnName.Length - 1; i >= 0; i--)
|
||||
{
|
||||
number += (columnName[i] - 'A' + 1) * pow;
|
||||
pow *= 26;
|
||||
}
|
||||
|
||||
if (number > MaxColumns)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(columnName),
|
||||
"Column number from columnName exceeds maximum");
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a column number to a column identifier using letters.
|
||||
/// </summary>
|
||||
/// <param name="columnNumber">Column number.</param>
|
||||
/// <returns>Column identifier using letters.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">When column number is invalid.</exception>
|
||||
public static string ConvertToColumnName(int columnNumber)
|
||||
{
|
||||
if (columnNumber < 1 || columnNumber > MaxColumns)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(columnNumber),
|
||||
"Invalid column number");
|
||||
}
|
||||
|
||||
var dividend = columnNumber;
|
||||
var columnName = string.Empty;
|
||||
while (dividend > 0)
|
||||
{
|
||||
var modulo = (dividend - 1) % 26;
|
||||
columnName = Convert.ToChar(65 + modulo) + columnName;
|
||||
dividend = (dividend - modulo) / 26;
|
||||
}
|
||||
|
||||
return columnName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an address, like "A1", to a cell coordinate.
|
||||
/// </summary>
|
||||
/// <param name="address">Cell address, like "A1".</param>
|
||||
/// <returns>Coordinate of cell on sheet.</returns>
|
||||
/// <exception cref="ArgumentNullException">When address argument is null.</exception>
|
||||
/// <exception cref="ArgumentException">When address is invalid.</exception>
|
||||
public static (int col1, int row1) ConvertAddressToCoord(string address)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
}
|
||||
|
||||
address = address.Replace("$", string.Empty);
|
||||
address = address.ToUpper();
|
||||
|
||||
var letters = new StringBuilder();
|
||||
foreach (var c in address)
|
||||
{
|
||||
if (c < 'A' || c > 'Z')
|
||||
{
|
||||
break;
|
||||
}
|
||||
letters.Append(c);
|
||||
}
|
||||
|
||||
if (letters.Length == 0 || letters.Length > 3)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Column name requires 1 to 3 letters",
|
||||
nameof(address));
|
||||
}
|
||||
|
||||
var numbers = address.Substring(letters.Length);
|
||||
if (numbers.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Row number is required", nameof(address));
|
||||
}
|
||||
|
||||
Debug.Assert(letters.Length + numbers.Length == address.Length);
|
||||
|
||||
var row1 = int.Parse(numbers);
|
||||
var col1 = ConvertToColumnNumber(letters.ToString());
|
||||
|
||||
return (col1, row1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert cell coordinate to cell address, like "A1".
|
||||
/// </summary>
|
||||
/// <param name="coordinate">Coordinate of cell on sheet.</param>
|
||||
/// <param name="absolute">Provide absolute address, like "$A$1".</param>
|
||||
/// <returns>Cell address, like "A1", or optionally like "$A$1".</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static string ConvertCoordToAddress((int col1, int row1) coordinate, bool absolute = false)
|
||||
{
|
||||
if (coordinate.row1 < 1 || coordinate.row1 > MaxRows ||
|
||||
coordinate.col1 < 1 || coordinate.col1 > MaxColumns)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(coordinate),
|
||||
"Invalid coordinate");
|
||||
}
|
||||
|
||||
var (col1, row1) = coordinate;
|
||||
return absolute
|
||||
? $"${ConvertToColumnName(col1)}${row1}"
|
||||
: $"{ConvertToColumnName(col1)}{row1}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IComparable
|
||||
|
||||
public int CompareTo(CellRef other)
|
||||
{
|
||||
if (other == null) return -1;
|
||||
return string.Compare(ToString(), other.ToString(),
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj.GetType() != GetType()) return -1;
|
||||
return CompareTo((CellRef)obj);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HashCode & Equals
|
||||
|
||||
/// <returns>Unique key</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return to_name();
|
||||
}
|
||||
|
||||
public string to_address()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SheetName))
|
||||
return $"'{SheetName}'!{LocalAddress}";
|
||||
else
|
||||
return $"{LocalAddress}";
|
||||
}
|
||||
|
||||
public string to_name()
|
||||
{
|
||||
if(!string.IsNullOrEmpty(named_range))
|
||||
return named_range;
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SheetName))
|
||||
{
|
||||
return $"{SimpleSheetName}_{LocalAddress}".ToLowerInvariant();
|
||||
}
|
||||
else
|
||||
return $"{LocalAddress}".ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
public string r1_c1_include_sheet()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SheetName))
|
||||
return $"'{SimpleSheetName}'!R{this.Row}C{this.Column}";
|
||||
else
|
||||
return $"R{this.Row}C{this.Column}";
|
||||
}
|
||||
|
||||
public string r1_c1()
|
||||
{
|
||||
return $"R{this.Row}C{this.Column}";
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ToString().GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
|
||||
if (obj != null && !(obj is CellRef))
|
||||
throw new ArgumentException("Object must be of type CellRef.");
|
||||
|
||||
return GetHashCode().Equals(obj.GetHashCode());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public class ErrorValue : IComparable, IComparable<ErrorValue>
|
||||
{
|
||||
private enum ErrorType
|
||||
{
|
||||
Null,
|
||||
Div0,
|
||||
Value,
|
||||
Ref,
|
||||
Name,
|
||||
Num,
|
||||
NA
|
||||
}
|
||||
|
||||
public static ErrorValue Null => new ErrorValue(ErrorType.Null);
|
||||
public static ErrorValue Div0 => new ErrorValue(ErrorType.Div0);
|
||||
public static ErrorValue Value => new ErrorValue(ErrorType.Value);
|
||||
public static ErrorValue Ref => new ErrorValue(ErrorType.Ref);
|
||||
public static ErrorValue Name => new ErrorValue(ErrorType.Name);
|
||||
public static ErrorValue Num => new ErrorValue(ErrorType.Num);
|
||||
public static ErrorValue NA => new ErrorValue(ErrorType.NA);
|
||||
|
||||
private const string HashNull = "#NULL!";
|
||||
private const string HashDiv0 = "#DIV/0!";
|
||||
private const string HashValue = "#VALUE!";
|
||||
private const string HashRef = "#REF!";
|
||||
private const string HashName = "#NAME?";
|
||||
private const string HashNum = "#NUM!";
|
||||
private const string HashNA = "#N/A";
|
||||
|
||||
private readonly ErrorType _errorType;
|
||||
|
||||
public ErrorValue(string token)
|
||||
: this(GetErrorType(token))
|
||||
{ }
|
||||
|
||||
private ErrorValue(ErrorType errorType)
|
||||
{
|
||||
_errorType = errorType;
|
||||
}
|
||||
|
||||
private static ErrorType GetErrorType(string token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case HashNull: return ErrorType.Null;
|
||||
case HashDiv0: return ErrorType.Div0;
|
||||
case HashValue: return ErrorType.Value;
|
||||
case HashRef: return ErrorType.Ref;
|
||||
case HashName: return ErrorType.Name;
|
||||
case HashNum: return ErrorType.Num;
|
||||
case HashNA: return ErrorType.NA;
|
||||
default: throw new Exception("Unrecongised error token");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsErrorToken(string token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case HashNull:
|
||||
case HashDiv0:
|
||||
case HashValue:
|
||||
case HashRef:
|
||||
case HashName:
|
||||
case HashNum:
|
||||
case HashNA:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (_errorType)
|
||||
{
|
||||
case ErrorType.Null: return HashNull;
|
||||
case ErrorType.Div0: return HashDiv0;
|
||||
case ErrorType.Value: return HashValue;
|
||||
case ErrorType.Ref: return HashRef;
|
||||
case ErrorType.Name: return HashName;
|
||||
case ErrorType.Num: return HashNum;
|
||||
case ErrorType.NA: return HashNA;
|
||||
default: throw new Exception("Unexpected condition");
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(ErrorValue other)
|
||||
{
|
||||
if (other == null) return -1;
|
||||
return _errorType.CompareTo(other._errorType);
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj.GetType() != GetType()) return -1;
|
||||
return CompareTo(obj as ErrorValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
using spark_excel.grammar;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public interface IExpression
|
||||
{
|
||||
object evaluate();
|
||||
}
|
||||
internal class Expression<T> : IExpression
|
||||
{
|
||||
private readonly ExcelFormulaParser.ExprContext _context;
|
||||
private readonly IExcelFormulaVisitor<T> _visitor;
|
||||
|
||||
public Expression(ExcelFormulaParser.ExprContext context, IExcelFormulaVisitor<T> visitor)
|
||||
{
|
||||
_context = context;
|
||||
_visitor = visitor;
|
||||
}
|
||||
|
||||
public object evaluate()
|
||||
{
|
||||
return _context.Accept(_visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using ExcelDna.Integration;
|
||||
using spark_excel.grammar;
|
||||
|
||||
using Antlr4.Runtime;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
internal class ExcelFormulaErrorListener : BaseErrorListener
|
||||
{
|
||||
public bool IsValid { get; private set; } = true;
|
||||
public string offendingSymbol { get; private set;}
|
||||
public int line { get; private set; }
|
||||
public int charPositionInLine { get; private set; }
|
||||
public string msg { get; private set; }
|
||||
public RecognitionException exception { get; private set;}
|
||||
|
||||
public override void SyntaxError(IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e)
|
||||
{
|
||||
IsValid = false;
|
||||
this.offendingSymbol = offendingSymbol.Text;
|
||||
this.line = line;
|
||||
this.charPositionInLine = charPositionInLine;
|
||||
this.msg = msg;
|
||||
this.exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
//https://theantlrguy.atlassian.net/wiki/spaces/ST/pages/1409118/Language+Translation+Using+ANTLR+and+StringTemplate
|
||||
//https://stackoverflow.com/questions/31511610/antlr-source-to-target-language-conversion
|
||||
public class Generate
|
||||
{
|
||||
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public string bts_generate_code([ExcelArgument(AllowReference=true)] object sink, string function_name = null)
|
||||
{
|
||||
ExcelReference rref = (ExcelReference)sink;
|
||||
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, rref);
|
||||
|
||||
var antlrstream = new AntlrInputStream(formula.ToString());
|
||||
var lexer = new ExcelFormulaLexer(antlrstream);
|
||||
var tokens = new CommonTokenStream(lexer);
|
||||
var parser = new ExcelFormulaParser(tokens);
|
||||
|
||||
var error_listener = new ExcelFormulaErrorListener();
|
||||
//parser.RemoveErrorListeners();
|
||||
parser.AddErrorListener(error_listener);
|
||||
var parsetree = parser.main();
|
||||
|
||||
if (!error_listener.IsValid)
|
||||
{
|
||||
write_file(function_name, error_listener.offendingSymbol + error_listener.msg + error_listener.exception?.ToString());
|
||||
throw new Exception(error_listener.offendingSymbol + error_listener.msg, error_listener.exception);
|
||||
}
|
||||
|
||||
//AbstractParseTreeVisitor
|
||||
GeneratorVisitor visitor = new GeneratorVisitor(rref);
|
||||
object val = visitor.VisitMain(parsetree);
|
||||
|
||||
if(string.IsNullOrEmpty(function_name))
|
||||
function_name = new CellRef(rref).to_name();
|
||||
string ret = $"auto {function_name}() {Environment.NewLine}{{{Environment.NewLine}";
|
||||
visitor.get_defs().ForEach(x => ret += " " + x + Environment.NewLine);
|
||||
ret += " return " + val.ToString() + " ;";
|
||||
ret += $"{Environment.NewLine} }}";
|
||||
|
||||
write_file(function_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void write_file(string function_name, string text)
|
||||
{
|
||||
using(System.IO.StreamWriter file =
|
||||
new System.IO.StreamWriter($"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\\{function_name}.function"))
|
||||
{
|
||||
file.WriteLine(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using spark_excel.grammar;
|
||||
using Antlr4.Runtime;
|
||||
|
||||
using ExcelDna.Integration;
|
||||
using bts.excel.extensions;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public interface IDefinitionVisitor
|
||||
{
|
||||
void add_def(string def);
|
||||
List<string> get_defs();
|
||||
}
|
||||
|
||||
|
||||
public class GeneratorVisitor : ExcelFormulaBaseVisitor<object>, IDefinitionVisitor
|
||||
{
|
||||
ExcelReference _ref;
|
||||
functions.lib.FunctionLibrary _library = new functions.lib.FunctionLibrary();
|
||||
ExcelFormulaErrorListener _error_listener = new ExcelFormulaErrorListener();
|
||||
List<string> _defs = new List<string>();
|
||||
Dictionary<string, string> _def_map = new Dictionary<string, string>();
|
||||
|
||||
|
||||
public GeneratorVisitor(ExcelReference rref)
|
||||
{
|
||||
_ref = rref;
|
||||
}
|
||||
|
||||
public void add_def(string def)
|
||||
{
|
||||
_defs.Add(def);
|
||||
}
|
||||
|
||||
public void map_def(string def, string mapped)
|
||||
{
|
||||
_def_map[def] = mapped;
|
||||
}
|
||||
|
||||
public List<string> get_defs()
|
||||
{
|
||||
var list = _defs.Distinct();
|
||||
List<string> defs = new List<string>();
|
||||
foreach(var d in list)
|
||||
defs.Add( _def_map.TryGetValue(d, out string mapped) ? mapped : d );
|
||||
return defs;
|
||||
}
|
||||
|
||||
#region Context visitors overrides
|
||||
|
||||
static ConcurrentDictionary<string, string> _algo_params = new ConcurrentDictionary<string, string>();
|
||||
public static void register_algo_parameter(string sheet, string name)
|
||||
{
|
||||
_algo_params[name] = sheet;
|
||||
}
|
||||
|
||||
public override object VisitName(ExcelFormulaParser.NameContext context)
|
||||
{
|
||||
var name = context.GetText();
|
||||
|
||||
var lookup = name;
|
||||
if(!lookup.Contains("!"))
|
||||
lookup = "!" + lookup;
|
||||
object value = XlCall.Excel(XlCall.xlfGetName, lookup);
|
||||
if(value != null && !(value is ExcelError))
|
||||
{
|
||||
string[] par = value.ToString().Split(new[] {'=', 'R', 'C'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
int r = int.Parse(par[0]) - 1;
|
||||
int c = int.Parse(par[1]) - 1;
|
||||
var rref = new ExcelReference(r, r, c, c, _ref.GetSheetName());
|
||||
process_cell(rref);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private object process_cell(ExcelReference rref)
|
||||
{
|
||||
var formula = XlCall.Excel(XlCall.xlfGetCell, 41, rref);
|
||||
|
||||
var formstring = formula.ToString();
|
||||
if(string.IsNullOrEmpty(formstring))
|
||||
formstring = "0.0";
|
||||
if (!formstring.StartsWith("="))
|
||||
formstring = "=" + formstring;
|
||||
|
||||
var cellref = new CellRef(rref);
|
||||
string r1c1 = cellref.r1_c1();
|
||||
var cellname = XlCall.Excel(XlCall.xlfGetDef, r1c1, cellref.SheetName);
|
||||
bool named = false;
|
||||
if (!(cellname is ExcelError))
|
||||
{
|
||||
named = true;
|
||||
cellref.named_range = cellname.ToString();
|
||||
}
|
||||
|
||||
var antlrstream = new AntlrInputStream(formstring);
|
||||
var lexer = new ExcelFormulaLexer(antlrstream);
|
||||
var tokens = new CommonTokenStream(lexer);
|
||||
var parser = new ExcelFormulaParser(tokens);
|
||||
parser.AddErrorListener(_error_listener);
|
||||
var parsetree = parser.main();
|
||||
if (!_error_listener.IsValid)
|
||||
{
|
||||
//throw new ExcelFormulaException(_error_listener.ErrorLocation, _error_listener.ErrorMessage);
|
||||
var def = $"{cellref.ToString()} = Error parsing cell {_error_listener.offendingSymbol } {_error_listener.exception?.ToString()} ;";
|
||||
add_def(def);
|
||||
}
|
||||
else
|
||||
{
|
||||
var def = $"auto&& {cellref.ToString()} = {this.VisitMain(parsetree)} ;";
|
||||
add_def(def);
|
||||
if (named && _algo_params.ContainsKey(cellref.named_range))
|
||||
{
|
||||
map_def(def, $"auto&& {cellref.named_range} = spark_algo_parameter(\"{cellref.named_range}\");");
|
||||
}
|
||||
}
|
||||
return cellref;
|
||||
}
|
||||
|
||||
public override object VisitMain(ExcelFormulaParser.MainContext context)
|
||||
{
|
||||
return context.expr().Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitMulDiv(ExcelFormulaParser.MulDivContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = expr[0].Accept(this);
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = expr[1].Accept(this);
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
string code = $"{arg1} {context.op.Text} {arg2}";
|
||||
return code;
|
||||
}
|
||||
|
||||
public override object VisitAddSub(ExcelFormulaParser.AddSubContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = expr[0].Accept(this);
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = expr[1].Accept(this);
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
string code = $"{arg1} {context.op.Text} {arg2}";
|
||||
return code;
|
||||
}
|
||||
|
||||
public override object VisitPercentage(ExcelFormulaParser.PercentageContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = expr.Accept(this);
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
return $"( {arg1} / 100.0 )";
|
||||
}
|
||||
|
||||
public override object VisitRelational(ExcelFormulaParser.RelationalContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = expr[0].Accept(this);
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = expr[1].Accept(this);
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
return $"{arg1} {context.op.Text} {arg2}";
|
||||
}
|
||||
|
||||
|
||||
public override object VisitError(ExcelFormulaParser.ErrorContext context)
|
||||
{
|
||||
return new ErrorValue(context.GetText());
|
||||
}
|
||||
|
||||
public override object VisitString(ExcelFormulaParser.StringContext context)
|
||||
{
|
||||
var str = context.GetText();
|
||||
return $"std::string({str})";
|
||||
}
|
||||
|
||||
public override object VisitUnary(ExcelFormulaParser.UnaryContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
if(context.sign.Text == "-")
|
||||
return "-" + expr.Accept(this);
|
||||
else
|
||||
return expr.Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitFunction(ExcelFormulaParser.FunctionContext context)
|
||||
{
|
||||
var name = context.name.Text;
|
||||
|
||||
if (!_library.is_defined(name))
|
||||
return ErrorValue.Name;
|
||||
|
||||
var expr = context.expr();
|
||||
var args = new IExpression[expr.Length];
|
||||
for (var i = 0; i < expr.Length; i++)
|
||||
args[i] = new Expression<object>(expr[i], this);
|
||||
return _library.call_function(name, args, this);
|
||||
}
|
||||
|
||||
public override object VisitParenthesis(ExcelFormulaParser.ParenthesisContext context)
|
||||
{
|
||||
return context.expr().Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitNumber(ExcelFormulaParser.NumberContext context)
|
||||
{
|
||||
var num = context.GetText();
|
||||
if(double.TryParse(num, out double res))
|
||||
return res.ToString("0.0############");
|
||||
return num;
|
||||
}
|
||||
|
||||
public override object VisitConcatenate(ExcelFormulaParser.ConcatenateContext context)
|
||||
{
|
||||
var expr = context.expr();
|
||||
|
||||
var arg1 = "std::to_string(" + expr[0].Accept(this) + ")";
|
||||
var arg2 = "std::to_string(" + expr[1].Accept(this) + ")";
|
||||
return $"{arg1}+{arg2}";
|
||||
}
|
||||
|
||||
public override object VisitRange(ExcelFormulaParser.RangeContext context)
|
||||
{
|
||||
var rng = context.rng();
|
||||
return rng.Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitRangeList(ExcelFormulaParser.RangeListContext context)
|
||||
{
|
||||
var result = new List<RangeRef>();
|
||||
|
||||
foreach (var range in context.rng())
|
||||
{
|
||||
var obj = range.Accept(this);
|
||||
|
||||
switch (obj)
|
||||
{
|
||||
case ErrorValue err:
|
||||
result.Add(null);
|
||||
break;
|
||||
case RangeRef rangeRef:
|
||||
result.Add(rangeRef);
|
||||
break;
|
||||
case null:
|
||||
result.Add(null);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object VisitRangeRef(ExcelFormulaParser.RangeRefContext context)
|
||||
{
|
||||
var obj = context.@ref().Accept(this);
|
||||
|
||||
switch (obj)
|
||||
{
|
||||
case ErrorValue _:
|
||||
return obj;
|
||||
case CellRef start:
|
||||
var (column, row) = CellRef.ConvertAddressToCoord(context.end.Text);
|
||||
var rng = new RangeRef(start, column, row);
|
||||
|
||||
if(rng.IsOneCell())
|
||||
{
|
||||
var rref = new ExcelReference(row - 1, row - 1, column - 1, column - 1, start.SheetName);
|
||||
process_cell(rref);
|
||||
}
|
||||
else
|
||||
{
|
||||
var def =
|
||||
$@"std::vector<auto> {rng.to_name()};
|
||||
auto {rng.to_name()}_load = [=](auto&& val)
|
||||
{{
|
||||
}};
|
||||
";
|
||||
var rref = new ExcelReference(rng.Start.Row - 1, rng.End.Row - 1,
|
||||
rng.Start.Column- 1, rng.End.Column - 1, start.SheetName);
|
||||
//process_cell(rref);
|
||||
}
|
||||
return rng;
|
||||
case null:
|
||||
return null;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
public override object VisitCell(ExcelFormulaParser.CellContext context)
|
||||
{
|
||||
return context.@ref().Accept(this);
|
||||
}
|
||||
|
||||
public override object VisitCellList(ExcelFormulaParser.CellListContext context)
|
||||
{
|
||||
var result = new List<CellRef>();
|
||||
foreach (var @ref in context.@ref())
|
||||
{
|
||||
var obj = @ref.Accept(this);
|
||||
switch (obj)
|
||||
{
|
||||
case ErrorValue _:
|
||||
// TODO: Warn that error has been replaced with null here?
|
||||
result.Add(null);
|
||||
break;
|
||||
case CellRef cellRef:
|
||||
result.Add(cellRef);
|
||||
break;
|
||||
case null:
|
||||
result.Add(null);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object VisitCellRef(ExcelFormulaParser.CellRefContext context)
|
||||
{
|
||||
var colRow = context.colRow.Text;
|
||||
var (col1, row1) = CellRef.ConvertAddressToCoord(colRow);
|
||||
var sheetname = context.sheet?.Text ?? _ref.GetSheetName();
|
||||
|
||||
var rref = new ExcelReference(row1 - 1, row1 - 1, col1 - 1, col1 - 1, sheetname);
|
||||
return process_cell(rref);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// public override object VisitRangeRef(ExcelFormulaParser.RangeRefContext context)
|
||||
// {
|
||||
// var obj = context.@ref().Accept(this);
|
||||
// var end = context.COL_ROW();
|
||||
//return obj;
|
||||
|
||||
// var result = new List<CellRef>();
|
||||
// var rng = context.GetRuleContexts<spark_excel.grammar.ExcelFormulaParser.RngContext>();
|
||||
// foreach(var r in rng)
|
||||
// {
|
||||
// var obj = r.Accept(this);
|
||||
// switch (obj)
|
||||
// {
|
||||
// case ErrorValue err:
|
||||
// result.Add(null);
|
||||
// break;
|
||||
// case CellRef rangeRef:
|
||||
// result.Add(rangeRef);
|
||||
// break;
|
||||
// case null:
|
||||
// result.Add(null);
|
||||
// break;
|
||||
// default:
|
||||
// throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
|
||||
// }
|
||||
// }
|
||||
// return result;
|
||||
|
||||
// var colRow = context.COL_ROW();
|
||||
// var colrowstring = colRow.ToString();
|
||||
// var (col1, row1) = CellRef.ConvertAddressToCoord(colrowstring);
|
||||
// var sheetname = !colrowstring.Contains("!") ? _simple_sheetname : _simple_sheetname;
|
||||
|
||||
// var rref = new ExcelReference(row1 - 1, row1 - 1, col1 - 1, col1 - 1, sheetname);
|
||||
// return process_cell(rref, sheetname);
|
||||
|
||||
// switch (obj)
|
||||
// {
|
||||
// case ErrorValue _:
|
||||
// return obj;
|
||||
// case CellRef start:
|
||||
// var (column, row) = CellRef.ConvertAddressToCoord(context.end.Text);
|
||||
// return new RangeRef(start, column, row);
|
||||
// case null:
|
||||
// return null;
|
||||
// default:
|
||||
// throw new ArgumentOutOfRangeException($"Unexpected type: {obj.GetType()}");
|
||||
// }
|
||||
// }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public class RangeRef
|
||||
{
|
||||
public RangeRef(CellRef start, CellRef end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public string named_range { get; set;}
|
||||
|
||||
public RangeRef(CellRef start, int endColumn, int endRow)
|
||||
: this(start, new CellRef(start.SheetName, (endColumn, endRow)))
|
||||
{ }
|
||||
|
||||
public RangeRef Offset(int columnOffset, int rowOffset)
|
||||
{
|
||||
var start = Start.Offset(columnOffset, rowOffset);
|
||||
var end = End.Offset(columnOffset, rowOffset);
|
||||
return new RangeRef(start, end);
|
||||
}
|
||||
|
||||
public CellRef Start { get; }
|
||||
|
||||
public CellRef End { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return to_name();
|
||||
}
|
||||
|
||||
public string SheetName => Start.SheetName;
|
||||
public string SimpleSheetName => Start.SimpleSheetName;
|
||||
|
||||
public string to_address()
|
||||
{
|
||||
return string.Format("'{0}'!{1}:{2}",
|
||||
SheetName,
|
||||
CellRef.ConvertCoordToAddress((Start.Column, Start.Row)),
|
||||
CellRef.ConvertCoordToAddress((End.Column, End.Row)));
|
||||
}
|
||||
|
||||
public string to_name()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(named_range))
|
||||
return named_range;
|
||||
else
|
||||
{
|
||||
return string.Format("{0}_{1}_{2}",
|
||||
Start.SimpleSheetName,
|
||||
CellRef.ConvertCoordToAddress((Start.Column, Start.Row)),
|
||||
CellRef.ConvertCoordToAddress((End.Column, End.Row)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool IsVector()
|
||||
{
|
||||
return IsSingleRow() || IsSingleColumn();
|
||||
}
|
||||
|
||||
public bool IsOneCell()
|
||||
{
|
||||
return IsSingleRow() && IsSingleColumn();
|
||||
}
|
||||
|
||||
internal List<CellRef> Get2DVector()
|
||||
{
|
||||
var result = new List<CellRef>();
|
||||
var sheetName = Start.SheetName;
|
||||
var numRows = NumRows();
|
||||
var lastRow = Start.Row + numRows - 1;
|
||||
|
||||
for (var row = Start.Row; row <= lastRow; row++)
|
||||
{
|
||||
var numCols = NumCols();
|
||||
var lastCol = Start.Column + numCols - 1;
|
||||
for (var col = Start.Column; col <= lastCol; col++)
|
||||
{
|
||||
result.Add(new CellRef(sheetName, (col, row)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
internal List<CellRef> GetVector()
|
||||
{
|
||||
Debug.Assert(IsVector());
|
||||
|
||||
var result = new List<CellRef>();
|
||||
|
||||
if (IsSingleColumn())
|
||||
{
|
||||
var col = Start.Column;
|
||||
var sheetName = Start.SheetName;
|
||||
var numRows = NumRows();
|
||||
var lastRow = Start.Row + numRows - 1;
|
||||
for (var row = Start.Row; row <= lastRow; row++)
|
||||
{
|
||||
result.Add(new CellRef(sheetName, (col, row)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(IsSingleRow());
|
||||
|
||||
var row = Start.Row;
|
||||
var sheetName = Start.SheetName;
|
||||
var numCols = NumCols();
|
||||
var lastCol = Start.Column + numCols - 1;
|
||||
for (var col = Start.Column; col <= lastCol; col++)
|
||||
{
|
||||
result.Add(new CellRef(sheetName, (col, row)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsSingleColumn()
|
||||
{
|
||||
Debug.Assert(Start.SheetName == End.SheetName);
|
||||
return Start.Column == End.Column;
|
||||
}
|
||||
|
||||
public bool IsSingleRow()
|
||||
{
|
||||
Debug.Assert(Start.SheetName == End.SheetName);
|
||||
return Start.Row == End.Row;
|
||||
}
|
||||
|
||||
public int NumRows()
|
||||
{
|
||||
Debug.Assert(Start.SheetName == End.SheetName);
|
||||
return End.Row - Start.Row + 1;
|
||||
}
|
||||
|
||||
public int NumCols()
|
||||
{
|
||||
Debug.Assert(Start.SheetName == End.SheetName);
|
||||
return End.Column - Start.Column + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using bts.excel.functions;
|
||||
using ExcelDna.Registration;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public interface IFunction
|
||||
{
|
||||
object function(IExpression[] args, IDefinitionVisitor dv);
|
||||
}
|
||||
public class FunctionLibrary
|
||||
{
|
||||
private readonly Dictionary<string, IFunction> _functions =
|
||||
new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<string, Delegate> _dnafunctions =
|
||||
new Dictionary<string, Delegate>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public FunctionLibrary()
|
||||
{
|
||||
add_functions(Logical.functions);
|
||||
add_functions(Mathematics.functions);
|
||||
add_functions(Stat.functions);
|
||||
|
||||
var dna = ExcelRegistration.GetExcelFunctions();
|
||||
foreach(var xl in dna)
|
||||
{
|
||||
_dnafunctions[xl.FunctionAttribute.Name] =
|
||||
xl.FunctionLambda.Compile();
|
||||
}
|
||||
}
|
||||
|
||||
public bool is_defined(string name) => _functions.ContainsKey(name) || _dnafunctions.ContainsKey(name);
|
||||
|
||||
public void add_function(string name, IFunction function)
|
||||
{
|
||||
_functions.Add(name, function);
|
||||
}
|
||||
|
||||
public void add_functions(Dictionary<string, IFunction> delegates)
|
||||
{
|
||||
foreach (var f in delegates)
|
||||
_functions.Add(f.Key, f.Value);
|
||||
}
|
||||
|
||||
public object call_function(string name, IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
if(_functions.TryGetValue(name, out IFunction func))
|
||||
return func.function(args, dv);
|
||||
else
|
||||
return call_dna_function(name, args, dv);
|
||||
}
|
||||
|
||||
object call_dna_function(string name, IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
if(_dnafunctions.TryGetValue(name, out Delegate func))
|
||||
{
|
||||
object[] param = args.Select(x => x.evaluate()).ToArray();
|
||||
// var pp = func.Method.GetParameters();
|
||||
// if(pp.Length -1 != param.Length)
|
||||
// throw new ArgumentException("invalide args to dna function " + name);
|
||||
|
||||
string code = $"{name} ( "; int i = 0;
|
||||
foreach(var p in param)
|
||||
code += p.ToString() + (i++ < param.Length-1 ? ", " : "");
|
||||
code += ")";
|
||||
return code;
|
||||
}
|
||||
else
|
||||
return ErrorValue.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public static class Logical
|
||||
{
|
||||
public static Dictionary<string, IFunction> functions { get; }
|
||||
|
||||
static Logical()
|
||||
{
|
||||
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"AND", new And()},
|
||||
{"IF", new If()},
|
||||
{"IFERROR", new IfError()},
|
||||
{"OR", new Or()},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class Mathematics
|
||||
{
|
||||
public static Dictionary<string, IFunction> functions { get; }
|
||||
|
||||
static Mathematics()
|
||||
{
|
||||
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"LN", new Ln()},
|
||||
{"LOG", new Log()},
|
||||
{"POWER", new Power()},
|
||||
{"PI", new Pi()},
|
||||
{"SQRT", new Sqrt()},
|
||||
{"SUM", new Sum()},
|
||||
{"SIN", new Sin()},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class Stat
|
||||
{
|
||||
public static Dictionary<string, IFunction> functions { get; }
|
||||
|
||||
static Stat()
|
||||
{
|
||||
functions = new Dictionary<string, IFunction>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"AVERAGE", new Average()},
|
||||
{"MAX", new Max()},
|
||||
{"MIN", new Min()},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class And : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: AND(logical1, [logical2], ...)
|
||||
// https://support.office.com/en-gb/article/AND-function-5f19b2e8-e1df-4408-897a-ce285a19e9d9
|
||||
|
||||
if (args.Length < 2)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"AND function requires at least 2 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string code = "(";
|
||||
foreach (var fx in args)
|
||||
{
|
||||
var arg = fx.evaluate();
|
||||
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
code += arg + " && ";
|
||||
}
|
||||
code += "true)";
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Average : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: Average(number1,[number2],...)
|
||||
// https://support.office.com/en-GB/article/AVERAGE-function-047bac88-d466-426c-a32b-8f33eb960cf6
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Average function takes at least 1 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string name = "avg_" + new Random().Next(0, int.MaxValue);
|
||||
string def = ($"auto {name} = 0.0;{Environment.NewLine}");
|
||||
|
||||
int count = 0;
|
||||
foreach (var arg in args)
|
||||
{
|
||||
var argv = arg.evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
count++;
|
||||
def += $" {name} += {argv};{Environment.NewLine}";
|
||||
}
|
||||
if(count > 1)
|
||||
def += $" {name} /= {count}";
|
||||
dv.add_def(def);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class If : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: IF(logical_test, value_if_true, [value_if_false])
|
||||
// https://support.office.com/en-gb/article/IF-function-69aed7c9-4e8a-4755-a9bc-aa8bbff73be2
|
||||
|
||||
if (args.Length != 2 && args.Length != 3)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"IF function takes 2 or 3 arguments",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
var arg = args[0].evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
string code = string.Empty;
|
||||
if (args.Length == 3)
|
||||
{
|
||||
code += $"( {arg} ? ({ args[1].evaluate() }) : ({args[2].evaluate()}) ) ";
|
||||
}
|
||||
else
|
||||
{
|
||||
code += $"( {arg} ? ({ args[1].evaluate() }) : false) ";
|
||||
}
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class IfError : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: IFERROR(value, value_if_error)
|
||||
// https://support.office.com/en-gb/article/IFERROR-function-c526fd07-caeb-47b8-8bb6-63f3e417f611
|
||||
|
||||
if (args.Length != 2)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"IFERROR function takes exactly 2 arguments",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var arg = args[0].evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
return arg;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return args[1].evaluate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Ln : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: LN(number)
|
||||
// https://support.office.com/en-gb/article/LN-function-81fe1ed7-dac9-4acd-ba1d-07a142c6118f
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"LN function takes only 1 argument, got {args.Length}");
|
||||
}
|
||||
|
||||
var arg = args[0].evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
string code = $"log({arg})";
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Log : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: LOG(number, [base])
|
||||
// https://support.office.com/en-gb/article/LOG-function-4e82f196-1ca9-4747-8fb0-6c4a3abb3280
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
case 1:
|
||||
var arg = args[0].evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
string code = $"log10({arg})";
|
||||
return code;
|
||||
|
||||
case 2:
|
||||
var arg1 = args[0].evaluate();
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = args[1].evaluate();
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
string code2 = $"log({arg1}, {arg2})";
|
||||
return code2;
|
||||
|
||||
default:
|
||||
throw new ArgumentException(
|
||||
$"LOG function takes only 1 or 2 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Max : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: MAX(number1, [number2], ...)
|
||||
// https://support.office.com/en-GB/article/MAX-function-e0012414-9ac8-4b34-9a47-73e662c08098
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"MAX function takes at least 1 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string code = $"std::max(\n";
|
||||
foreach (var arg in args)
|
||||
{
|
||||
var argv = arg.evaluate();
|
||||
if (argv is ErrorValue)
|
||||
return argv;
|
||||
|
||||
code += $"{argv},";
|
||||
}
|
||||
code += $"{ args[args.Length-1].evaluate()} );\n";
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Min : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: MIN(number1, [number2], ...)
|
||||
// https://support.office.com/en-GB/article/MIN-function-61635d12-920f-4ce2-a70f-96f202dcc152
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"MIN function takes at least 1 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string code = $"std::min(\n";
|
||||
foreach (var arg in args)
|
||||
{
|
||||
var argv = arg.evaluate();
|
||||
if (argv is ErrorValue)
|
||||
return argv;
|
||||
|
||||
code += $"{argv},";
|
||||
}
|
||||
code += $"{ args[args.Length - 1].evaluate()} );\n";
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Or : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: OR(logical1, [logical2], ...)
|
||||
// https://support.office.com/en-gb/article/OR-function-7d17ad14-8700-4281-b308-00b131e22af0
|
||||
|
||||
string code = "(";
|
||||
foreach (var fx in args)
|
||||
{
|
||||
var arg = fx.evaluate();
|
||||
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
code += arg + " || ";
|
||||
}
|
||||
code += "false)";
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Pi : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: PI()
|
||||
// https://support.office.com/en-gb/article/PI-function-264199d0-a3ba-46b8-975a-c4a04608989b
|
||||
|
||||
if (args.Length != 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"PI function takes no arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
return Math.PI.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Power : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: POWER(number, power)
|
||||
// https://support.office.com/en-gb/article/POWER-function-d3f2908b-56f4-4c3f-895a-07fb519c362a
|
||||
|
||||
if (args.Length != 2)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"POWER function takes 2 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
var arg1 = args[0].evaluate();
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
var arg2 = args[1].evaluate();
|
||||
if (arg2 is ErrorValue)
|
||||
return arg2;
|
||||
|
||||
return $"pow({arg1}, {arg2})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Sin : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: SIN(number)
|
||||
// https://support.office.com/en-gb/article/SIN-function-654975c2-05c4-4831-9a24-2c65e4040fdf
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Sin function takes only 1 argument, got {args.Length}");
|
||||
}
|
||||
|
||||
var arg1 = args[0].evaluate();
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
return $"sin({arg1})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Sqrt : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: SQRT(number)
|
||||
// https://support.office.com/en-gb/article/SQRT-function-654975c2-05c4-4831-9a24-2c65e4040fdf
|
||||
|
||||
if (args.Length != 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"SQRT function takes only 1 argument, got {args.Length}");
|
||||
}
|
||||
|
||||
var arg1 = args[0].evaluate();
|
||||
if (arg1 is ErrorValue)
|
||||
return arg1;
|
||||
|
||||
return $"sqrt({arg1})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace bts.excel.functions.lib
|
||||
{
|
||||
public class Sum : IFunction
|
||||
{
|
||||
public object function(IExpression[] args, IDefinitionVisitor dv)
|
||||
{
|
||||
// Syntax: SUM(number1,[number2],...)
|
||||
// https://support.office.com/en-GB/article/SUM-function-043e1c7d-7726-4e80-8f32-07b23e057f89
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"SUM function takes at least 1 arguments, got {args.Length}",
|
||||
nameof(args));
|
||||
}
|
||||
|
||||
string name = "sum_" + new Random().Next(0, int.MaxValue);
|
||||
string def = ($"auto {name} = 0.0;{Environment.NewLine}");
|
||||
|
||||
foreach (var arg in args)
|
||||
{
|
||||
var argv = arg.evaluate();
|
||||
if (arg is ErrorValue)
|
||||
return arg;
|
||||
|
||||
def += $" {name} += {argv};{Environment.NewLine}";
|
||||
}
|
||||
dv.add_def(def);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using ExcelDna.Integration;
|
||||
|
||||
namespace bts.excel.extensions
|
||||
{
|
||||
public static class ExcelDNAExtensions
|
||||
{
|
||||
public static string GetSheetName(this ExcelReference rref)
|
||||
{
|
||||
string sheetname = (string)XlCall.Excel(XlCall.xlSheetNm, rref);
|
||||
return sheetname;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ using bts.excel;
|
||||
using bts.excel.extensions;
|
||||
using bts.excel.rtd;
|
||||
using Newtonsoft.Json;
|
||||
using bts.api;
|
||||
|
||||
|
||||
namespace bts.api.orderman
|
||||
@@ -20,6 +21,14 @@ namespace bts.api.orderman
|
||||
readonly static Lazy<user.AuthService> _users = new Lazy<user.AuthService>(() => Locator.locate<user.AuthService>(), true);
|
||||
readonly static Lazy<admin.AdminService> _admin = new Lazy<admin.AdminService>(() => Locator.locate<admin.AdminService>(), true);
|
||||
|
||||
struct OrderDef
|
||||
{
|
||||
public Create create;
|
||||
public Response response;
|
||||
}
|
||||
static ConcurrentDictionary<long, OrderDef> _orders = new ConcurrentDictionary<long, OrderDef>();
|
||||
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Get All Accessible Accounts")]
|
||||
async public static void spark_get_accounts(string login_token, ExcelAsyncHandle handle)
|
||||
{
|
||||
@@ -42,48 +51,66 @@ namespace bts.api.orderman
|
||||
}
|
||||
}
|
||||
|
||||
static Response cancel_order(long order_id)
|
||||
{
|
||||
var resp = _service.Value.cancel_order(new Cancel() { OrderKey = Convert.ToUInt32(order_id) });
|
||||
return resp;
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Cancel an Order by Order ID", IsMacroType = true)]
|
||||
public static object spark_orderservice_cancel_order(long order_id)
|
||||
{
|
||||
var resp = _service.Value.cancel_order(new Cancel() { OrderKey = Convert.ToUInt32(order_id) });
|
||||
var resp = cancel_order(order_id);
|
||||
return parse_response(resp);
|
||||
}
|
||||
|
||||
static Response update_order(long order_id, double price, long quantity, CustomOrderDefinition cust = null)
|
||||
{
|
||||
var resp = _service.Value.update_order(new Update()
|
||||
{
|
||||
OrderKey = Convert.ToUInt32(order_id),
|
||||
Price = price,
|
||||
Quantity = Convert.ToUInt32(quantity) ,
|
||||
Custom = cust
|
||||
});
|
||||
return resp;
|
||||
}
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Update an Order by Order ID", IsMacroType = true)]
|
||||
public static object spark_orderservice_update_order(long order_id, double price, long quantity)
|
||||
{
|
||||
var resp = _service.Value.update_order(new Update() { OrderKey = Convert.ToUInt32(order_id), Price = price, Quantity = Convert.ToUInt32(quantity) });
|
||||
var resp = update_order(order_id, price, quantity);
|
||||
return parse_response(resp);
|
||||
}
|
||||
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key", IsMacroType = true)]
|
||||
public static object spark_orderservice_send_order_key(
|
||||
[ExcelArgument("Required. Only send the order if > last")] long local_order_id,
|
||||
[ExcelArgument("Required. Only send the order if > 1")] long on_off,
|
||||
[ExcelArgument("Required. Local Order ID used to differentiate between create / update")] long local_order_id,
|
||||
[ExcelArgument("Required. Security key")] long security_key,
|
||||
[ExcelArgument("Required. Side BUY or SELL")] string side,
|
||||
[ExcelArgument("Required. User")] string user,
|
||||
[ExcelArgument("Required. Account")] string account,
|
||||
[ExcelArgument("Required. Price")] double price,
|
||||
[ExcelArgument("Required. Quantity")] long quantity,
|
||||
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
|
||||
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type = null,
|
||||
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params = null,
|
||||
|
||||
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null,
|
||||
[ExcelArgument("OPTIONAL. (YES,NO) - Default YES")] string manual_order_indicator = null,
|
||||
[ExcelArgument("OPTIONAL. (1,2,3,4) - Default 1")] string cti_code = null,
|
||||
[ExcelArgument("OPTIONAL. (CUSTOMER,FIRM) - Default FIRM")] string customer_or_firm = null)
|
||||
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null)
|
||||
{
|
||||
if (local_order_id <= _local_order_id)
|
||||
return "local_order_id must be greater than " + _local_order_id;
|
||||
if(on_off <= 0)
|
||||
return "this order is turned off";
|
||||
if(_orders.ContainsKey(local_order_id))
|
||||
return "use a new local order id per order";
|
||||
Create cr = new Create();
|
||||
cr.NewOrder = new OrderDefinition()
|
||||
{
|
||||
SecurityKey = Convert.ToUInt32(security_key),
|
||||
Side = EnumParser.ToEnum<api.Side>(side),
|
||||
TimeInForce = time_in_force.ToEnum<TimeInForce>(TimeInForce.Day),
|
||||
ManualOrderIndicator = manual_order_indicator.ToEnum<ManualOrderIndicator>(ManualOrderIndicator.Yes),
|
||||
CtiCode = cti_code.ToEnum<CTICode>(CTICode.CtiCode1),
|
||||
CustomerOrFirm = customer_or_firm.ToEnum<CustomerOrFirm>(CustomerOrFirm.Firm),
|
||||
// ManualOrderIndicator = manual_order_indicator.ToEnum<ManualOrderIndicator>(ManualOrderIndicator.Yes),
|
||||
// CtiCode = cti_code.ToEnum<CTICode>(CTICode.CtiCode1),
|
||||
// CustomerOrFirm = customer_or_firm.ToEnum<CustomerOrFirm>(CustomerOrFirm.Firm),
|
||||
};
|
||||
cr.Price = price;
|
||||
cr.Quantity = Convert.ToUInt32(quantity);
|
||||
@@ -98,12 +125,14 @@ namespace bts.api.orderman
|
||||
return "Could not resolve account: " + account;
|
||||
cr.NewOrder.Account = btaccount.Name;
|
||||
cr.NewOrder.AccountId = btaccount.Id;
|
||||
if(string.IsNullOrEmpty(order_type))
|
||||
order_type = "Limit";
|
||||
cr.NewOrder.CustomDef = custom_def(order_type, order_params);
|
||||
if(cr.NewOrder.CustomDef == null || cr.NewOrder.CustomDef.CustomCase == CustomOrderDefinition.CustomOneofCase.None)
|
||||
return "Could not parse order_type and/or order_params info";
|
||||
|
||||
var resp = _service.Value.create_order(cr);
|
||||
_local_order_id = local_order_id;
|
||||
_orders[local_order_id] = new OrderDef { create = cr, response = resp };
|
||||
return parse_response(resp, true);
|
||||
}
|
||||
|
||||
@@ -129,7 +158,7 @@ namespace bts.api.orderman
|
||||
}
|
||||
|
||||
static CustomOrderDefinition custom_def(string order_type, string order_params)
|
||||
{
|
||||
{
|
||||
var member = ReflectionExtensions.get_fields_or_properties(typeof(CustomOrderDefinition))
|
||||
.Where(x => x.Name.ToLower() == order_type.ToLower()).FirstOrDefault();
|
||||
if (member != null)
|
||||
@@ -142,13 +171,13 @@ namespace bts.api.orderman
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static long _local_order_id = 0;
|
||||
|
||||
[ExcelFunction(Category = "BTS", Description = "Send an Order to Spark via security_key", IsMacroType = true)]
|
||||
public static object spark_orderservice_send_order(
|
||||
[ExcelArgument("Required. Only send the order if > last")] long local_order_id,
|
||||
[ExcelFunction(Category = "BTS", Description = "Send or Modify or Cancel a Spark Order ")]
|
||||
public async static void spark_send_or_update_order(
|
||||
[ExcelArgument("Required. Only send the order if > 1")] long on_off,
|
||||
[ExcelArgument("Required. Local Order ID used to differentiate between create / update")] long local_order_id,
|
||||
[ExcelArgument("Required. Exchange (GLBX,SIM)")] string exchange,
|
||||
[ExcelArgument("Required. Security Desc (ZNZ9 or ZNZ9-ZNH0)")] string security_desc,
|
||||
[ExcelArgument("Required. Side BUY or SELL")] string side,
|
||||
@@ -156,29 +185,64 @@ namespace bts.api.orderman
|
||||
[ExcelArgument("Required. Account")] string account,
|
||||
[ExcelArgument("Required. Price")] double price,
|
||||
[ExcelArgument("Required. Quantity")] long quantity,
|
||||
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type,
|
||||
[ExcelArgument("Required. OrderType (Limit, Stop, Stop_Limit, Iceberg) - Default Limit")] string order_type = null,
|
||||
[ExcelArgument("OPTIONAL. Order Params JSON (\"stop_price\" : 51.88)")] string order_params = null,
|
||||
|
||||
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null,
|
||||
[ExcelArgument("OPTIONAL. (YES,NO) - Default YES")] string manual_order_indicator = null,
|
||||
[ExcelArgument("OPTIONAL. (1,2,3,4) - Default 1")] string cti_code = null,
|
||||
[ExcelArgument("OPTIONAL. (CUSTOMER,FIRM) - Default FIRM")] string customer_or_firm = null)
|
||||
{
|
||||
if (local_order_id <= _local_order_id)
|
||||
return "local_order_id must be greater than " + _local_order_id;
|
||||
Venue ven;
|
||||
if (!Utilities.parse_venue(exchange, out ven))
|
||||
return "Can't Parse Exchange: " + (exchange ?? string.Empty);
|
||||
|
||||
uint key;
|
||||
if (MDSubscribe.market_data(ven).lookup_security_key_from_desc(security_desc, out key))
|
||||
[ExcelArgument("OPTIONAL. (DAY,GTC,IOC) - Default DAY")] string time_in_force = null,
|
||||
ExcelAsyncHandle handle = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return spark_orderservice_send_order_key(local_order_id, (long)key, side, user, account, price, quantity, order_type, order_params,
|
||||
time_in_force, manual_order_indicator, cti_code, customer_or_firm);
|
||||
Venue ven;
|
||||
if (!Utilities.parse_venue(exchange, out ven))
|
||||
handle.SetResult("Can't Parse Exchange: " + (exchange ?? string.Empty));
|
||||
if (on_off <= 0)
|
||||
handle.SetResult("this order is turned off");
|
||||
|
||||
var sec = await MDSubscribe.market_data(ven).find_security_async(x => x.security_desc() == security_desc);
|
||||
if (sec == null)
|
||||
{
|
||||
handle.SetResult("Can't find security desc " + security_desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_orders.TryGetValue(local_order_id, out OrderDef def) && def.response.OrderKey > 0)
|
||||
{
|
||||
if(quantity > 0)
|
||||
{
|
||||
if (string.IsNullOrEmpty(order_type))
|
||||
order_type = "Limit";
|
||||
var cust = custom_def(order_type, order_params);
|
||||
if (cust == null || cust.CustomCase == CustomOrderDefinition.CustomOneofCase.None)
|
||||
handle.SetResult("Could not parse order_type and/or order_params info");
|
||||
if(def.create.Price != price || def.create.Quantity != Convert.ToUInt32(quantity) ||
|
||||
(cust != null && !cust.Equals(def.create.NewOrder.CustomDef)))
|
||||
{
|
||||
var resp = update_order(def.response.OrderKey, price, quantity, cust);
|
||||
if(resp.ErrorCode == OrderError.Ok)
|
||||
{
|
||||
def.create.Price = price;
|
||||
def.create.Quantity = Convert.ToUInt32(quantity);
|
||||
}
|
||||
handle.SetResult( parse_response(resp, true) );
|
||||
}
|
||||
else
|
||||
handle.SetResult( parse_response(def.response, true) );
|
||||
}
|
||||
else
|
||||
{
|
||||
var resp = cancel_order(def.response.OrderKey);
|
||||
handle.SetResult( parse_response(resp, true) );
|
||||
}
|
||||
}
|
||||
else
|
||||
handle.SetResult( spark_orderservice_send_order_key(on_off, local_order_id, (long)(sec.Value.Id), side, user, account, price, quantity,
|
||||
order_type, order_params, time_in_force) );
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
return "Can't find security desc " + security_desc;
|
||||
handle.SetException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,723 @@
|
||||
/*
|
||||
[The "BSD licence"]
|
||||
Copyright (c) 2013 Sam Harwell
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use
|
||||
in source and binary forms, with or without
|
||||
modification, are permitted provided that the
|
||||
following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form
|
||||
must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the
|
||||
author may not be used to endorse or promote products
|
||||
derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** C 2011 grammar built from the C11 Spec */
|
||||
grammar C;
|
||||
|
||||
primaryExpression:
|
||||
Identifier
|
||||
| Constant
|
||||
| StringLiteral+
|
||||
| '(' expression ')'
|
||||
| genericSelection
|
||||
| '__extension__'? '(' compoundStatement ')' // Blocks (GCC extension)
|
||||
| '__builtin_va_arg' '(' unaryExpression ',' typeName ')'
|
||||
| '__builtin_offsetof' '(' typeName ',' unaryExpression ')';
|
||||
|
||||
genericSelection:
|
||||
'_Generic' '(' assignmentExpression ',' genericAssocList ')';
|
||||
|
||||
genericAssocList:
|
||||
genericAssociation
|
||||
| genericAssocList ',' genericAssociation;
|
||||
|
||||
genericAssociation:
|
||||
typeName ':' assignmentExpression
|
||||
| 'default' ':' assignmentExpression;
|
||||
|
||||
postfixExpression:
|
||||
primaryExpression
|
||||
| postfixExpression '[' expression ']'
|
||||
| postfixExpression '(' argumentExpressionList? ')'
|
||||
| postfixExpression '.' Identifier
|
||||
| postfixExpression '->' Identifier
|
||||
| postfixExpression '++'
|
||||
| postfixExpression '--'
|
||||
| '(' typeName ')' '{' initializerList '}'
|
||||
| '(' typeName ')' '{' initializerList ',' '}'
|
||||
| '__extension__' '(' typeName ')' '{' initializerList '}'
|
||||
| '__extension__' '(' typeName ')' '{' initializerList ',' '}';
|
||||
|
||||
argumentExpressionList:
|
||||
assignmentExpression
|
||||
| argumentExpressionList ',' assignmentExpression;
|
||||
|
||||
unaryExpression:
|
||||
postfixExpression
|
||||
| '++' unaryExpression
|
||||
| '--' unaryExpression
|
||||
| unaryOperator castExpression
|
||||
| 'sizeof' unaryExpression
|
||||
| 'sizeof' '(' typeName ')'
|
||||
| '_Alignof' '(' typeName ')'
|
||||
| '&&' Identifier ; // GCC extension address of label
|
||||
|
||||
unaryOperator: '&' | '*' | '+' | '-' | '~' | '!';
|
||||
|
||||
castExpression:
|
||||
'(' typeName ')' castExpression
|
||||
| '__extension__' '(' typeName ')' castExpression
|
||||
| unaryExpression
|
||||
| DigitSequence ; // for
|
||||
|
||||
multiplicativeExpression:
|
||||
castExpression
|
||||
| multiplicativeExpression '*' castExpression
|
||||
| multiplicativeExpression '/' castExpression
|
||||
| multiplicativeExpression '%' castExpression;
|
||||
|
||||
additiveExpression:
|
||||
multiplicativeExpression
|
||||
| additiveExpression '+' multiplicativeExpression
|
||||
| additiveExpression '-' multiplicativeExpression;
|
||||
|
||||
shiftExpression:
|
||||
additiveExpression
|
||||
| shiftExpression '<<' additiveExpression
|
||||
| shiftExpression '>>' additiveExpression;
|
||||
|
||||
relationalExpression:
|
||||
shiftExpression
|
||||
| relationalExpression '<' shiftExpression
|
||||
| relationalExpression '>' shiftExpression
|
||||
| relationalExpression '<=' shiftExpression
|
||||
| relationalExpression '>=' shiftExpression;
|
||||
|
||||
equalityExpression:
|
||||
relationalExpression
|
||||
| equalityExpression '==' relationalExpression
|
||||
| equalityExpression '!=' relationalExpression;
|
||||
|
||||
andExpression:
|
||||
equalityExpression
|
||||
| andExpression '&' equalityExpression;
|
||||
|
||||
exclusiveOrExpression:
|
||||
andExpression
|
||||
| exclusiveOrExpression '^' andExpression;
|
||||
|
||||
inclusiveOrExpression:
|
||||
exclusiveOrExpression
|
||||
| inclusiveOrExpression '|' exclusiveOrExpression;
|
||||
|
||||
logicalAndExpression:
|
||||
inclusiveOrExpression
|
||||
| logicalAndExpression '&&' inclusiveOrExpression;
|
||||
|
||||
logicalOrExpression:
|
||||
logicalAndExpression
|
||||
| logicalOrExpression '||' logicalAndExpression;
|
||||
|
||||
conditionalExpression:
|
||||
logicalOrExpression (
|
||||
'?' expression ':' conditionalExpression
|
||||
)?;
|
||||
|
||||
assignmentExpression:
|
||||
conditionalExpression
|
||||
| unaryExpression assignmentOperator assignmentExpression
|
||||
| DigitSequence ; // for
|
||||
|
||||
assignmentOperator:
|
||||
'='
|
||||
| '*='
|
||||
| '/='
|
||||
| '%='
|
||||
| '+='
|
||||
| '-='
|
||||
| '<<='
|
||||
| '>>='
|
||||
| '&='
|
||||
| '^='
|
||||
| '|=';
|
||||
|
||||
expression:
|
||||
assignmentExpression
|
||||
| expression ',' assignmentExpression;
|
||||
|
||||
constantExpression: conditionalExpression;
|
||||
|
||||
declaration:
|
||||
declarationSpecifiers initDeclaratorList ';'
|
||||
| declarationSpecifiers ';'
|
||||
| staticAssertDeclaration;
|
||||
|
||||
declarationSpecifiers: declarationSpecifier+;
|
||||
|
||||
declarationSpecifiers2: declarationSpecifier+;
|
||||
|
||||
declarationSpecifier:
|
||||
storageClassSpecifier
|
||||
| typeSpecifier
|
||||
| typeQualifier
|
||||
| functionSpecifier
|
||||
| alignmentSpecifier;
|
||||
|
||||
initDeclaratorList:
|
||||
initDeclarator
|
||||
| initDeclaratorList ',' initDeclarator;
|
||||
|
||||
initDeclarator: declarator | declarator '=' initializer;
|
||||
|
||||
storageClassSpecifier:
|
||||
'typedef'
|
||||
| 'extern'
|
||||
| 'static'
|
||||
| '_Thread_local'
|
||||
| 'auto'
|
||||
| 'register';
|
||||
|
||||
typeSpecifier: (
|
||||
'void'
|
||||
| 'char'
|
||||
| 'short'
|
||||
| 'int'
|
||||
| 'long'
|
||||
| 'float'
|
||||
| 'double'
|
||||
| 'signed'
|
||||
| 'unsigned'
|
||||
| '_Bool'
|
||||
| '_Complex'
|
||||
| '__m128'
|
||||
| '__m128d'
|
||||
| '__m128i'
|
||||
)
|
||||
| '__extension__' '(' ('__m128' | '__m128d' | '__m128i') ')'
|
||||
| atomicTypeSpecifier
|
||||
| structOrUnionSpecifier
|
||||
| enumSpecifier
|
||||
| typedefName
|
||||
| '__typeof__' '(' constantExpression ')' // GCC extension
|
||||
| typeSpecifier pointer;
|
||||
|
||||
structOrUnionSpecifier:
|
||||
structOrUnion Identifier? '{' structDeclarationList '}'
|
||||
| structOrUnion Identifier;
|
||||
|
||||
structOrUnion: 'struct' | 'union';
|
||||
|
||||
structDeclarationList:
|
||||
structDeclaration
|
||||
| structDeclarationList structDeclaration;
|
||||
|
||||
structDeclaration:
|
||||
specifierQualifierList structDeclaratorList? ';'
|
||||
| staticAssertDeclaration;
|
||||
|
||||
specifierQualifierList:
|
||||
typeSpecifier specifierQualifierList?
|
||||
| typeQualifier specifierQualifierList?;
|
||||
|
||||
structDeclaratorList:
|
||||
structDeclarator
|
||||
| structDeclaratorList ',' structDeclarator;
|
||||
|
||||
structDeclarator:
|
||||
declarator
|
||||
| declarator? ':' constantExpression;
|
||||
|
||||
enumSpecifier:
|
||||
'enum' Identifier? '{' enumeratorList '}'
|
||||
| 'enum' Identifier? '{' enumeratorList ',' '}'
|
||||
| 'enum' Identifier;
|
||||
|
||||
enumeratorList: enumerator | enumeratorList ',' enumerator;
|
||||
|
||||
enumerator:
|
||||
enumerationConstant
|
||||
| enumerationConstant '=' constantExpression;
|
||||
|
||||
enumerationConstant: Identifier;
|
||||
|
||||
atomicTypeSpecifier: '_Atomic' '(' typeName ')';
|
||||
|
||||
typeQualifier: 'const' | 'restrict' | 'volatile' | '_Atomic';
|
||||
|
||||
functionSpecifier: (
|
||||
'inline'
|
||||
| '_Noreturn'
|
||||
| '__inline__' // GCC extension
|
||||
| '__stdcall'
|
||||
)
|
||||
| gccAttributeSpecifier
|
||||
| '__declspec' '(' Identifier ')';
|
||||
|
||||
alignmentSpecifier:
|
||||
'_Alignas' '(' typeName ')'
|
||||
| '_Alignas' '(' constantExpression ')';
|
||||
|
||||
declarator: pointer? directDeclarator gccDeclaratorExtension*;
|
||||
|
||||
directDeclarator:
|
||||
Identifier
|
||||
| '(' declarator ')'
|
||||
| directDeclarator '[' typeQualifierList? assignmentExpression? ']'
|
||||
| directDeclarator '[' 'static' typeQualifierList? assignmentExpression ']'
|
||||
| directDeclarator '[' typeQualifierList 'static' assignmentExpression ']'
|
||||
| directDeclarator '[' typeQualifierList? '*' ']'
|
||||
| directDeclarator '(' parameterTypeList ')'
|
||||
| directDeclarator '(' identifierList? ')'
|
||||
| Identifier ':' DigitSequence // bit field
|
||||
| '(' typeSpecifier? pointer directDeclarator ')' ; // function pointer like: (__cdecl *f)
|
||||
|
||||
gccDeclaratorExtension:
|
||||
'__asm' '(' StringLiteral+ ')'
|
||||
| gccAttributeSpecifier;
|
||||
|
||||
gccAttributeSpecifier:
|
||||
'__attribute__' '(' '(' gccAttributeList ')' ')';
|
||||
|
||||
gccAttributeList:
|
||||
gccAttribute (',' gccAttribute)*
|
||||
| ; // empty
|
||||
|
||||
gccAttribute:
|
||||
~(
|
||||
','
|
||||
| '('
|
||||
| ')'
|
||||
) // relaxed def for "identifier or reserved word"
|
||||
('(' argumentExpressionList? ')')?
|
||||
| ; // empty
|
||||
|
||||
nestedParenthesesBlock: (
|
||||
~('(' | ')')
|
||||
| '(' nestedParenthesesBlock ')'
|
||||
)*;
|
||||
|
||||
pointer:
|
||||
'*' typeQualifierList?
|
||||
| '*' typeQualifierList? pointer
|
||||
| '^' typeQualifierList? // Blocks language extension
|
||||
| '^' typeQualifierList? pointer ; // Blocks language extension
|
||||
|
||||
typeQualifierList:
|
||||
typeQualifier
|
||||
| typeQualifierList typeQualifier;
|
||||
|
||||
parameterTypeList: parameterList | parameterList ',' '...';
|
||||
|
||||
parameterList:
|
||||
parameterDeclaration
|
||||
| parameterList ',' parameterDeclaration;
|
||||
|
||||
parameterDeclaration:
|
||||
declarationSpecifiers declarator
|
||||
| declarationSpecifiers2 abstractDeclarator?;
|
||||
|
||||
identifierList: Identifier | identifierList ',' Identifier;
|
||||
|
||||
typeName: specifierQualifierList abstractDeclarator?;
|
||||
|
||||
abstractDeclarator:
|
||||
pointer
|
||||
| pointer? directAbstractDeclarator gccDeclaratorExtension*;
|
||||
|
||||
directAbstractDeclarator:
|
||||
'(' abstractDeclarator ')' gccDeclaratorExtension*
|
||||
| '[' typeQualifierList? assignmentExpression? ']'
|
||||
| '[' 'static' typeQualifierList? assignmentExpression ']'
|
||||
| '[' typeQualifierList 'static' assignmentExpression ']'
|
||||
| '[' '*' ']'
|
||||
| '(' parameterTypeList? ')' gccDeclaratorExtension*
|
||||
| directAbstractDeclarator '[' typeQualifierList? assignmentExpression? ']'
|
||||
| directAbstractDeclarator '[' 'static' typeQualifierList? assignmentExpression ']'
|
||||
| directAbstractDeclarator '[' typeQualifierList 'static' assignmentExpression ']'
|
||||
| directAbstractDeclarator '[' '*' ']'
|
||||
| directAbstractDeclarator '(' parameterTypeList? ')' gccDeclaratorExtension*;
|
||||
|
||||
typedefName: Identifier;
|
||||
|
||||
initializer:
|
||||
assignmentExpression
|
||||
| '{' initializerList '}'
|
||||
| '{' initializerList ',' '}';
|
||||
|
||||
initializerList:
|
||||
designation? initializer
|
||||
| initializerList ',' designation? initializer;
|
||||
|
||||
designation: designatorList '=';
|
||||
|
||||
designatorList: designator | designatorList designator;
|
||||
|
||||
designator: '[' constantExpression ']' | '.' Identifier;
|
||||
|
||||
staticAssertDeclaration:
|
||||
'_Static_assert' '(' constantExpression ',' StringLiteral+ ')' ';';
|
||||
|
||||
statement:
|
||||
labeledStatement
|
||||
| compoundStatement
|
||||
| expressionStatement
|
||||
| selectionStatement
|
||||
| iterationStatement
|
||||
| jumpStatement
|
||||
| ('__asm' | '__asm__') ('volatile' | '__volatile__') '(' (
|
||||
logicalOrExpression (',' logicalOrExpression)*
|
||||
)? (':' (logicalOrExpression (',' logicalOrExpression)*)?)* ')' ';';
|
||||
|
||||
labeledStatement:
|
||||
Identifier ':' statement
|
||||
| 'case' constantExpression ':' statement
|
||||
| 'default' ':' statement;
|
||||
|
||||
compoundStatement: '{' blockItemList? '}';
|
||||
|
||||
blockItemList: blockItem | blockItemList blockItem;
|
||||
|
||||
blockItem: statement | declaration;
|
||||
|
||||
expressionStatement: expression? ';';
|
||||
|
||||
selectionStatement:
|
||||
'if' '(' expression ')' statement ('else' statement)?
|
||||
| 'switch' '(' expression ')' statement;
|
||||
|
||||
iterationStatement:
|
||||
While '(' expression ')' statement
|
||||
| Do statement While '(' expression ')' ';'
|
||||
| For '(' forCondition ')' statement;
|
||||
|
||||
// | 'for' '(' expression? ';' expression? ';' forUpdate? ')' statement | For '(' declaration
|
||||
// expression? ';' expression? ')' statement
|
||||
|
||||
forCondition:
|
||||
forDeclaration ';' forExpression? ';' forExpression?
|
||||
| expression? ';' forExpression? ';' forExpression?;
|
||||
|
||||
forDeclaration:
|
||||
declarationSpecifiers initDeclaratorList
|
||||
| declarationSpecifiers;
|
||||
|
||||
forExpression:
|
||||
assignmentExpression
|
||||
| forExpression ',' assignmentExpression;
|
||||
|
||||
jumpStatement:
|
||||
'goto' Identifier ';'
|
||||
| 'continue' ';'
|
||||
| 'break' ';'
|
||||
| 'return' expression? ';'
|
||||
| 'goto' unaryExpression ';' ; // GCC extension
|
||||
|
||||
compilationUnit: translationUnit? EOF;
|
||||
|
||||
translationUnit:
|
||||
externalDeclaration
|
||||
| translationUnit externalDeclaration;
|
||||
|
||||
externalDeclaration:
|
||||
functionDefinition
|
||||
| declaration
|
||||
| ';' ; // stray ;
|
||||
|
||||
functionDefinition:
|
||||
declarationSpecifiers? declarator declarationList? compoundStatement;
|
||||
|
||||
declarationList: declaration | declarationList declaration;
|
||||
|
||||
Auto: 'auto';
|
||||
Break: 'break';
|
||||
Case: 'case';
|
||||
Char: 'char';
|
||||
Const: 'const';
|
||||
Continue: 'continue';
|
||||
Default: 'default';
|
||||
Do: 'do';
|
||||
Double: 'double';
|
||||
Else: 'else';
|
||||
Enum: 'enum';
|
||||
Extern: 'extern';
|
||||
Float: 'float';
|
||||
For: 'for';
|
||||
Goto: 'goto';
|
||||
If: 'if';
|
||||
Inline: 'inline';
|
||||
Int: 'int';
|
||||
Long: 'long';
|
||||
Register: 'register';
|
||||
Restrict: 'restrict';
|
||||
Return: 'return';
|
||||
Short: 'short';
|
||||
Signed: 'signed';
|
||||
Sizeof: 'sizeof';
|
||||
Static: 'static';
|
||||
Struct: 'struct';
|
||||
Switch: 'switch';
|
||||
Typedef: 'typedef';
|
||||
Union: 'union';
|
||||
Unsigned: 'unsigned';
|
||||
Void: 'void';
|
||||
Volatile: 'volatile';
|
||||
While: 'while';
|
||||
|
||||
Alignas: '_Alignas';
|
||||
Alignof: '_Alignof';
|
||||
Atomic: '_Atomic';
|
||||
Bool: '_Bool';
|
||||
Complex: '_Complex';
|
||||
Generic: '_Generic';
|
||||
Imaginary: '_Imaginary';
|
||||
Noreturn: '_Noreturn';
|
||||
StaticAssert: '_Static_assert';
|
||||
ThreadLocal: '_Thread_local';
|
||||
|
||||
LeftParen: '(';
|
||||
RightParen: ')';
|
||||
LeftBracket: '[';
|
||||
RightBracket: ']';
|
||||
LeftBrace: '{';
|
||||
RightBrace: '}';
|
||||
|
||||
Less: '<';
|
||||
LessEqual: '<=';
|
||||
Greater: '>';
|
||||
GreaterEqual: '>=';
|
||||
LeftShift: '<<';
|
||||
RightShift: '>>';
|
||||
|
||||
Plus: '+';
|
||||
PlusPlus: '++';
|
||||
Minus: '-';
|
||||
MinusMinus: '--';
|
||||
Star: '*';
|
||||
Div: '/';
|
||||
Mod: '%';
|
||||
|
||||
And: '&';
|
||||
Or: '|';
|
||||
AndAnd: '&&';
|
||||
OrOr: '||';
|
||||
Caret: '^';
|
||||
Not: '!';
|
||||
Tilde: '~';
|
||||
|
||||
Question: '?';
|
||||
Colon: ':';
|
||||
Semi: ';';
|
||||
Comma: ',';
|
||||
|
||||
Assign: '=';
|
||||
// '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '&=' | '^=' | '|='
|
||||
StarAssign: '*=';
|
||||
DivAssign: '/=';
|
||||
ModAssign: '%=';
|
||||
PlusAssign: '+=';
|
||||
MinusAssign: '-=';
|
||||
LeftShiftAssign: '<<=';
|
||||
RightShiftAssign: '>>=';
|
||||
AndAssign: '&=';
|
||||
XorAssign: '^=';
|
||||
OrAssign: '|=';
|
||||
|
||||
Equal: '==';
|
||||
NotEqual: '!=';
|
||||
|
||||
Arrow: '->';
|
||||
Dot: '.';
|
||||
Ellipsis: '...';
|
||||
|
||||
Identifier: IdentifierNondigit ( IdentifierNondigit | Digit)*;
|
||||
|
||||
fragment IdentifierNondigit:
|
||||
Nondigit
|
||||
| UniversalCharacterName
|
||||
; //| // other implementation-defined characters...
|
||||
|
||||
fragment Nondigit: [a-zA-Z_];
|
||||
|
||||
fragment Digit: [0-9];
|
||||
|
||||
fragment UniversalCharacterName:
|
||||
'\\u' HexQuad
|
||||
| '\\U' HexQuad HexQuad;
|
||||
|
||||
fragment HexQuad:
|
||||
HexadecimalDigit HexadecimalDigit HexadecimalDigit HexadecimalDigit;
|
||||
|
||||
Constant:
|
||||
IntegerConstant
|
||||
| FloatingConstant
|
||||
//| EnumerationConstant
|
||||
| CharacterConstant;
|
||||
|
||||
fragment IntegerConstant:
|
||||
DecimalConstant IntegerSuffix?
|
||||
| OctalConstant IntegerSuffix?
|
||||
| HexadecimalConstant IntegerSuffix?
|
||||
| BinaryConstant;
|
||||
|
||||
fragment BinaryConstant: '0' [bB] [0-1]+;
|
||||
|
||||
fragment DecimalConstant: NonzeroDigit Digit*;
|
||||
|
||||
fragment OctalConstant: '0' OctalDigit*;
|
||||
|
||||
fragment HexadecimalConstant:
|
||||
HexadecimalPrefix HexadecimalDigit+;
|
||||
|
||||
fragment HexadecimalPrefix: '0' [xX];
|
||||
|
||||
fragment NonzeroDigit: [1-9];
|
||||
|
||||
fragment OctalDigit: [0-7];
|
||||
|
||||
fragment HexadecimalDigit: [0-9a-fA-F];
|
||||
|
||||
fragment IntegerSuffix:
|
||||
UnsignedSuffix LongSuffix?
|
||||
| UnsignedSuffix LongLongSuffix
|
||||
| LongSuffix UnsignedSuffix?
|
||||
| LongLongSuffix UnsignedSuffix?;
|
||||
|
||||
fragment UnsignedSuffix: [uU];
|
||||
|
||||
fragment LongSuffix: [lL];
|
||||
|
||||
fragment LongLongSuffix: 'll' | 'LL';
|
||||
|
||||
fragment FloatingConstant:
|
||||
DecimalFloatingConstant
|
||||
| HexadecimalFloatingConstant;
|
||||
|
||||
fragment DecimalFloatingConstant:
|
||||
FractionalConstant ExponentPart? FloatingSuffix?
|
||||
| DigitSequence ExponentPart FloatingSuffix?;
|
||||
|
||||
fragment HexadecimalFloatingConstant:
|
||||
HexadecimalPrefix HexadecimalFractionalConstant BinaryExponentPart FloatingSuffix?
|
||||
| HexadecimalPrefix HexadecimalDigitSequence BinaryExponentPart FloatingSuffix?;
|
||||
|
||||
fragment FractionalConstant:
|
||||
DigitSequence? '.' DigitSequence
|
||||
| DigitSequence '.';
|
||||
|
||||
fragment ExponentPart:
|
||||
'e' Sign? DigitSequence
|
||||
| 'E' Sign? DigitSequence;
|
||||
|
||||
fragment Sign: '+' | '-';
|
||||
|
||||
DigitSequence: Digit+;
|
||||
|
||||
fragment HexadecimalFractionalConstant:
|
||||
HexadecimalDigitSequence? '.' HexadecimalDigitSequence
|
||||
| HexadecimalDigitSequence '.';
|
||||
|
||||
fragment BinaryExponentPart:
|
||||
'p' Sign? DigitSequence
|
||||
| 'P' Sign? DigitSequence;
|
||||
|
||||
fragment HexadecimalDigitSequence: HexadecimalDigit+;
|
||||
|
||||
fragment FloatingSuffix: 'f' | 'l' | 'F' | 'L';
|
||||
|
||||
fragment CharacterConstant:
|
||||
'\'' CCharSequence '\''
|
||||
| 'L\'' CCharSequence '\''
|
||||
| 'u\'' CCharSequence '\''
|
||||
| 'U\'' CCharSequence '\'';
|
||||
|
||||
fragment CCharSequence: CChar+;
|
||||
|
||||
fragment CChar: ~['\\\r\n] | EscapeSequence;
|
||||
|
||||
fragment EscapeSequence:
|
||||
SimpleEscapeSequence
|
||||
| OctalEscapeSequence
|
||||
| HexadecimalEscapeSequence
|
||||
| UniversalCharacterName;
|
||||
|
||||
fragment SimpleEscapeSequence: '\\' ['"?abfnrtv\\];
|
||||
|
||||
fragment OctalEscapeSequence:
|
||||
'\\' OctalDigit
|
||||
| '\\' OctalDigit OctalDigit
|
||||
| '\\' OctalDigit OctalDigit OctalDigit;
|
||||
|
||||
fragment HexadecimalEscapeSequence: '\\x' HexadecimalDigit+;
|
||||
|
||||
StringLiteral: EncodingPrefix? '"' SCharSequence? '"';
|
||||
|
||||
fragment EncodingPrefix: 'u8' | 'u' | 'U' | 'L';
|
||||
|
||||
fragment SCharSequence: SChar+;
|
||||
|
||||
fragment SChar:
|
||||
~["\\\r\n]
|
||||
| EscapeSequence
|
||||
| '\\\n' // Added line
|
||||
| '\\\r\n' ; // Added line
|
||||
|
||||
ComplexDefine: '#' Whitespace? 'define' ~[#]* -> skip;
|
||||
|
||||
IncludeDirective:
|
||||
'#' Whitespace? 'include' Whitespace? (
|
||||
('"' ~[\r\n]* '"')
|
||||
| ('<' ~[\r\n]* '>')
|
||||
) Whitespace? Newline -> skip;
|
||||
|
||||
// ignore the following asm blocks:
|
||||
/*
|
||||
asm
|
||||
{
|
||||
mfspr x, 286;
|
||||
}
|
||||
*/
|
||||
AsmBlock: 'asm' ~'{'* '{' ~'}'* '}' -> skip;
|
||||
|
||||
// ignore the lines generated by c preprocessor sample line : '#line 1 "/home/dm/files/dk1.h" 1'
|
||||
LineAfterPreprocessing: '#line' Whitespace* ~[\r\n]* -> skip;
|
||||
|
||||
LineDirective:
|
||||
'#' Whitespace? DecimalConstant Whitespace? StringLiteral ~[\r\n]* -> skip;
|
||||
|
||||
PragmaDirective:
|
||||
'#' Whitespace? 'pragma' Whitespace ~[\r\n]* -> skip;
|
||||
|
||||
Whitespace: [ \t]+ -> skip;
|
||||
|
||||
Newline: ( '\r' '\n'? | '\n') -> skip;
|
||||
|
||||
BlockComment: '/*' .*? '*/' -> skip;
|
||||
|
||||
LineComment: '//' ~[\r\n]* -> skip;
|
||||
@@ -0,0 +1,93 @@
|
||||
grammar ExcelFormula;
|
||||
|
||||
/* Parser rules */
|
||||
main: '=' expr EOF;
|
||||
expr:
|
||||
sign = ('+' | '-') expr # Unary
|
||||
| expr '%' # Percentage
|
||||
| <assoc=right> expr '^' expr # Pow
|
||||
| expr op = ('*' | '/') expr # MulDiv
|
||||
| expr op = ('+' | '-') expr # AddSub
|
||||
| expr '&' expr # Concatenate
|
||||
| expr op = ('=' | '<>') expr # Equality
|
||||
| expr op = ('<' | '>' | '<=' | '>=') expr # Relational
|
||||
| '(' expr ')' # Parenthesis
|
||||
| name = NAME '(' (expr (',' expr)*)? ')' # Function
|
||||
| rng # Range
|
||||
| rng (',' rng)+ # RangeList
|
||||
| ref # Cell
|
||||
| ref (',' ref)+ # CellList
|
||||
| BOOLEAN # Boolean
|
||||
| NUMBER # Number
|
||||
| NAME # Name
|
||||
| STRING # String
|
||||
| ERROR # Error;
|
||||
ref: sheet = SHEET? colRow = COL_ROW # CellRef;
|
||||
rng: start = ref ':' end = COL_ROW # RangeRef;
|
||||
|
||||
/* Lexer rules */
|
||||
COL_ROW: '$'? COL '$'? DIGIT+;
|
||||
STRING: '"' ~["]* '"';
|
||||
BOOLEAN: T R U E | F A L S E;
|
||||
NUMBER: FLOAT | DIGIT+;
|
||||
NAME: [a-zA-Z_\\] ([0-9a-zA-Z_.\\])*;
|
||||
ERROR:
|
||||
E_NULL
|
||||
| E_DIV0
|
||||
| E_VALUE
|
||||
| E_NAME
|
||||
| E_NUM
|
||||
| E_NA
|
||||
| E_REF;
|
||||
E_NULL: '#' N U L L '!';
|
||||
E_DIV0: '#' D I V '/0';
|
||||
E_VALUE: '#' V A L U E '!';
|
||||
E_NAME: '#' N A M E '?';
|
||||
E_NUM: '#' N U M '!';
|
||||
E_NA: '#' N '/' A;
|
||||
E_REF: '#' R E F '!';
|
||||
SHEET:
|
||||
SHEET_C+ '!'
|
||||
| SHEET_C+ ':'+ SHEET_C+ '!'
|
||||
| '\'' Q_SHEET_C+ '\'!'
|
||||
| '\'' Q_SHEET_C+ ':' Q_SHEET_C+ '\'!';
|
||||
SPACE: (' ' | '\t' | '\r' | '\n' | '\u000C') -> skip;
|
||||
INVALID: .;
|
||||
|
||||
/* Lexer fragments */
|
||||
fragment COL: [a-zA-Z]? [a-zA-Z]? [a-zA-Z]; // Max col is XFD
|
||||
fragment DIGIT: [0-9];
|
||||
fragment FLOAT: DIGIT+ '.' DIGIT+ EXPONENT? | DIGIT+ EXPONENT;
|
||||
fragment EXPONENT: E ('+'? | '-') DIGIT+;
|
||||
fragment FILE: '[' DIGIT+ ']' | '[' FILENAME_C+ ']';
|
||||
fragment WIN_PATH: [A-Z] ':\\' (FILENAME_C+ '\\')*;
|
||||
fragment FILENAME_C: ~["*[\]\\:/?<>|];
|
||||
fragment SHEET_C: ~['*[\]\\:/?<>();{}#"=&+^%,!\u2229\u222A \-];
|
||||
fragment Q_SHEET_C: ~['*[\]\\:/?];
|
||||
|
||||
fragment A: ('a' | 'A');
|
||||
fragment B: ('b' | 'B');
|
||||
fragment C: ('c' | 'C');
|
||||
fragment D: ('d' | 'D');
|
||||
fragment E: ('e' | 'E');
|
||||
fragment F: ('f' | 'F');
|
||||
fragment G: ('g' | 'G');
|
||||
fragment H: ('h' | 'H');
|
||||
fragment I: ('i' | 'I');
|
||||
fragment J: ('j' | 'J');
|
||||
fragment K: ('k' | 'K');
|
||||
fragment L: ('l' | 'L');
|
||||
fragment M: ('m' | 'M');
|
||||
fragment N: ('n' | 'N');
|
||||
fragment O: ('o' | 'O');
|
||||
fragment P: ('p' | 'P');
|
||||
fragment Q: ('q' | 'Q');
|
||||
fragment R: ('r' | 'R');
|
||||
fragment S: ('s' | 'S');
|
||||
fragment T: ('t' | 'T');
|
||||
fragment U: ('u' | 'U');
|
||||
fragment V: ('v' | 'V');
|
||||
fragment W: ('w' | 'W');
|
||||
fragment X: ('x' | 'X');
|
||||
fragment Y: ('y' | 'Y');
|
||||
fragment Z: ('z' | 'Z');
|
||||
Binary file not shown.
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="ExcelDna.AddIn" version="0.34.6" targetFramework="net471" />
|
||||
<package id="ExcelDna.Integration" version="0.34.6" targetFramework="net471" />
|
||||
</packages>
|
||||
@@ -13,6 +13,8 @@ namespace bts.excel.rtd
|
||||
class JsonFormatter : IExcelFormatter
|
||||
{
|
||||
private object _data;
|
||||
static Google.Protobuf.JsonFormatter _protoformatter =
|
||||
new Google.Protobuf.JsonFormatter(Google.Protobuf.JsonFormatter.Settings.Default.WithFormatDefaultValues(true));
|
||||
public JsonFormatter(object data)
|
||||
{
|
||||
_data = data;
|
||||
@@ -81,7 +83,7 @@ namespace bts.excel.rtd
|
||||
{
|
||||
string json = null;
|
||||
if(data is IMessage imsg)
|
||||
json = Google.Protobuf.JsonFormatter.Default.Format(imsg);
|
||||
json = _protoformatter.Format(imsg);
|
||||
else
|
||||
json = JsonConvert.SerializeObject(data);
|
||||
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Platforms>x64</Platforms>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<NoWarn>0108,3021</NoWarn>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<Version>1.0.0</Version>
|
||||
<Antlr4UseCSharpGenerator>true</Antlr4UseCSharpGenerator>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -28,10 +30,21 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="System.Linq.Dynamic" Version="1.0.7" />
|
||||
|
||||
<PackageReference Include="ExcelDna.AddIn" Version="0.34.6" />
|
||||
<PackageReference Include="ExcelDna.Integration" Version="0.34.6" />
|
||||
<PackageReference Include="ExcelDna.AddIn" Version="1.0.0" />
|
||||
<PackageReference Include="ExcelDna.Integration" Version="1.0.0" />
|
||||
<PackageReference Include="ExcelDna.IntelliSense" Version="1.1.0" />
|
||||
<PackageReference Include="ExcelDna.Registration" Version="0.34.7" />
|
||||
<PackageReference Include="ExcelDna.Interop" Version="14.0.1" />
|
||||
|
||||
<PackageReference Include="Antlr4" Version="4.6.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Antlr4.Runtime" Version="4.6.6" />
|
||||
<PackageReference Include="Antlr4.CodeGenerator" Version="4.6.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -50,17 +63,28 @@
|
||||
<None Include="spark_excel-AddIn.dna" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\ExcelDna.Build.props" />
|
||||
<None Include="$(IntermediateOutputPath)\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../spark_api/spark_api.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<!-- <ItemGroup>
|
||||
<Antlr4 Include="grammar/ExcelFormula.g4">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CustomToolNamespace>bts.excel.grammar</CustomToolNamespace>
|
||||
<Listener>True</Listener>
|
||||
<Visitor>True</Visitor>
|
||||
</Antlr4>
|
||||
</ItemGroup> -->
|
||||
|
||||
<ItemGroup>
|
||||
<Proto3Excel Include="**/*.proto" />
|
||||
<Proto3Excel Include="../../lib/api/services/*.proto" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="spark_excel_example_2018.xlsm"><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></None>
|
||||
</ItemGroup>
|
||||
@@ -74,9 +98,9 @@
|
||||
-->
|
||||
|
||||
<!-- <Import Project="$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets" Condition="Exists('$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets')" /> -->
|
||||
<Import Project="$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets"/>
|
||||
<!-- <Import Project="$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets"/>
|
||||
<Target Name="EnsureExcelDnaTargetsImported" BeforeTargets="BeforeBuild" Condition="'$(ExcelDnaTargetsImported)' == ''">
|
||||
<Error Condition="!Exists('$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets') And ('$(RunExcelDnaBuild)' != '' And $(RunExcelDnaBuild))" Text="You are trying to build with ExcelDna, but the NuGet targets file that ExcelDna depends on is not available on this computer. This is probably because the ExcelDna package has not been committed to source control, or NuGet Package Restore is not enabled. Please enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
|
||||
<Error Condition="Exists('$(NuGetPackageRoot)\ExcelDna.AddIn\0.34.6\tools\ExcelDna.AddIn.targets') And ('$(RunExcelDnaBuild)' != '' And $(RunExcelDnaBuild))" Text="ExcelDna cannot be run because NuGet packages were restored prior to the build running, and the targets file was unavailable when the build started. Please build the project again to include these packages in the build. You may also need to make sure that your build server does not delete packages prior to each build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
|
||||
</Target>
|
||||
</Target> -->
|
||||
</Project>
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using ExcelDna.Integration;
|
||||
using bts.api;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
namespace bts.excel
|
||||
{
|
||||
public class AlgoHelpers
|
||||
{
|
||||
static ConcurrentDictionary<string, object> _cache = new ConcurrentDictionary<string, object>();
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_register_parameter([ExcelArgument(AllowReference = true)] object parameter)
|
||||
{
|
||||
ExcelReference par = (ExcelReference)parameter;
|
||||
string sheetname = (string)XlCall.Excel(XlCall.xlSheetNm, par);
|
||||
var cellref = new CellRef(sheetname, (par.ColumnFirst + 1, par.RowFirst + 1));
|
||||
string r1c1 = cellref.r1_c1();
|
||||
var cellname = XlCall.Excel(XlCall.xlfGetDef, r1c1, sheetname);
|
||||
if (!(cellname is ExcelError))
|
||||
{
|
||||
GeneratorVisitor.register_algo_parameter(sheetname, cellname.ToString());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return $"{ErrorValue.Name}, please make algo parameters named ranges";
|
||||
|
||||
// ExcelReference caller = (ExcelReference)XlCall.Excel(XlCall.xlfCaller);
|
||||
// var refr = $"R{caller.RowFirst+1}C{caller.ColumnFirst+1}:R{caller.RowLast+1}C{caller.ColumnLast+1}";
|
||||
// var reff = new CellRef(sheetname, (caller.ColumnFirst + 1, caller.RowFirst + 1));
|
||||
// refr = reff.r1_c1();
|
||||
// bool hidden = false; bool local = true;
|
||||
// var res = XlCall.Excel(XlCall.xlcDefineName, name, "=" + refr, Type.Missing, Type.Missing, hidden, Type.Missing, local);
|
||||
}
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_take_snapshot(object data, string tag)
|
||||
{
|
||||
object val;
|
||||
if(!_cache.TryGetValue(tag, out val))
|
||||
{
|
||||
_cache[tag] = val = data;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_get_snapshot(string tag)
|
||||
{
|
||||
_cache.TryGetValue(tag, out object val);
|
||||
return val;
|
||||
}
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public object sparkalgo_snapshot_switch(string tag, int on_off)
|
||||
{
|
||||
object val;
|
||||
if (!_cache.TryGetValue(tag, out val) && on_off > 0)
|
||||
{
|
||||
_cache[tag] = val = on_off;
|
||||
}
|
||||
return val ?? 0;
|
||||
}
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public void sparkalgo_reset_all_snapshots(object _)
|
||||
{
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
static public void sparkalgo_reset_all_snapshots_no_args()
|
||||
{
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
[ExcelFunction(IsMacroType = true)]
|
||||
static public void sparkalgo_reset_snapshot(string tag)
|
||||
{
|
||||
_cache.TryRemove(tag, out object val);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,10 @@ Commands:
|
||||
order <exchange> <security_type> <symbol> <security_desc> <signed_quantity> [<price>]
|
||||
Arguments:
|
||||
--pump=<pump_interval_in_ms=100 default>
|
||||
|
||||
Example:
|
||||
To print simulated market data to the screen:
|
||||
spark_sdk.exe md SIM FUTURE ES SIM:ESU9 --user=bts
|
||||
```
|
||||
|
||||
* Check out sample code
|
||||
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.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;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.cpp;orderman/actors/MDSupplementarySource.cpp;orderman/actors/OrderStore.cpp;orderman/actors/SODPosition.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/OrderServices.cpp;orderman/actors/order_manLine truncated
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.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;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;mercury/actors/IPollableActor.cpp;mercury/broker/Broker.cpp;mercury/broker/BrokerProxy.cpp;mercury/broker/ILocalBroker.cpp;mercury/broker/IRemoteBroker.cpp;mercury/common/mercury_common.cpp;mercury/events/TimerEvent.cpp;mercury/executor/Executor.cpp;mercury/topics/Topic.cpp;metrics/metrics.cpp;metrics/system_metrics.cpp;orderman/actors/AsyncFactory.cpp;orderman/actors/ExecutionGateway.cpp;orderman/actors/FillStore.cpp;orderman/actors/MDSupplementarySource.cpp;orderman/actors/OrderStore.cpp;orderman/actors/SODPosition.cpp;orderman/actors/SecuritySrc.cpp;orderman/actors/ServiceBridge.cpp;orderman/actors/order_manager/OrderBuilder.cpp;orderman/actors/order_manager/OrderMLine truncated
|
||||
@@ -1 +1 @@
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthSessionManager.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/crypto/CipherType.cpp;utils/crypto/CryptoInit.cpp;utils/crypto/Digest.cpp;utils/crypto/KeyCodeHelper.cpp;utils/crypto/PasswordCallback.cpp;utils/crypto/PrivateKey.cpp;utils/crypto/PublicKey.cpp;utils/crypto/iostreambio.cpp;utils/datetime.cpp;utils/download.cpp;utLine truncated
|
||||
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/ExchangeSimCfg.proto;config/protos/MarketData.proto;config/protos/Ports.proto;config/protos/Symbols.proto;config/protos/UI.proto;config/protos/cme_reference_data.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spreader.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOServiceServer.cpp;md/MDClient.cpp;md/MDConsumer.cpp;md/MDConsumerHandle.cpp;md/MDConsumerInterop.cpp;md/MDProducer.cpp;md/MDProducerHandle.cpp;md/MemorySegmentUtils.cpp;md/PlatformLoader.cpp;md/PriceSize.cpp;md/SecurityCache.cpp;md/SecurityDB.cpp;md/SecurityIDRecorder.cpp;md/SecurityOracle.cpp;md/SettlementRecorder.cpp;md/Shm/FixedSecurity.cpp;md/SyntheticSecurityService.cpp;md/cme/MDP3Utils.cpp;md/common.cpp;md/ice/ICEUtils.cpp;md/protos/co2.proto;md/relay/QueryServer.cpp;md/relay/RelayClient.cpp;md/relay/RelayRecorder.cpp;md/test/Co2ManagedTestHelper.cpp;md/test/Co2TestInstrument.cpp;md/test/mbo_tests.cpp;md/test/md2_tests.cpp;md/test/md_test_utils.cpp;md/test/synthetic_book_merge_tests.cpp;os/os.cpp;os/tests/os_tests.cpp;quickbus/core/LocalNode.cpp;quickbus/core/Quickbus.cpp;quickbus/core/SubscriptionID.cpp;quickbus/core/TableID.cpp;quickbus/message_model/Update.cpp;quickbus/message_model/merge_updates.cpp;quickbus/network/QBConnection.cpp;quickbus/network/QBConnectionManager.cpp;quickbus/network/endpoint_description.cpp;quickbus/network/quickbus_msgs.proto;quickbus/pubsub/TablePublisher.cpp;quickbus/pubsub/TableSubscriber.cpp;quickbus/pubsub/tests/qb_pubsub_tests.cpp;quickbus/routing/INode.cpp;quickbus/routing/NodeID.cpp;quickbus/routing/Router.cpp;quickbus/rpc/RPCClient.cpp;quickbus/rpc/RPCServer.cpp;quickbus/rpc/RPCSessionID.cpp;quickbus/rpc/rpc_utils.cpp;quickbus/rpc/tests/qb_rpc_tests.cpp;quickbus/tests/quickbus_tests.cpp;sparkproto/SparkProtos.proto;testing/catch_test_runner.cpp;utils/AuthSessionManager.cpp;utils/AuthToken.cpp;utils/Dispatcher.cpp;utils/GrowableSPSCQueue.cpp;utils/HTTPStream.cpp;utils/PBUtils.cpp;utils/Puid.cpp;utils/SecureLogon.cpp;utils/SharedMemory.cpp;utils/Sqlite_utils.cpp;utils/Uuid.cpp;utils/crypto/CipherType.cpp;utils/crypto/CryptoInit.cpp;utils/crypto/Digest.cpp;utils/crypto/KeyCodeHelper.cpp;utils/crypto/PasswordCallback.cpp;utils/crypto/PrivateKey.cpp;utils/cryptoLine truncated
|
||||
@@ -50,6 +50,7 @@ message VoidMessage {}
|
||||
message BytesMessage {
|
||||
bytes buffer = 1;
|
||||
}
|
||||
message BoolResultMessage {
|
||||
bool success = 1;
|
||||
message ResultMessage {
|
||||
bool success = 1;
|
||||
string error_message = 2;
|
||||
}
|
||||
@@ -27,7 +27,12 @@ namespace api {
|
||||
_removed_items(std::move(removed))
|
||||
{}
|
||||
|
||||
Update(const Update& other) = delete;
|
||||
Update(const Update& other) : _all_items(other._all_items),
|
||||
_added_items(other._added_items),
|
||||
_changed_items(other._changed_items),
|
||||
_removed_items(other._removed_items)
|
||||
{}
|
||||
|
||||
Update(Update&& other) :
|
||||
_all_items(std::move(other._all_items)),
|
||||
_added_items(std::move(other._added_items)),
|
||||
@@ -104,4 +109,4 @@ namespace api {
|
||||
}
|
||||
|
||||
|
||||
}}//namespace
|
||||
}}//namespace
|
||||
@@ -149,6 +149,7 @@ message BTSAccount{
|
||||
ExchangeAccountData exchange_account_data = 110;
|
||||
// extra parameters for risk enforcement
|
||||
RiskLimitEnforcement risk_limit_enforcement=120;
|
||||
AccountLossLimit account_loss_limit = 130;
|
||||
}
|
||||
|
||||
message RiskLimitEnforcement{
|
||||
@@ -190,6 +191,11 @@ message AccountExchangeConnections{
|
||||
repeated string exchange_connection_names = 20;
|
||||
}
|
||||
|
||||
message AccountLossLimit{
|
||||
bool enforce = 10;
|
||||
uint32 total_loss_limit = 20;
|
||||
// if we need per product we can add it here
|
||||
}
|
||||
/************************************************************************/
|
||||
/***************************Exchange Data********************************/
|
||||
/************************************************************************/
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
syntax="proto3";
|
||||
package bts.api.position;
|
||||
option csharp_namespace="bts.api.position";
|
||||
import "lib/api/apioptions.proto";
|
||||
import "lib/api/apicore.proto";
|
||||
import "lib/api/services/PositionService.proto";
|
||||
|
||||
message MarginResult {
|
||||
enum ResultStatus { NewValid = 0; CachedValid = 1; Error = 2; }
|
||||
|
||||
string account = 10 [(bts.api.key)=true];
|
||||
ResultStatus status = 12;
|
||||
string portfolio_id = 15;
|
||||
string settle_indicator = 20;
|
||||
string settle_qualifier = 30;
|
||||
string as_of_time = 40;
|
||||
string calculation_id = 50;
|
||||
|
||||
double opt_value = 100;
|
||||
double nonopt_value = 110;
|
||||
double maint_margin = 120;
|
||||
double initial_margin = 130;
|
||||
}
|
||||
|
||||
message ExcessLiquidityReport {
|
||||
string account = 10 [(bts.api.key)=true];
|
||||
uint32 account_id = 11;
|
||||
double leverage_factor = 20;
|
||||
double cash_balance = 30;
|
||||
MarginResult margin_result = 40;
|
||||
AccountPNL account_pnl = 50;
|
||||
bool should_enforce_margin = 60;
|
||||
double excess_liquidity = 100;
|
||||
}
|
||||
|
||||
|
||||
message MarginRequest {
|
||||
string account = 10;
|
||||
}
|
||||
|
||||
message CashFactorUpdate {
|
||||
string account = 10;
|
||||
double leverage_factor = 20;
|
||||
}
|
||||
|
||||
message CashBalanceUpdate {
|
||||
string account = 10;
|
||||
double cash_balance = 20;
|
||||
}
|
||||
|
||||
message EnforceUpdate {
|
||||
string account = 10;
|
||||
bool should_enforce_margin = 20;
|
||||
}
|
||||
|
||||
service MarginService {
|
||||
option (bts.api.service_description) = "Provides CME Margin Information per account";
|
||||
|
||||
rpc get_margin(MarginRequest) returns (MarginResult);
|
||||
rpc get_excess_liquidity_report(MarginRequest) returns (ExcessLiquidityReport);
|
||||
rpc set_should_enforce_margin(EnforceUpdate) returns (bts.api.ResultMessage);
|
||||
rpc set_cash_factor(CashFactorUpdate) returns (bts.api.ResultMessage);
|
||||
rpc set_cash_balance(CashBalanceUpdate) returns (bts.api.ResultMessage);
|
||||
|
||||
rpc margin_data(bts.api.VoidMessage) returns (stream MarginResult);
|
||||
rpc excess_liquidity_report(bts.api.VoidMessage) returns (stream ExcessLiquidityReport);
|
||||
}
|
||||
@@ -160,8 +160,8 @@ message JoinOrderDefinition {
|
||||
CustomOrderDefinition child = 6;
|
||||
}
|
||||
message TopOrderDefinition {
|
||||
uint32 watch_level_tick_offset = 1;
|
||||
spark.QuantityMeasure top_threshold = 2;
|
||||
uint32 ticks_off = 3;
|
||||
CustomOrderDefinition child = 6;
|
||||
}
|
||||
message TriggerOrderDefinition {
|
||||
|
||||
@@ -8,6 +8,7 @@ import "lib/domain_model/Security.proto";
|
||||
message PositionUnit{
|
||||
int32 record_id = 1 [(bts.api.key)=true];
|
||||
string account = 10;
|
||||
uint32 account_id = 15;
|
||||
uint32 security_id = 20;
|
||||
double sod_long_pos = 30;
|
||||
double sod_long_avg_price = 40;
|
||||
@@ -19,6 +20,22 @@ message PositionUnit{
|
||||
double day_short_avg_price = 100;
|
||||
}
|
||||
|
||||
message PNLUnit{
|
||||
int32 record_id = 1 [(bts.api.key)=true];
|
||||
PositionUnit pos = 10;
|
||||
double cash_value = 20;
|
||||
double bid_price = 30;
|
||||
double ask_price = 40;
|
||||
double last_price = 50;
|
||||
double settlement_price = 60;
|
||||
}
|
||||
|
||||
message AccountPNL{
|
||||
uint32 account_id = 1 [(bts.api.key)=true];
|
||||
string account_name = 10;
|
||||
double day_pnl = 20;
|
||||
}
|
||||
|
||||
message ManualFill {
|
||||
int32 record_id = 1 [(bts.api.key)=true];
|
||||
uint32 security_key = 2;
|
||||
@@ -53,5 +70,6 @@ service PositionService {
|
||||
// subscription
|
||||
rpc manual_fills(bts.api.VoidMessage) returns (stream ManualFill);
|
||||
rpc position_data(bts.api.VoidMessage) returns (stream PositionUnit);
|
||||
|
||||
rpc pnl_data(bts.api.VoidMessage) returns (stream PNLUnit);
|
||||
rpc pnl_account_data(bts.api.VoidMessage) returns (stream AccountPNL);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
SpanMargin {
|
||||
value {
|
||||
enabled: false
|
||||
login: 'api_bts1'
|
||||
password: 'Summer123'
|
||||
url: 'https://cmecorenr.cmegroup.com/MarginServiceApi/1.4/'
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,4 @@ OrdersSoftCleanup {
|
||||
value {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,11 @@ message TopAttachUIOptions {
|
||||
optional bool show_quantity = 2 [default = false];
|
||||
}
|
||||
|
||||
message OffsetAttachUIOptions {
|
||||
optional bool show_price = 1 [default = false];
|
||||
optional bool show_quantity = 2 [default = false];
|
||||
}
|
||||
|
||||
message ManualFillsOptions {
|
||||
optional bool modal_transactions = 1 [default = true];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
syntax='proto2';
|
||||
package bts.config;
|
||||
|
||||
message SpanMargin
|
||||
{
|
||||
optional bool enabled = 10;
|
||||
optional string login = 20;
|
||||
optional string password = 30;
|
||||
optional string url = 40;
|
||||
}
|
||||
@@ -53,4 +53,4 @@ message EnsureOnScreenWithWorkAreaRect {
|
||||
// and the new ensure_onscreen code has only been tested with a single monitor arrangement.
|
||||
// Should verify that the code works with every monitor arrangement before enabling by default.
|
||||
optional bool enabled = 1 [default = false];
|
||||
}
|
||||
}
|
||||
@@ -102,14 +102,13 @@ void MDProducerHandle::set_status(CoconutSecurityStatus status) {
|
||||
}
|
||||
|
||||
void MDProducerHandle::add_trade(TimeStamp time, const PriceSize price, LLDateTime tradetime, co2::AggressorSide agside, uint32_t volume) {
|
||||
// Add this trade to the last trade if price/side match
|
||||
// Add this trade to the last trade if price match
|
||||
auto current = std::get<PriceSize>(cdata().last_trade.load());
|
||||
if (current.int_price() == price.int_price() && agside == cdata().last_side) {
|
||||
if (current.int_price() == price.int_price()) {
|
||||
cdata().last_trade.store(PriceSize { price.int_price(), price.size() + current.size() }, time);
|
||||
}
|
||||
else {
|
||||
cdata().last_trade.store(price,time);
|
||||
cdata().last_side = agside;
|
||||
}
|
||||
cdata().last_trade_time = tradetime;
|
||||
cdata().total_volume = volume;
|
||||
|
||||
@@ -22,6 +22,7 @@ void SecurityCacheImpl::enqueue_update_db(const SecurityPtr& security) {
|
||||
}
|
||||
|
||||
void SecurityCacheImpl::flush() {
|
||||
#ifndef _WIN32
|
||||
if (!update_queue.empty()) {
|
||||
std::vector<SecurityPtr> work;
|
||||
SecurityPtr temp;
|
||||
@@ -33,6 +34,7 @@ void SecurityCacheImpl::flush() {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace md {
|
||||
|
||||
using utils::TimeStamp;
|
||||
|
||||
const uint32_t shm_version = 101;
|
||||
const uint32_t shm_version = 102;
|
||||
|
||||
struct ALIGNED_(CACHE_LINE) HotHeader {
|
||||
size_t count;
|
||||
@@ -214,7 +214,6 @@ struct ColdEntry {
|
||||
last_trade(),
|
||||
total_volume(0),
|
||||
last_trade_time(),
|
||||
last_side(co2::AggressorSide::AGGRESSOR_SIDE_NONE),
|
||||
misc(),
|
||||
deleted(false)
|
||||
{
|
||||
@@ -230,7 +229,6 @@ struct ColdEntry {
|
||||
AtomicPriceSize last_trade;
|
||||
size_t total_volume;
|
||||
datetime::LLDateTime last_trade_time;
|
||||
co2::AggressorSide last_side;
|
||||
|
||||
MiscData misc;
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ tuple<shared_ptr<SyntheticStrategy>,vector<Co2TestInstrument>> make_securities(c
|
||||
l->mutable_security()->mutable_contract_spec()->set_low_tick(tick_size);
|
||||
l->mutable_security()->mutable_contract_spec()->set_high_tick(tick_size);
|
||||
l->set_quantity(quantity);
|
||||
l->set_price_multiplier(quantity * (side == api::SIDE_BUY ? 1.0 : -1));
|
||||
l->set_price_multiplier(mult);
|
||||
l->set_side(side);
|
||||
synthetic->legs.push_back(instr.consumer());
|
||||
legs.push_back(instr);
|
||||
@@ -126,7 +126,8 @@ tuple<shared_ptr<SyntheticStrategy>,vector<Co2TestInstrument>> make_securities(c
|
||||
double quantity, tick_size;
|
||||
api::Side side;
|
||||
std::tie(quantity, tick_size, side) = t;
|
||||
legs.push_back({ quantity, quantity, tick_size, side });
|
||||
double mult = quantity * (side == api::SIDE_BUY ? 1.0 : -1);
|
||||
legs.push_back({ quantity, mult, tick_size, side });
|
||||
}
|
||||
return make_securities(legs);
|
||||
}
|
||||
@@ -457,6 +458,113 @@ TEST_CASE("Synthetic_net_change", "[md][synthetic]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("HCL_SP_104", "[md][synthetic]") {
|
||||
shared_ptr<SyntheticStrategy> synthetic;
|
||||
vector<Co2TestInstrument> test_instr;
|
||||
tie(synthetic, test_instr) = make_securities({
|
||||
// (quantity, multiplier, tick_size, side)
|
||||
{1.0, 1.0, 0.01, api::SIDE_BUY},
|
||||
{1.0, 1.0, 0.01, api::SIDE_BUY},
|
||||
{1.0, -1.0, 0.01, api::SIDE_SELL},
|
||||
{1.0, -1.0, 0.01, api::SIDE_SELL }
|
||||
});
|
||||
auto leg1 = test_instr[0]; // CLQ9-CLU9
|
||||
auto leg2 = test_instr[1]; // HCLQ9-CLQ9
|
||||
auto leg3 = test_instr[2]; // HCLQ9-HCLU9
|
||||
auto leg4 = test_instr[3]; // HCLU9-CLU9
|
||||
|
||||
setup_market(leg1.producer(),
|
||||
{
|
||||
{ -0.10, 3299 },
|
||||
{ -0.11, 2885 },
|
||||
{ -0.12, 2558 },
|
||||
{ -0.13, 2447 },
|
||||
{ -0.14, 1540 },
|
||||
{ -0.15, 2334 },
|
||||
{ -0.16, 1396 },
|
||||
{ -0.17, 1190 },
|
||||
{ -0.18, 1221 },
|
||||
{ -0.19, 812 }
|
||||
},
|
||||
{
|
||||
{ -0.09, 1318 },
|
||||
{ -0.08, 2174 },
|
||||
{ -0.07, 3057 },
|
||||
{ -0.06, 2331 },
|
||||
{ -0.05, 2543 },
|
||||
{ -0.04, 2638 },
|
||||
{ -0.03, 2232 },
|
||||
{ -0.02, 3221 },
|
||||
{ -0.01, 2863 },
|
||||
{ 0.00, 3327 }
|
||||
});
|
||||
|
||||
setup_market(leg2.producer(),
|
||||
{
|
||||
{ 4.06, 1 },
|
||||
{ 4.05, 2 },
|
||||
{ 4.04, 3 },
|
||||
{ 4.03, 12 },
|
||||
{ 4.02, 6 },
|
||||
{ 4.01, 1 }
|
||||
},
|
||||
{
|
||||
{ 4.07, 1 },
|
||||
{ 4.08, 2 },
|
||||
{ 4.10, 8 },
|
||||
{ 4.11, 10 },
|
||||
{ 4.12, 2 },
|
||||
{ 4.14, 1 }
|
||||
});
|
||||
|
||||
setup_market(leg3.producer(),
|
||||
{
|
||||
{ 0.20, 2 },
|
||||
{ 0.19, 6 },
|
||||
{ 0.18, 5 },
|
||||
{ 0.17, 18 },
|
||||
{ 0.16, 2 },
|
||||
{ 0.15, 3 },
|
||||
{ 0.14, 2 }
|
||||
},
|
||||
{
|
||||
{ 0.22, 2 },
|
||||
{ 0.23, 4 },
|
||||
{ 0.24, 9 },
|
||||
{ 0.25, 17},
|
||||
{ 0.26, 5 },
|
||||
{ 0.27, 5 },
|
||||
{ 0.28, 4 },
|
||||
{ 0.29, 2 },
|
||||
{ 0.30, 2 }
|
||||
});
|
||||
|
||||
setup_market(leg4.producer(),
|
||||
{
|
||||
{ 3.75, 1 },
|
||||
{ 3.74, 3 },
|
||||
{ 3.73, 4 },
|
||||
{ 3.72, 12 },
|
||||
{ 3.71, 1 },
|
||||
{ 3.70, 3 }
|
||||
},
|
||||
{
|
||||
{ 3.78, 1 },
|
||||
{ 3.79, 2 },
|
||||
{ 3.80, 6 },
|
||||
{ 3.81, 1 },
|
||||
{ 3.82, 10 },
|
||||
{ 3.83, 1 }
|
||||
});
|
||||
|
||||
MDConsumerHandle synthetic_handle(synthetic);
|
||||
// dump_market(synthetic_handle);
|
||||
expect_market(synthetic_handle,
|
||||
{ { -0.04,1 },{ -0.06,1 },{ -0.07,1 },{ -0.09,3 },{ -0.11,3 },{ -0.12,1 },{ -0.13,5 },{ -0.14,3 },{ -0.15,2 },{ -0.16,1 } },
|
||||
{ { 0.03,1 },{ 0.05,1 },{ 0.06,1 },{ 0.08,1 },{ 0.09,4 },{ 0.11,3 },{ 0.12,2 },{ 0.13,7 },{ 0.14,1 },{ 0.16,2 } }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Benchmarks
|
||||
|
||||
@@ -126,6 +126,7 @@ void FillStore::on_execution_report(
|
||||
if (it != me->known_orders.end()) {
|
||||
auto &order_def = it->second;
|
||||
fill.set_account(order_def.account());
|
||||
fill.set_internal_account_id(order_def.account_id());
|
||||
} else
|
||||
me->fills_with_unresolved_accounts[er.order_key]
|
||||
.fills.push_back(fill);
|
||||
@@ -153,6 +154,7 @@ void FillStore::on_order_definition_update(
|
||||
auto &unresolved_fills = me->fills_with_unresolved_accounts[ev.key];
|
||||
for (auto &f : unresolved_fills.fills) {
|
||||
f.set_account(ev.def->account());
|
||||
f.set_internal_account_id(ev.def->account_id());
|
||||
auto fv = utils::pb::to_string(f);
|
||||
me->store->update(fills_ns, f.fill_key(), fv);
|
||||
publish(me->fill_stream, f, *unresolved_fills.security);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "lib/orderman/actors/SODPosition.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/orders_fills_cleanup.pb.h"
|
||||
#include "lib/api/service_helpers/AdminServiceUtils.h"
|
||||
#include "lib/config/protos/toggles.pb.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
@@ -131,6 +132,7 @@ void SODPosition::_merge_pos(Fill &pos, const Fill &fill) {
|
||||
void SODPosition::_zero_pos(Fill &pos, const Fill &fill, api::Side side) {
|
||||
pos.set_security_key(fill.security_key());
|
||||
pos.set_account(fill.account());
|
||||
pos.set_internal_account_id(fill.internal_account_id());
|
||||
pos.set_side(side);
|
||||
pos.mutable_timestamp()->set_seconds(_sod_time.tv_sec);
|
||||
pos.mutable_timestamp()->set_nanos(0);
|
||||
@@ -140,14 +142,20 @@ void SODPosition::_zero_pos(Fill &pos, const Fill &fill, api::Side side) {
|
||||
pos.set_last_shares(0);
|
||||
}
|
||||
|
||||
bool SODPosition::add_fill_to_sod_pos(const Fill &fill) {
|
||||
bool SODPosition::add_fill_to_sod_pos(const Fill &fill) {
|
||||
if (_enabled && fill.timestamp().seconds() < _sod_time.tv_sec) {
|
||||
uint32_t seckey = fill.security_key();
|
||||
const std::string &acct = fill.account();
|
||||
auto acct_it = _sod_map.find(acct);
|
||||
if (!bts::admin::AdminServiceUtils::has_id(fill.internal_account_id())){
|
||||
LOG(persist, Error, "SODPosition::add_fill_to_sod_pos Could not find "
|
||||
"account_id for fill:{} account:{}",
|
||||
fill.fill_key(), fill.account());
|
||||
return false;
|
||||
}
|
||||
uint32_t seckey = fill.security_key(),
|
||||
account_id = fill.internal_account_id();
|
||||
auto acct_it = _sod_map.find(account_id);
|
||||
if (acct_it == _sod_map.end())
|
||||
_sod_map[acct] = SecPosMap();
|
||||
acct_it = _sod_map.find(fill.account());
|
||||
_sod_map[account_id] = SecPosMap();
|
||||
acct_it = _sod_map.find(account_id);
|
||||
bts_assert(acct_it != _sod_map.end());
|
||||
SecPosMap &sec_map = acct_it->second;
|
||||
auto sec_it = sec_map.find(seckey);
|
||||
@@ -165,7 +173,8 @@ bool SODPosition::add_fill_to_sod_pos(const Fill &fill) {
|
||||
else
|
||||
_merge_pos(pos.second, fill);
|
||||
return true;
|
||||
} else
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ SecPosMap;
|
||||
|
||||
class SODPosition{
|
||||
private:
|
||||
std::map<std::string,SecPosMap> _sod_map;
|
||||
std::map<uint32_t, const api::Security*> _sec_ptr_map;
|
||||
std::map<uint32_t,SecPosMap> _sod_map;
|
||||
const bool _enabled;
|
||||
const timeval _sod_time;
|
||||
private:
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#include "ServiceBridge.h"
|
||||
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/api/services/PositionService.pb.h"
|
||||
#include "lib/mercury/broker/BrokerProxy.h"
|
||||
#include "lib/orderman/events/AccountPnLUpdateEvent.h"
|
||||
#include "lib/orderman/events/ActivateOrderEvent.h"
|
||||
#include "lib/orderman/events/CancelOrderEvent.h"
|
||||
#include "lib/orderman/events/DetachOrderEvent.h"
|
||||
#include "lib/orderman/events/ExcessLiquidityUpdateEvent.h"
|
||||
#include "lib/orderman/events/NewAdminDataEvent.h"
|
||||
#include "lib/orderman/events/OrderManagerReadyEvent.h"
|
||||
#include "lib/orderman/events/OrderOpResultEvent.h"
|
||||
@@ -13,13 +17,11 @@
|
||||
#include "lib/orderman/events/UpdateOrderEvent.h"
|
||||
#include "lib/orderman/order_factory/OrderFactory.h"
|
||||
#include "lib/orderman/order_router/OrderRouter.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_common.h"
|
||||
#include "lib/orderman/orders/Order.h"
|
||||
#include "lib/orderman/securities/MapSecurityDB.h"
|
||||
#include "lib/tracing/PerfLog.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_common.h"
|
||||
#include "lib/orderman/orders/Order.h"
|
||||
#include <future>
|
||||
|
||||
namespace bts {
|
||||
@@ -50,6 +52,8 @@ struct ServiceBridge::PImpl {
|
||||
mercury::Stream<CancelOrderEvent> cancel_order_pub;
|
||||
mercury::Stream<DetachOrderEvent> detach_order_pub;
|
||||
mercury::Stream<NewAdminDataEvent> new_admin_data_pub;
|
||||
mercury::Stream<AccountPnLUpdateEvent> account_pnl_pub;
|
||||
mercury::Stream<ExcessLiquidityUpdateEvent> excess_liquidity_pub;
|
||||
std::unordered_set<OrderKey> known_orders;
|
||||
std::vector<std::shared_ptr<const api::admin::AdminData>> admin_data_updates;
|
||||
mercury::BrokerProxy &service_call_marshaller;
|
||||
@@ -81,6 +85,8 @@ void ServiceBridge::init() {
|
||||
me->cancel_order_pub = make_stream<CancelOrderEvent>();
|
||||
me->detach_order_pub = make_stream<DetachOrderEvent>();
|
||||
me->new_admin_data_pub = make_stream<NewAdminDataEvent>();
|
||||
me->account_pnl_pub = make_stream<AccountPnLUpdateEvent>();
|
||||
me->excess_liquidity_pub = make_stream<ExcessLiquidityUpdateEvent>();
|
||||
me->ready.set_value();
|
||||
}
|
||||
|
||||
@@ -217,6 +223,36 @@ void ServiceBridge::update_admin_data(
|
||||
});
|
||||
}
|
||||
|
||||
void ServiceBridge::update_account_pnl(
|
||||
api::position::Pnl_account_dataUpdate const &pnl) {
|
||||
me->service_call_marshaller.schedule_action([this, pnl]() {
|
||||
for (auto const &account_pnl : pnl.added()) {
|
||||
publish(me->account_pnl_pub, AccountID(account_pnl->account_id()),
|
||||
account_pnl->day_pnl());
|
||||
}
|
||||
for (auto const &account_pnl : pnl.changed()) {
|
||||
publish(me->account_pnl_pub,
|
||||
AccountID(account_pnl.current()->account_id()),
|
||||
account_pnl.current()->day_pnl());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ServiceBridge::update_excess_liquidity_report(
|
||||
api::position::Excess_liquidity_reportUpdate const &elr) {
|
||||
me->service_call_marshaller.schedule_action([this, elr]() {
|
||||
for (auto const &elr : elr.added()) {
|
||||
publish(me->excess_liquidity_pub, AccountID(elr->account_id()),
|
||||
elr->excess_liquidity());
|
||||
}
|
||||
for (auto const &elr : elr.changed()) {
|
||||
publish(me->excess_liquidity_pub,
|
||||
AccountID(elr.current()->account_id()),
|
||||
elr.current()->excess_liquidity());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool ServiceBridge::is_ready() const {
|
||||
return me->have_orders && me->order_manager_ready;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/services/MarginService.client.pb.h"
|
||||
#include "lib/api/services/PositionService.client.pb.h"
|
||||
#include "lib/config/protos/jetfix.pb.h"
|
||||
#include "lib/mercury/actors/Actor.h"
|
||||
#include "lib/md/SecurityOracle.h"
|
||||
#include "lib/mercury/actors/Actor.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
// fwds
|
||||
namespace bts {
|
||||
namespace mercury {class BrokerProxy;}
|
||||
namespace mercury {
|
||||
class BrokerProxy;
|
||||
}
|
||||
namespace api {
|
||||
class ServiceContext;
|
||||
namespace admin {
|
||||
class AdminData;
|
||||
}
|
||||
namespace position {
|
||||
class AccountPNL;
|
||||
}
|
||||
namespace orderman {
|
||||
class OrderRecord;
|
||||
class Fill;
|
||||
@@ -45,16 +53,14 @@ class ServiceBridge : public mercury::Actor {
|
||||
|
||||
using OnReady = std::function<void()>;
|
||||
using OnOrders =
|
||||
std::function<void(std::vector<api::orderman::OrderRecord> const &)>;
|
||||
std::function<void(std::vector<api::orderman::OrderRecord> const &)>;
|
||||
using OnFills =
|
||||
std::function<void(std::vector<api::orderman::Fill> const &)>;
|
||||
std::function<void(std::vector<api::orderman::Fill> const &)>;
|
||||
|
||||
using OnOrderOpResult = std::function<void(OrderOpResultEvent const &)>;
|
||||
|
||||
using OnOrderOpResult =
|
||||
std::function<void(OrderOpResultEvent const&)>;
|
||||
|
||||
ServiceBridge(OnReady const &, OnOrders const &, OnFills const &,
|
||||
OnOrderOpResult const&,
|
||||
DynamicOrderRegistry const &,
|
||||
OnOrderOpResult const &, DynamicOrderRegistry const &,
|
||||
mercury::BrokerProxy &service_call_marshaller,
|
||||
md::SecurityOracle o);
|
||||
void init() override;
|
||||
@@ -71,6 +77,9 @@ class ServiceBridge : public mercury::Actor {
|
||||
|
||||
void
|
||||
update_admin_data(std::shared_ptr<api::admin::AdminData const> const &admin);
|
||||
void update_account_pnl(api::position::Pnl_account_dataUpdate const &);
|
||||
void update_excess_liquidity_report(
|
||||
api::position::Excess_liquidity_reportUpdate const &);
|
||||
|
||||
private:
|
||||
struct PImpl;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "lib/mercury/events/TimerEvent.h"
|
||||
#include "lib/orderman/actors/order_manager/OrderServices.h"
|
||||
#include "lib/orderman/actors/order_manager/OrderServicesData.h"
|
||||
#include "lib/orderman/events/AccountPnLUpdateEvent.h"
|
||||
#include "lib/orderman/events/ExcessLiquidityUpdateEvent.h"
|
||||
#include "lib/orderman/events/NewAdminDataEvent.h"
|
||||
#include "lib/orderman/events/NewSecuritiesEvent.h"
|
||||
#include "lib/orderman/events/OrderManagerReadyEvent.h"
|
||||
@@ -52,6 +54,11 @@ OrderManager::~OrderManager() {}
|
||||
void OrderManager::init() {
|
||||
subscribe<NewAdminDataEvent>(
|
||||
[this](auto const &admin_update) { this->on_admin_data(admin_update); });
|
||||
subscribe<AccountPnLUpdateEvent>(
|
||||
[this](auto const &pnl_update) { this->on_pnl_update(pnl_update); });
|
||||
subscribe<ExcessLiquidityUpdateEvent>([this](auto const &liq_update) {
|
||||
this->on_excess_liquidity_update(liq_update);
|
||||
});
|
||||
}
|
||||
|
||||
void OrderManager::on_admin_data(const mercury::Update<NewAdminDataEvent> &m) {
|
||||
@@ -74,6 +81,20 @@ void OrderManager::on_admin_data(const mercury::Update<NewAdminDataEvent> &m) {
|
||||
}
|
||||
}
|
||||
|
||||
void OrderManager::on_pnl_update(
|
||||
const mercury::Update<AccountPnLUpdateEvent> &u) {
|
||||
if (u.events)
|
||||
for (auto ev : *u.events)
|
||||
me->services_data.limiter.set_day_pnl(ev.account_id, ev.day_pnl);
|
||||
}
|
||||
|
||||
void OrderManager::on_excess_liquidity_update(
|
||||
const mercury::Update<ExcessLiquidityUpdateEvent> &u) {
|
||||
if (u.events)
|
||||
for (auto ev : *u.events)
|
||||
me->services_data.limiter.set_excess_liquidity(ev.account_id, ev.excess_liquidity);
|
||||
}
|
||||
|
||||
void OrderManager::on_securities(
|
||||
const mercury::Update<NewSecuritiesEvent> &update) {
|
||||
|
||||
@@ -116,9 +137,9 @@ void OrderManager::on_fill_recovery(
|
||||
for (auto const &ev : *(update.events)) {
|
||||
auto trimmed_account_name =
|
||||
boost::algorithm::trim_copy(ev.fill.account());
|
||||
me->services_data.limiter.add(*ev.security, trimmed_account_name,
|
||||
ev.fill.side(),
|
||||
Quantity(ev.fill.last_shares()),false);
|
||||
me->services_data.limiter.add(
|
||||
*ev.security, trimmed_account_name, ev.fill.side(),
|
||||
Quantity(ev.fill.last_shares()), false);
|
||||
}
|
||||
|
||||
me->stored_order_sub = subscribe<StoredOrderEvent>(
|
||||
|
||||
@@ -34,6 +34,8 @@ class OrderServices;
|
||||
class CreateFIXBufferReplyEvent;
|
||||
class GatewayStatusChangeEvent;
|
||||
class NewAdminDataEvent;
|
||||
class AccountPnLUpdateEvent;
|
||||
class ExcessLiquidityUpdateEvent;
|
||||
struct SessionServicesData;
|
||||
class DynamicOrderRegistry;
|
||||
} // namespace orderman
|
||||
@@ -83,6 +85,8 @@ class OrderManager : public mercury::Actor, public mercury::IPollableActor {
|
||||
void on_gateway_state(const mercury::Update<GatewayStatusChangeEvent> &);
|
||||
void on_timer(const mercury::Update<mercury::TimerEvent> &);
|
||||
void on_admin_data(const mercury::Update<NewAdminDataEvent> &);
|
||||
void on_pnl_update(const mercury::Update<AccountPnLUpdateEvent> &);
|
||||
void on_excess_liquidity_update(const mercury::Update<ExcessLiquidityUpdateEvent> &);
|
||||
void on_poll(trace::PerfLog& log) override;
|
||||
// end external events
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/api/services/AdminService.pb.h"
|
||||
|
||||
#include "lib/mercury/common/mercury_common.h"
|
||||
#include "lib/mercury/events/Event.h"
|
||||
#include "lib/mercury/synopsis/MergeByKey.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class AccountPnLUpdateEvent : public mercury::Event<AccountPnLUpdateEvent> {
|
||||
public:
|
||||
using SynopsisPolicy = mercury::MergeByKey<AccountPnLUpdateEvent>;
|
||||
AccountID key() const { return account_id; }
|
||||
|
||||
AccountPnLUpdateEvent(AccountID id, double pnl)
|
||||
: account_id(id), day_pnl(pnl) {}
|
||||
|
||||
AccountID account_id;
|
||||
double day_pnl;
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/mercury/common/mercury_common.h"
|
||||
#include "lib/mercury/events/Event.h"
|
||||
#include "lib/mercury/synopsis/MergeByKey.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class ExcessLiquidityUpdateEvent : public mercury::Event<ExcessLiquidityUpdateEvent> {
|
||||
public:
|
||||
using SynopsisPolicy = mercury::MergeByKey<ExcessLiquidityUpdateEvent>;
|
||||
AccountID key() const { return account_id; }
|
||||
|
||||
ExcessLiquidityUpdateEvent(AccountID id, double excess_liquidity)
|
||||
: account_id(id), excess_liquidity(excess_liquidity) {}
|
||||
|
||||
AccountID account_id;
|
||||
double excess_liquidity;
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -5,8 +5,10 @@ namespace orderman {
|
||||
|
||||
LimitStore::LimitStore(const bts::api::admin::AdminData &admin_data,
|
||||
const std::map<uint32_t, api::Security const *> &sec_map)
|
||||
: _admin_data(bts::admin::AdminServiceUtils::sort_limits(admin_data)),
|
||||
_account_map(bts::admin::AdminServiceUtils::accounts_map(_admin_data)) {
|
||||
: _admin_data(bts::admin::AdminServiceUtils::sort_limits(admin_data)) {
|
||||
for (const auto& acct : _admin_data.accounts())
|
||||
_account_data_map.insert(std::pair<uint32_t,AccountData>(
|
||||
acct.id(),AccountData(acct)));
|
||||
for (auto it = sec_map.begin(); it != sec_map.end(); it++) {
|
||||
const api::Security &security = *(it->second);
|
||||
_add_product_entry(security, _admin_data);
|
||||
@@ -22,7 +24,7 @@ LimitStore::LimitStore(const bts::api::admin::AdminData &admin_data,
|
||||
}
|
||||
_set_user_data();
|
||||
}
|
||||
void LimitStore::copy_positions_from(LimitStore &source) {
|
||||
void LimitStore::copy_data_from(LimitStore &source) {
|
||||
// copy securities
|
||||
for (auto it = _sec_map.begin(); it != _sec_map.end(); it++) {
|
||||
SecurityLimitTree *source_security_limit_tree =
|
||||
@@ -30,6 +32,25 @@ void LimitStore::copy_positions_from(LimitStore &source) {
|
||||
if (source_security_limit_tree != nullptr)
|
||||
it->second.copy_positions_from(*source_security_limit_tree);
|
||||
}
|
||||
// copy pnl's and liqs
|
||||
for (auto it=_account_data_map.begin();it!=_account_data_map.end();it++){
|
||||
auto source_it = source._account_data_map.find(it->first);
|
||||
if(source_it != source._account_data_map.end()) {
|
||||
it->second.curr_day_pnl = source_it->second.curr_day_pnl;
|
||||
it->second.excess_liquidity = source_it->second.excess_liquidity;
|
||||
}
|
||||
}
|
||||
}
|
||||
void LimitStore::set_pnl (AccountID account_id, double pnl){
|
||||
auto it = _account_data_map.find(account_id.value());
|
||||
bts_assert(it != _account_data_map.end());
|
||||
it->second.curr_day_pnl = pnl;
|
||||
}
|
||||
void LimitStore::set_excess_liquidity (AccountID account_id,
|
||||
double excess_liquidity){
|
||||
auto it = _account_data_map.find(account_id.value());
|
||||
bts_assert(it != _account_data_map.end());
|
||||
it->second.excess_liquidity = excess_liquidity;
|
||||
}
|
||||
void LimitStore::_add_product_entry(
|
||||
const api::Security &security,
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include "Limiter.h"
|
||||
#include "ProductLimitTree.h"
|
||||
#include "SecurityLimitTree.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/span.pb.h"
|
||||
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
@@ -33,9 +37,22 @@ private:
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class AccountData{
|
||||
public:
|
||||
const bts::api::admin::BTSAccount& account;
|
||||
double curr_day_pnl;
|
||||
double excess_liquidity;
|
||||
AccountData (const bts::api::admin::BTSAccount& account)
|
||||
:
|
||||
account(account),
|
||||
curr_day_pnl (os::nan()),
|
||||
excess_liquidity (os::nan())
|
||||
{}
|
||||
};
|
||||
const bts::api::admin::AdminData _admin_data;
|
||||
// just an easy way to find accounts
|
||||
const std::map<uint32_t, bool> _account_map;
|
||||
// easy account access (uses for loss limit)
|
||||
std::map<uint32_t, AccountData> _account_data_map;
|
||||
// map of user id's vs the user pointer and acccounts
|
||||
std::map<uint32_t, UserData> _user_data_map;
|
||||
// map of securities vs their account limits and positions
|
||||
@@ -52,14 +69,20 @@ public:
|
||||
|
||||
// accessors ///////////////
|
||||
////////////////////////////
|
||||
bool has_account (AccountID account_id) const
|
||||
{return _account_map.find(account_id.value()) != _account_map.end();}
|
||||
bool has_account (AccountID account_id) const{
|
||||
return _account_data_map.find(account_id.value()) !=
|
||||
_account_data_map.end();
|
||||
}
|
||||
bool has_security (uint32_t sec_id) const
|
||||
{return _sec_map.find(sec_id) != _sec_map.end();}
|
||||
const bts::api::admin::AdminData& admin_data () const {return _admin_data;}
|
||||
// position updates ////////
|
||||
////////////////////////////
|
||||
void copy_positions_from(LimitStore& source);
|
||||
// data updates ////////////
|
||||
// once the LimitStore is created its position and
|
||||
// pnl is updated from outside.
|
||||
////////////////////////////
|
||||
// copy_data_from copies the position and
|
||||
// data from another source
|
||||
void copy_data_from(LimitStore& source);
|
||||
void add_position(const api::Security& security,
|
||||
AccountID account_id, api::Side side,
|
||||
Quantity quantity, bool recurse){
|
||||
@@ -88,6 +111,8 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
void set_pnl (AccountID account_id, double pnl);
|
||||
void set_excess_liquidity (AccountID account_id, double excess_liquidity);
|
||||
// risk limit checks
|
||||
////////////////////
|
||||
// checks to see if the user is allowed to trade
|
||||
@@ -130,11 +155,21 @@ public:
|
||||
Limiter::LimiterQueryResult can_send_new_order(AccountID account_id,
|
||||
const api::Security& security,
|
||||
api::Side side,
|
||||
Quantity quantity) const{
|
||||
Quantity quantity,
|
||||
bool enforce_clip = true) const{
|
||||
Limiter::LimiterQueryResult rv; rv.result = Limiter::OK;
|
||||
const auto acct_it = _account_data_map.find(account_id.value());
|
||||
AccountData const* account_ptr = (acct_it == _account_data_map.end()) ?
|
||||
nullptr : &(acct_it->second);
|
||||
_loss_limit(account_ptr, rv);
|
||||
if(!rv.ok())
|
||||
return rv;
|
||||
_margin_limit(account_ptr,rv);
|
||||
if(!rv.ok())
|
||||
return rv;
|
||||
const SecurityLimitTree* sec_tree = _find_security(security.id());
|
||||
bts_assert(sec_tree != nullptr);
|
||||
rv = sec_tree->can_send_new_order(account_id, side, quantity);
|
||||
rv = sec_tree->can_send_new_order(account_id, side, quantity, enforce_clip);
|
||||
if (rv.ok() && security.has_spread()){
|
||||
// if its rejected we dont need to validate further,
|
||||
// however if its ok and its a spread we need to check
|
||||
@@ -142,7 +177,12 @@ public:
|
||||
for (const api::ContractLeg& leg : security.spread().legs()){
|
||||
api::Side effective_leg_side = side == api::Side::SIDE_BUY ? leg.side() : LimiterUtils::invert_side(leg.side());
|
||||
Quantity effective_leg_qty = Quantity (quantity.value() * std::abs(leg.quantity()));
|
||||
rv = can_send_new_order (account_id, leg.security(), effective_leg_side, effective_leg_qty);
|
||||
// in the case of a spread with future legs the clip limits
|
||||
// for the legs are not checked. if the legs are are spreads
|
||||
// themselves then they are checked (eventually it will recuse
|
||||
// down to a non spread)
|
||||
rv = can_send_new_order (account_id, leg.security(), effective_leg_side, effective_leg_qty,
|
||||
leg.security().has_spread());
|
||||
if (!rv.ok())
|
||||
return rv;
|
||||
}
|
||||
@@ -152,18 +192,29 @@ public:
|
||||
Limiter::LimiterQueryResult can_update_order(AccountID account_id,
|
||||
const api::Security& security,
|
||||
api::Side side,
|
||||
Quantity from, Quantity to) const{
|
||||
Quantity from, Quantity to,
|
||||
bool enforce_clip = true) const{
|
||||
Limiter::LimiterQueryResult rv; rv.result = Limiter::OK;
|
||||
const auto acct_it = _account_data_map.find(account_id.value());
|
||||
AccountData const* account_ptr = (acct_it == _account_data_map.end()) ?
|
||||
nullptr : &(acct_it->second);
|
||||
// in case of order modifications, loss limits are only checked
|
||||
// for size increases
|
||||
if (to.value() > from.value())
|
||||
_loss_limit(account_ptr, rv);
|
||||
if(!rv.ok())
|
||||
return rv;
|
||||
const SecurityLimitTree* sec_tree = _find_security(security.id());
|
||||
bts_assert(sec_tree != nullptr);
|
||||
rv = sec_tree->can_update_order(account_id, side, from, to);
|
||||
rv = sec_tree->can_update_order(account_id, side, from, to, enforce_clip);
|
||||
// same logic for the spread legs as above
|
||||
if (rv.ok() && security.has_spread()){
|
||||
for (const api::ContractLeg& leg : security.spread().legs()){
|
||||
api::Side effective_leg_side = side == api::Side::SIDE_BUY ? leg.side() : LimiterUtils::invert_side(leg.side());
|
||||
Quantity effective_leg_from_qty = Quantity (from.value() * std::abs(leg.quantity()));
|
||||
Quantity effective_leg_to_qty = Quantity (to.value() * std::abs(leg.quantity()));
|
||||
rv = can_update_order (account_id, leg.security(), effective_leg_side, effective_leg_from_qty, effective_leg_to_qty);
|
||||
rv = can_update_order (account_id, leg.security(), effective_leg_side, effective_leg_from_qty, effective_leg_to_qty,
|
||||
leg.security().has_spread());
|
||||
if (!rv.ok())
|
||||
return rv;
|
||||
}
|
||||
@@ -190,5 +241,66 @@ private:
|
||||
void _add_product_entry(const api::Security& security,
|
||||
const bts::api::admin::AdminData& admin_data);
|
||||
void _set_user_data();
|
||||
void _loss_limit(AccountData const* account_ptr,
|
||||
Limiter::LimiterQueryResult& out_res) const{
|
||||
// the usual flow should not alter out_res.
|
||||
// its set to an error state iff :
|
||||
// i. account is not found
|
||||
// ii. limit check is enabled but pnl is nan
|
||||
// iii. limit check is enable and it actually fails
|
||||
if(account_ptr != nullptr){
|
||||
const auto& account_data = *account_ptr;
|
||||
const auto& account = account_data.account;
|
||||
if (account.has_account_loss_limit() &&
|
||||
account.account_loss_limit().enforce()){
|
||||
double pnl = account_data.curr_day_pnl;
|
||||
if(os::is_nan(pnl)){
|
||||
out_res.result = Limiter::LimiterErrorCode::LOSS_LIMIT_BREACHED;
|
||||
out_res.error_msg = "Could not find account PNL";
|
||||
}
|
||||
else{
|
||||
double loss_limit = -1.0 * static_cast<double>(
|
||||
account.account_loss_limit().total_loss_limit());
|
||||
if(pnl<loss_limit){
|
||||
out_res.result = Limiter::LimiterErrorCode::LOSS_LIMIT_BREACHED;
|
||||
out_res.error_msg = fmt::format("Loss Limit (Max {} Current {})",
|
||||
loss_limit, pnl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
out_res.result = Limiter::LimiterErrorCode::NO_SUCH_ACCOUNT;
|
||||
out_res.error_msg = "Could not find account";
|
||||
}
|
||||
}
|
||||
void _margin_limit(AccountData const* account_ptr,
|
||||
Limiter::LimiterQueryResult& out_res) const{
|
||||
// similiar flow as the loss_limit above
|
||||
if(account_ptr != nullptr){
|
||||
const auto& account_data = *account_ptr;
|
||||
const auto& account = account_data.account;
|
||||
if(config::get<bts::config::SpanMargin>().enabled()) {
|
||||
if (account.has_margin_limit() && account.margin_limit().enforce_margin()){
|
||||
double ex_liq = account_data.excess_liquidity;
|
||||
if(os::is_nan(ex_liq)){
|
||||
out_res.result = Limiter::LimiterErrorCode::MARGIN_LIMIT_BREACHED;
|
||||
out_res.error_msg = "Could not find account Excess Liquidity";
|
||||
}
|
||||
else{
|
||||
if(ex_liq < 0){
|
||||
out_res.result = Limiter::LimiterErrorCode::MARGIN_LIMIT_BREACHED;
|
||||
out_res.error_msg = fmt::format("Margin Limit Excess Liquidity : {}",
|
||||
ex_liq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
out_res.result = Limiter::LimiterErrorCode::NO_SUCH_ACCOUNT;
|
||||
out_res.error_msg = "Could not find account";
|
||||
}
|
||||
}
|
||||
};
|
||||
}}
|
||||
@@ -73,6 +73,20 @@ void Limiter::remove(const api::Security &security,
|
||||
else
|
||||
remove(security, AccountID(acct_ptr->id()), side, quantity, recurse);
|
||||
}
|
||||
void Limiter::set_day_pnl (AccountID account_id, double pnl){
|
||||
if (!me->limit_store->has_account(account_id))
|
||||
_add_account_and_reset(account_id);
|
||||
me->limit_store->set_pnl(account_id, pnl);
|
||||
}
|
||||
void Limiter::set_excess_liquidity (AccountID account_id,
|
||||
double excess_liquidity) {
|
||||
LOG(OrderMan, Info,
|
||||
"Limiter::set_excess_liquidity account:{} excess_liquidity: {}",
|
||||
account_id, excess_liquidity);
|
||||
if (!me->limit_store->has_account(account_id))
|
||||
_add_account_and_reset(account_id);
|
||||
me->limit_store->set_excess_liquidity(account_id, excess_liquidity);
|
||||
}
|
||||
Limiter::LimiterQueryResult Limiter::can_trade(UserID user_id,
|
||||
AccountID account_id) const {
|
||||
LimiterQueryResult rv = me->limit_store->can_trade(user_id, account_id);
|
||||
@@ -119,7 +133,7 @@ void Limiter::_add_account_and_reset(AccountID account_id) {
|
||||
}
|
||||
void Limiter::_reset(const bts::api::admin::AdminData &admin_data) {
|
||||
LimitStore *new_store = new LimitStore(admin_data, me->sec_map);
|
||||
new_store->copy_positions_from(*me->limit_store);
|
||||
new_store->copy_data_from(*me->limit_store);
|
||||
me->limit_store.reset(new_store);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ class Limiter {
|
||||
api::Side, Quantity, bool recurse);
|
||||
void remove(api::Security const &, std::string const & /* account_name */,
|
||||
api::Side, Quantity, bool recurse);
|
||||
|
||||
void set_day_pnl (AccountID account_id, double pnl);
|
||||
void set_excess_liquidity(AccountID account_id, double excesss_liquidity);
|
||||
|
||||
enum LimiterErrorCode {
|
||||
OK,
|
||||
NO_SUCH_SECURITY,
|
||||
@@ -45,7 +47,9 @@ class Limiter {
|
||||
ACCOUNT_DEACTIVATED,
|
||||
NO_SUCH_ACCOUNT,
|
||||
USER_DEACTIVATED,
|
||||
NO_SUCH_USER
|
||||
NO_SUCH_USER,
|
||||
LOSS_LIMIT_BREACHED,
|
||||
MARGIN_LIMIT_BREACHED
|
||||
};
|
||||
|
||||
struct LimiterQueryResult {
|
||||
|
||||
@@ -63,9 +63,11 @@ public:
|
||||
}
|
||||
// risk limit checks
|
||||
////////////////////
|
||||
Limiter::LimiterQueryResult can_send_new_order(api::Side side, Quantity quantity) const{
|
||||
Limiter::LimiterQueryResult can_send_new_order(api::Side side,
|
||||
Quantity quantity,
|
||||
bool enforce_clip = true) const{
|
||||
Limiter::LimiterQueryResult result;result.result= Limiter::OK;
|
||||
if (quantity.value() > _clip_size){
|
||||
if (enforce_clip && quantity.value() > _clip_size){
|
||||
result.result = Limiter::CLIP_SIZE_BREACHED;
|
||||
result.error_msg = fmt::format("Security Clip Size (Max {} Qty {})", _clip_size, quantity.value());
|
||||
return result;
|
||||
@@ -78,9 +80,10 @@ public:
|
||||
result = _product_node.can_send_new_order(side, quantity);
|
||||
return result;
|
||||
}
|
||||
Limiter::LimiterQueryResult can_update_order(api::Side side, Quantity from, Quantity to) const{
|
||||
Limiter::LimiterQueryResult can_update_order(api::Side side, Quantity from, Quantity to,
|
||||
bool enforce_clip = true) const{
|
||||
Limiter::LimiterQueryResult result;result.result=Limiter::OK;
|
||||
if (to.value() > _clip_size){
|
||||
if (enforce_clip && to.value() > _clip_size){
|
||||
result.result = Limiter::CLIP_SIZE_BREACHED;
|
||||
result.error_msg = fmt::format("Security Clip Size (Max {} Qty {})", _clip_size, to.value());
|
||||
return result;
|
||||
|
||||
@@ -50,17 +50,19 @@ public:
|
||||
////////////////////
|
||||
Limiter::LimiterQueryResult can_send_new_order(AccountID account_id,
|
||||
api::Side side,
|
||||
Quantity quantity) const{
|
||||
Quantity quantity,
|
||||
bool enforce_clip = true) const{
|
||||
const SecurityLimitNode* node_ptr = _find_account(account_id.value());
|
||||
bts_assert(node_ptr!=nullptr);
|
||||
return node_ptr->can_send_new_order(side, quantity);
|
||||
return node_ptr->can_send_new_order(side, quantity, enforce_clip);
|
||||
}
|
||||
Limiter::LimiterQueryResult can_update_order(AccountID account_id,
|
||||
api::Side side,
|
||||
Quantity from, Quantity to) const{
|
||||
Quantity from, Quantity to,
|
||||
bool enforce_clip = true) const{
|
||||
const SecurityLimitNode* node_ptr = _find_account(account_id.value());
|
||||
bts_assert(node_ptr!=nullptr);
|
||||
return node_ptr->can_update_order(side, from, to);
|
||||
return node_ptr->can_update_order(side, from, to, enforce_clip);
|
||||
}
|
||||
// accessors
|
||||
////////////
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/TopAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/IcebergAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/LeanAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/OffsetAlgo.h"
|
||||
#include "lib/orderman/orders/book_algos/OCOOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/OffsetOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/PricerOrder.h"
|
||||
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
|
||||
#include "lib/orderman/orders/CompositeOrder.h"
|
||||
@@ -183,7 +183,7 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
new orderman::AlgoWrapperOrder(std::make_unique<CoverAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::kOffset:
|
||||
order_ptr.reset(new orderman::OffsetOrder());
|
||||
order_ptr.reset(new orderman::AlgoWrapperOrder(std::make_unique<OffsetAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::kQueueHolder:
|
||||
order_ptr.reset(new orderman::QueueHolderOrder());
|
||||
|
||||
@@ -14,6 +14,7 @@ bool can_consume_attachment(api::orderman::CustomOrderDefinition::CustomCase ord
|
||||
case CustomOrderDefinition::kTop:
|
||||
case CustomOrderDefinition::kCover:
|
||||
case CustomOrderDefinition::kLean:
|
||||
case CustomOrderDefinition::kOffset:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -44,6 +45,10 @@ bool should_forward_attachment(
|
||||
return
|
||||
cdef.lean().has_child() &&
|
||||
can_consume_attachment(cdef.lean().child().custom_case());
|
||||
case CustomOrderDefinition::kOffset:
|
||||
return
|
||||
cdef.offset().has_child() &&
|
||||
can_consume_attachment(cdef.offset().child().custom_case());
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/OffsetAlgo.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
using namespace std;
|
||||
|
||||
enum class OffsetAlgo::Status { WAITING_FOR_MD, DEPLOYED };
|
||||
|
||||
struct OffsetAlgo::PImpl {
|
||||
IAlgoServices *algo;
|
||||
Status status = Status::WAITING_FOR_MD;
|
||||
OrderKey child_key = null_order_key();
|
||||
ChildTemplate *child_template = nullptr;
|
||||
Book last_book;
|
||||
uint32_t tick_offset_on_deploy;
|
||||
double child_price = os::nan();
|
||||
};
|
||||
|
||||
OffsetAlgo::OffsetAlgo() : me(new PImpl()) {}
|
||||
OffsetAlgo::~OffsetAlgo() {}
|
||||
IAlgoServices &OffsetAlgo::algo() const { return *me->algo; }
|
||||
|
||||
OrderOpResult OffsetAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
|
||||
auto &def = serv.definition();
|
||||
bts_assert(def.has_custom_def());
|
||||
bts_assert(def.custom_def().has_offset());
|
||||
// Validate the offset part of the definition
|
||||
auto &offset = def.custom_def().offset();
|
||||
if (!offset.has_child() &&
|
||||
OrderKey(def.attach_target_order_key()) == null_order_key()) {
|
||||
return OrderOpResult{
|
||||
ORDER_ERROR_INVALID_ORDER_DEFINITON,
|
||||
"Offset order must have either a child order definition or an "
|
||||
"order to attach to"};
|
||||
}
|
||||
me->tick_offset_on_deploy = offset.tick_offset_on_deploy();
|
||||
if (!serv.is_attaching()) {
|
||||
// if we are not attaching create a child template so we can
|
||||
// quickly launch our child when the time comes.
|
||||
auto child_def = def;
|
||||
*child_def.mutable_custom_def() = def.custom_def().offset().child();
|
||||
me->child_template = &(serv.make_child_template(child_def));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void OffsetAlgo::do_launch(IAlgoServices &algo) {
|
||||
me->algo = &algo;
|
||||
bts_assert(me->child_template);
|
||||
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||
}
|
||||
|
||||
void OffsetAlgo::do_launch_attached(IAlgoServices &algo, OrderKey k,
|
||||
ChildState st) {
|
||||
me->algo = &algo;
|
||||
me->child_key = k;
|
||||
me->child_price = st.price.value();
|
||||
algo.subscribe_book(SecurityKey(algo.security().id()));
|
||||
}
|
||||
|
||||
void OffsetAlgo::do_update(api::orderman::Update const &update) {
|
||||
LOG(OrderMan, Diag, "OffsetAlgo order {} receiving update: {}",
|
||||
algo().order_key().value(), utils::pb::to_string(update));
|
||||
if (update.has_custom()) {
|
||||
if (!update.custom().has_offset()) {
|
||||
algo().terminate_error("Invalid update");
|
||||
return;
|
||||
}
|
||||
me->tick_offset_on_deploy =
|
||||
update.custom().offset().tick_offset_on_deploy();
|
||||
}
|
||||
switch (me->status) {
|
||||
case Status::WAITING_FOR_MD:
|
||||
if (update.has_custom()) {
|
||||
if (algo().is_attaching()) {
|
||||
// don't change the child's price
|
||||
// (need md to determine initial deploy price)
|
||||
bts_assert(!os::is_nan(me->child_price));
|
||||
algo().update_child(me->child_key, mk_child_update(update, me->child_price));
|
||||
} else {
|
||||
auto &def = algo().definition();
|
||||
auto child_def = def;
|
||||
*child_def.mutable_custom_def() = update.custom().offset().child();
|
||||
me->child_template = &(algo().make_child_template(child_def));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Status::DEPLOYED:
|
||||
algo().update_child(me->child_key, mk_child_update(update));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OffsetAlgo::on_book(SecurityKey, Book const &b) {
|
||||
me->last_book = b;
|
||||
// log top of book
|
||||
LOG(OrderMan, Diag, "OffsetAlgo order {} on book. BID: {}@{} ASK: {}@{}",
|
||||
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
|
||||
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
|
||||
b.side(api::SIDE_SELL)[0].price());
|
||||
// attempt deploy
|
||||
if (me->status == Status::WAITING_FOR_MD)
|
||||
try_deploy();
|
||||
}
|
||||
|
||||
Update OffsetAlgo::mk_child_update(const Update &source, const double &price_override) {
|
||||
Update target;
|
||||
if (source.has_custom()) {
|
||||
bts_assert(source.custom().has_offset());
|
||||
*target.mutable_custom() = source.custom().offset().child();
|
||||
}
|
||||
if (!os::is_nan(price_override))
|
||||
target.set_price(price_override);
|
||||
else
|
||||
target.set_price(source.price());
|
||||
target.set_quantity(source.quantity());
|
||||
return target;
|
||||
}
|
||||
|
||||
void OffsetAlgo::try_deploy() {
|
||||
bts_assert(me->status == Status::WAITING_FOR_MD);
|
||||
auto side = algo().definition().side();
|
||||
auto &top_psz = me->last_book.side(side)[0];
|
||||
if (!top_psz.size()) {
|
||||
// log state (still waiting for md)
|
||||
LOG(OrderMan, Diag,
|
||||
"OffsetAlgo order {} still waiting for md. Order: {} {}@ {} ticks "
|
||||
"away.",
|
||||
algo().order_key().value(), algo().definition().side(),
|
||||
algo().quantity().value(), me->tick_offset_on_deploy);
|
||||
|
||||
} else {
|
||||
const double &offset = securities::tick_size(algo().security()).value() *
|
||||
me->tick_offset_on_deploy;
|
||||
me->child_price =
|
||||
securities::worsen_price(side, Price(top_psz.price()), Price(offset)).value();
|
||||
if (algo().is_attaching()) {
|
||||
// log imminent update
|
||||
LOG(OrderMan, Diag,
|
||||
"OffsetAlgo order {} attaching child {}. Order: {} {}@ {} ({} ticks "
|
||||
"away).",
|
||||
algo().order_key().value(),
|
||||
me->child_key.value(), side,
|
||||
algo().quantity().value(),
|
||||
me->child_price, me->tick_offset_on_deploy);
|
||||
// update
|
||||
Update child_update;
|
||||
child_update.set_quantity(algo().quantity().value());
|
||||
child_update.set_price(me->child_price);
|
||||
algo().update_child(me->child_key, child_update);
|
||||
} else {
|
||||
// log imminent deploy
|
||||
LOG(OrderMan, Diag,
|
||||
"OffsetAlgo order {} deploying child. Order: {} {}@ {} ({} ticks "
|
||||
"away).",
|
||||
algo().order_key().value(), side,
|
||||
algo().quantity().value(),
|
||||
me->child_price, me->tick_offset_on_deploy);
|
||||
// deploy
|
||||
me->child_key =
|
||||
algo().launch_child(*me->child_template, Price(me->child_price), algo().quantity());
|
||||
}
|
||||
algo().set_ladder_visibility(false);
|
||||
me->status = Status::DEPLOYED;
|
||||
}
|
||||
}
|
||||
|
||||
void OffsetAlgo::on_child_change(OrderKey, ChildState const &st) {
|
||||
me->child_price = st.price.value();
|
||||
algo().update_fill_state(st.total_fills, st.last_price);
|
||||
switch (st.status) {
|
||||
case ChildStatus::WORKING:
|
||||
return;
|
||||
case ChildStatus::COMPLETED:
|
||||
algo().complete();
|
||||
return;
|
||||
case ChildStatus::CANCELED:
|
||||
algo().cancel("Unexpected child cancel");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
|
||||
using Update = bts::api::orderman::Update;
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
// Offset sends a child-order/algo N ticks away from the top of the book.
|
||||
// If necessary, it will wait for the market to arrive before shooting the
|
||||
// child.
|
||||
|
||||
class OffsetAlgo : public IAlgoOrder {
|
||||
public:
|
||||
OffsetAlgo();
|
||||
~OffsetAlgo();
|
||||
|
||||
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
|
||||
void do_launch(IAlgoServices &) override;
|
||||
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
|
||||
void do_update(Update const &) override;
|
||||
void on_book(SecurityKey, Book const &) override;
|
||||
void on_child_change(OrderKey, ChildState const &) override;
|
||||
|
||||
private:
|
||||
enum class Status;
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
|
||||
static Update mk_child_update(const Update& source_update, const double& price_override = os::nan());
|
||||
|
||||
IAlgoServices &algo() const;
|
||||
void try_deploy();
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -16,6 +16,7 @@ using namespace api::orderman;
|
||||
struct TopAlgo::PImpl {
|
||||
IAlgoServices *algo;
|
||||
spark::QuantityMeasure top_threshold_measure;
|
||||
uint32_t ticks_off = 0;
|
||||
OrderKey child_key = null_order_key();
|
||||
ChildTemplate *child_template = nullptr;
|
||||
Book last_book;
|
||||
@@ -41,7 +42,7 @@ OrderOpResult TopAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
|
||||
}
|
||||
|
||||
me->top_threshold_measure = top.top_threshold();
|
||||
|
||||
me->ticks_off = top.ticks_off();
|
||||
if (!serv.is_attaching()) {
|
||||
// if we are not attaching create a child template so we can
|
||||
// quickly launch our child when the time comes.
|
||||
@@ -87,13 +88,14 @@ void TopAlgo::do_update(api::orderman::Update const &update) {
|
||||
auto &top_update = update.custom().top();
|
||||
*child_update.mutable_custom() = top_update.child();
|
||||
me->top_threshold_measure = top_update.top_threshold();
|
||||
me->ticks_off = top_update.ticks_off();
|
||||
}
|
||||
algo().update_child(me->child_key, child_update);
|
||||
}
|
||||
|
||||
void TopAlgo::on_book(SecurityKey, Book const &b) {
|
||||
me->last_book = b;
|
||||
ensure_min_quantity();
|
||||
ensure_order_meets_constraints();
|
||||
}
|
||||
|
||||
void TopAlgo::on_child_change(OrderKey, ChildState const &st) {
|
||||
@@ -111,14 +113,14 @@ void TopAlgo::on_child_change(OrderKey, ChildState const &st) {
|
||||
}
|
||||
}
|
||||
|
||||
bool TopAlgo::ensure_min_quantity() {
|
||||
bool TopAlgo::ensure_order_meets_constraints() {
|
||||
LOG(OrderMan, Diag,
|
||||
"Top order {} received book update. Current top of book: {}@{}",
|
||||
algo().order_key().value(), top_of_book_quantity().value(),
|
||||
top_of_book_price().value());
|
||||
|
||||
if (better_than_top()) {
|
||||
LOG(OrderMan, Diag,
|
||||
LOG(OrderMan, Info,
|
||||
"Top order {} became better than the top of the book, canceling",
|
||||
algo().order_key().value());
|
||||
algo().cancel("Order would become BBO");
|
||||
@@ -135,7 +137,7 @@ bool TopAlgo::ensure_min_quantity() {
|
||||
algo().order_key().value(), total_min_quantity,
|
||||
top_of_book_quantity().value());
|
||||
if (top_of_book_quantity() < Quantity(total_min_quantity)) {
|
||||
LOG(OrderMan, Diag,
|
||||
LOG(OrderMan, Info,
|
||||
"Top order {} is canceling because min quantiy is not met",
|
||||
algo().order_key().value());
|
||||
algo().cancel("Top of book is below threshold");
|
||||
@@ -147,6 +149,26 @@ bool TopAlgo::ensure_min_quantity() {
|
||||
algo().order_key().value(), algo().price().value(),
|
||||
top_of_book_price().value());
|
||||
|
||||
if (me->ticks_off > 0) {
|
||||
auto tick_diff = securities::price_diff_to_ticks(
|
||||
algo().price().value(), top_of_book_price().value(),
|
||||
securities::tick_size(algo().security()).value());
|
||||
if (std::abs(tick_diff) <= (int)me->ticks_off) {
|
||||
LOG(OrderMan, Info,
|
||||
"Top order {} price {} is less than {} ticks away from top ({}). "
|
||||
"Canceling",
|
||||
algo().order_key().value(), algo().price().value(),
|
||||
me->ticks_off + 1, top_of_book_price().value());
|
||||
algo().cancel("Order is too close to top of the book");
|
||||
return false;
|
||||
} else
|
||||
LOG(OrderMan, Diag,
|
||||
"Top order {} price {} is {} ticks away from top. Top: {} Ticks "
|
||||
"off: {}",
|
||||
algo().order_key().value(), algo().price().value(),
|
||||
std::abs(tick_diff), top_of_book_price().value(), me->ticks_off);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class TopAlgo : public IAlgoOrder {
|
||||
bool at_top_of_book() const;
|
||||
bool better_than_top() const;
|
||||
Quantity min_quantity_at_top() const;
|
||||
bool ensure_min_quantity();
|
||||
bool ensure_order_meets_constraints();
|
||||
bool has_running_child() const;
|
||||
Price top_of_book_price() const;
|
||||
Quantity top_of_book_quantity() const;
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
#include "OffsetOrder.h"
|
||||
|
||||
#include "lib/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "lib/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 OffsetOrder::PImpl
|
||||
{
|
||||
PImpl() {}
|
||||
|
||||
uint32_t tick_offset_on_deploy;
|
||||
};
|
||||
|
||||
|
||||
OffsetOrder::OffsetOrder() : me(new PImpl()) {}
|
||||
|
||||
OffsetOrder::~OffsetOrder() {}
|
||||
|
||||
void OffsetOrder::setup_child(const api::orderman::OrderDefinition& this_def, const PreActivationOrderServices&,
|
||||
api::orderman::OrderState& initial_state, api::orderman::OrderDefinition* out_child)
|
||||
{
|
||||
initial_state.mutable_offset();
|
||||
auto const &offset_def = this_def.custom_def().offset();
|
||||
*out_child->mutable_custom_def() = offset_def.child();
|
||||
|
||||
me->tick_offset_on_deploy = offset_def.tick_offset_on_deploy();
|
||||
}
|
||||
|
||||
|
||||
void OffsetOrder::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)
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_BUY)[0];
|
||||
double price = same.price() - tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& same = book.book.side(api::Side::SIDE_SELL)[0];
|
||||
double price = same.price() + tick_size() * me->tick_offset_on_deploy;
|
||||
activate_child(Price(price), Quantity(this_state().quantity()));
|
||||
transition(ORDER_STATUS_WORKING);
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
break;
|
||||
case ORDER_STATUS_WORKING:
|
||||
{
|
||||
detach();
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
break;
|
||||
|
||||
// child cannot be in any of these states if we are in the working state
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_COMPLETED:
|
||||
case ORDER_STATUS_CANCELED:
|
||||
case ORDER_STATUS_ERROR:
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_INACTIVE:
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
case ORDER_STATUS_DISCONNECTED:
|
||||
case OrderStatus_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
case OrderStatus_INT_MAX_SENTINEL_DO_NOT_USE_:
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/book_algos/BookAlgo.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
|
||||
//OffsetOrder deploys N ticks away from the initial same-side inside market it's mainly for composition / fat finger
|
||||
|
||||
class OffsetOrder : public BookAlgo
|
||||
{
|
||||
public:
|
||||
OffsetOrder();
|
||||
virtual ~OffsetOrder();
|
||||
|
||||
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:
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "OrderServiceServer.h"
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
#include "lib/api/services/AdminService.client.pb.h"
|
||||
#include "lib/api/services/MarginService.client.pb.h"
|
||||
#include "lib/api/services/PositionService.client.pb.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/jetfix.pb.h"
|
||||
#include "lib/config/protos/orderman_cfg.pb.h"
|
||||
#include "lib/md/MDConsumer.h"
|
||||
#include "lib/md/MDClient.h"
|
||||
#include "lib/md/MDConsumer.h"
|
||||
#include "lib/mercury/actors/Timer.h"
|
||||
#include "lib/mercury/executor/Executor.h"
|
||||
#include "lib/metrics/metrics.h"
|
||||
@@ -45,14 +47,14 @@ struct ActorThread {
|
||||
stats.publish(loop_metric);
|
||||
}
|
||||
|
||||
void start(trace::PerfLog& log) {
|
||||
void start(trace::PerfLog &log) {
|
||||
if (cfg.poll())
|
||||
thread = threading::make_thread(cfg.thread_name(), [this,&log]() {
|
||||
thread = threading::make_thread(cfg.thread_name(), [this, &log]() {
|
||||
for (;;)
|
||||
executor.do_work(log);
|
||||
});
|
||||
else {
|
||||
thread = threading::make_thread(cfg.thread_name(), [this,&log]() {
|
||||
thread = threading::make_thread(cfg.thread_name(), [this, &log]() {
|
||||
const auto inner_iter = 1000;
|
||||
while (!done) {
|
||||
auto start = Timer::now();
|
||||
@@ -90,14 +92,16 @@ ActorThread *find_thread_for_actor(
|
||||
}
|
||||
|
||||
struct OrderServiceServer::PImpl {
|
||||
PImpl(bts::api::ServiceContext &ctx,
|
||||
md::MDClient& mdc,
|
||||
PImpl(bts::api::ServiceContext &ctx, md::MDClient &mdc,
|
||||
DynamicOrderRegistry const &d, metrics::MetricsContainer &stats,
|
||||
trace::PerfLog &log)
|
||||
: service_bridge(), service_ready(false), known_orders(), known_fills(),
|
||||
publish_orders(), publish_fills(),
|
||||
admin_service(ctx.locate<api::admin::AdminService>()), md_consumer(mdc.current_consumer()),
|
||||
stats(stats), dyn_order_registry(d), log(log), in_flight_order_ops(),
|
||||
admin_service(ctx.locate<api::admin::AdminService>()),
|
||||
position_service(ctx.locate<api::position::PositionService>()),
|
||||
margin_service(ctx.locate<api::position::MarginService>()),
|
||||
md_consumer(mdc.current_consumer()), stats(stats),
|
||||
dyn_order_registry(d), log(log), in_flight_order_ops(),
|
||||
current_req_op_id(0) {}
|
||||
|
||||
ServiceBridge *service_bridge;
|
||||
@@ -111,7 +115,9 @@ struct OrderServiceServer::PImpl {
|
||||
OrderServiceServer::OrdersPublisher publish_orders;
|
||||
OrderServiceServer::FillsPublisher publish_fills;
|
||||
api::admin::AdminService admin_service;
|
||||
bts::md::MDConsumer& md_consumer;
|
||||
api::position::PositionService position_service;
|
||||
api::position::MarginService margin_service;
|
||||
bts::md::MDConsumer &md_consumer;
|
||||
metrics::MetricsContainer &stats;
|
||||
DynamicOrderRegistry const &dyn_order_registry;
|
||||
trace::PerfLog &log;
|
||||
@@ -120,12 +126,13 @@ struct OrderServiceServer::PImpl {
|
||||
uint32_t current_req_op_id;
|
||||
};
|
||||
|
||||
OrderServiceServer::OrderServiceServer(
|
||||
bts::api::ServiceContext &ctx,
|
||||
md::MDClient& mdc,
|
||||
DynamicOrderRegistry const &d, metrics::MetricsContainer &stats,
|
||||
trace::PerfLog &log)
|
||||
: api::orderman::OrderServiceBase(ctx), me(new PImpl(ctx, mdc, d, stats,log)) {
|
||||
OrderServiceServer::OrderServiceServer(bts::api::ServiceContext &ctx,
|
||||
md::MDClient &mdc,
|
||||
DynamicOrderRegistry const &d,
|
||||
metrics::MetricsContainer &stats,
|
||||
trace::PerfLog &log)
|
||||
: api::orderman::OrderServiceBase(ctx),
|
||||
me(new PImpl(ctx, mdc, d, stats, log)) {
|
||||
me->publish_orders = make_orders_publisher();
|
||||
me->publish_fills = make_fills_publisher();
|
||||
|
||||
@@ -152,7 +159,7 @@ OrderServiceServer::OrderServiceServer(
|
||||
bts_assert_msg(thread,
|
||||
"Could not find a configured thread for OrderManager. See "
|
||||
"the OrdermanCfg key");
|
||||
thread->executor.spawn<OrderManager>(d,me->log, mdc.oracle());
|
||||
thread->executor.spawn<OrderManager>(d, me->log, mdc.oracle());
|
||||
|
||||
// Timer
|
||||
thread = find_thread_for_actor("Timer", me->actor_threads);
|
||||
@@ -212,12 +219,10 @@ OrderServiceServer::OrderServiceServer(
|
||||
},
|
||||
[this, &ctx](auto const &order_op_result) {
|
||||
ctx.post([this, order_op_result] {
|
||||
this->on_order_op_result(order_op_result);
|
||||
this->on_order_op_result(order_op_result);
|
||||
});
|
||||
},
|
||||
me->dyn_order_registry,
|
||||
thread->executor.make_proxy(),mdc.oracle());
|
||||
|
||||
me->dyn_order_registry, thread->executor.make_proxy(), mdc.oracle());
|
||||
|
||||
thread->executor.activate(me->service_bridge);
|
||||
|
||||
@@ -235,6 +240,19 @@ OrderServiceServer::OrderServiceServer(
|
||||
bts_assert(update.all().size() == 1);
|
||||
me->service_bridge->update_admin_data(update.all()[0]);
|
||||
});
|
||||
|
||||
// subscribe to position updates and foward them to the service bridge
|
||||
me->position_service.subscribe_pnl_account_data() |
|
||||
rxcpp::operators::subscribe<api::position::Pnl_account_dataUpdate>(
|
||||
[this](auto const &update) {
|
||||
me->service_bridge->update_account_pnl(update);
|
||||
});
|
||||
|
||||
me->margin_service.subscribe_excess_liquidity_report() |
|
||||
rxcpp::operators::subscribe<api::position::Excess_liquidity_reportUpdate>(
|
||||
[this](auto const &update) {
|
||||
me->service_bridge->update_excess_liquidity_report(update);
|
||||
});
|
||||
}
|
||||
|
||||
OrderServiceServer::~OrderServiceServer() {
|
||||
@@ -358,7 +376,7 @@ void OrderServiceServer::get_registered_dynamic_order_types(
|
||||
void OrderServiceServer::on_order_op_result(
|
||||
OrderOpResultEvent const &op_result) {
|
||||
auto it = me->in_flight_order_ops.find(op_result.req_id);
|
||||
bts_assert(it!=me->in_flight_order_ops.end());
|
||||
bts_assert(it != me->in_flight_order_ops.end());
|
||||
auto &responder = it->second;
|
||||
api::orderman::Response r;
|
||||
r.set_order_key(op_result.ok.value());
|
||||
|
||||
@@ -86,7 +86,7 @@ bool AuthSessionManager::add_or_replace_session(const api::user::TokenPayload& t
|
||||
return true;
|
||||
if(datetime::ll_diff_in_us(datetime::ll_now(), _expiration) < 0)
|
||||
{
|
||||
LOG(Gen,Error, "Your BTS Keycode is expired!");
|
||||
LOG(User,Error, "Your BTS Keycode is expired!");
|
||||
if(msg)
|
||||
*msg = "Your BTS Keycode is EXPIRED! Please contact sales@bluetradesys.com";
|
||||
return false;
|
||||
@@ -105,13 +105,18 @@ bool AuthSessionManager::add_or_replace_session(const api::user::TokenPayload& t
|
||||
if(it != _map.end())
|
||||
{
|
||||
_map[tok.id()] = Session{tok};
|
||||
LOG(User, Warn, "KEYCODE: Replacing session {} ", tok.id());
|
||||
return true;
|
||||
}
|
||||
else if(_map.size() < _nusers)
|
||||
{
|
||||
_map[tok.id()] = Session{tok};
|
||||
LOG(User, Warn, "KEYCODE: Creating session {} ", tok.id());
|
||||
return true;
|
||||
}
|
||||
if(msg)
|
||||
*msg = "KEYCODES: Not Enough Logins available for session";
|
||||
LOG(User, Error, "KEYCODES: Not Enough Logins available for session {}", tok.id());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,35 @@ AuthSessionManager& session_manager()
|
||||
return mgr;
|
||||
}
|
||||
|
||||
bool requires_auth_session(const admin::BTSUser& user)
|
||||
{
|
||||
//if the user can trade in any account or see market data, it needs a session
|
||||
if(user.has_user_data() &&
|
||||
user.user_data().has_account_permissions() &&
|
||||
user.user_data().account_permissions().account_permissions_size())
|
||||
{
|
||||
for(const auto& i: user.user_data().account_permissions().account_permissions())
|
||||
{
|
||||
if(i.second.has_trading_permission() && i.second.trading_permission().has_permissions())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(user.has_user_data() &&
|
||||
user.user_data().has_exchange_user_data())
|
||||
{
|
||||
if(user.user_data().exchange_user_data().has_cme_data())
|
||||
{
|
||||
for(const auto& i: user.user_data().exchange_user_data().cme_data().md_permissions())
|
||||
{
|
||||
if(i.enabled())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AuthToken generate_token(const admin::BTSUser& user, std::string* msg)
|
||||
{
|
||||
api::user::TokenPayload payload;
|
||||
@@ -58,7 +87,9 @@ AuthToken generate_token(const admin::BTSUser& user, std::string* msg)
|
||||
payload.set_id(user.id());
|
||||
if(user.name() == "bts")
|
||||
payload.set_wheel(true);
|
||||
session_manager().add_or_replace_session(payload, msg);
|
||||
|
||||
if(requires_auth_session(user))
|
||||
session_manager().add_or_replace_session(payload, msg);
|
||||
auto token = encrypt(payload.SerializeAsString());
|
||||
bts_assert_d(token.size() < 255-8);
|
||||
AuthToken result;
|
||||
|
||||
@@ -214,6 +214,20 @@ void Dispatcher::post(Action&& action, int64_t delay_ms)
|
||||
}
|
||||
}
|
||||
|
||||
void Dispatcher::post_copy(Action action, int64_t delay_ms)
|
||||
{
|
||||
if (delay_ms > 0)
|
||||
{
|
||||
shared_ptr<deadline_timer> timer = make_shared<deadline_timer>(_me->service);
|
||||
timer->expires_from_now(boost::posix_time::milliseconds(delay_ms));
|
||||
timer->async_wait([timer, action] (const boost::system::error_code& err) { if (!err) action(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
_me->service.post(action);
|
||||
}
|
||||
}
|
||||
|
||||
io_service* Dispatcher::io_service() { return &_me->service; }
|
||||
|
||||
//
|
||||
|
||||
@@ -88,6 +88,7 @@ public:
|
||||
void invoke(Action action);
|
||||
// Queue an action on the dispatcher thread and return immediately.
|
||||
void post(Action&& action, int64_t delay_ms=0);
|
||||
void post_copy(Action action, int64_t delay_ms=0);
|
||||
|
||||
boost::asio::io_service* io_service();
|
||||
|
||||
|
||||
@@ -5,19 +5,34 @@ namespace bts {
|
||||
namespace test {
|
||||
|
||||
TEST_CASE("AuthToken", "[security]") {
|
||||
api::admin::BTSUser user;
|
||||
user.set_id(42);
|
||||
user.set_name("user1");
|
||||
api::admin::BTSUser admin_user;
|
||||
admin_user.set_id(42);
|
||||
admin_user.set_name("user1");
|
||||
|
||||
api::admin::BTSUser trader_user;
|
||||
trader_user.set_id(43);
|
||||
trader_user.set_name("trader1");
|
||||
trader_user.mutable_user_data()->mutable_exchange_user_data()->mutable_cme_data()->add_md_permissions()->set_enabled(true);
|
||||
|
||||
SECTION("round trip") {
|
||||
auto token = api::auth::generate_token(user);
|
||||
SECTION("round trip admin") {
|
||||
auto token = api::auth::generate_token(admin_user);
|
||||
// We use 1 byte for api header size
|
||||
REQUIRE(token.ByteSize() <= 240);
|
||||
api::user::TokenPayload payload;
|
||||
REQUIRE(!api::auth::decode_token(token, &payload));
|
||||
REQUIRE(payload.id() == admin_user.id());
|
||||
REQUIRE(payload.wheel() == false);
|
||||
}
|
||||
|
||||
SECTION("round trip trader") {
|
||||
auto token = api::auth::generate_token(trader_user);
|
||||
// We use 1 byte for api header size
|
||||
REQUIRE(token.ByteSize() <= 240);
|
||||
api::user::TokenPayload payload;
|
||||
REQUIRE(api::auth::decode_token(token, &payload));
|
||||
REQUIRE(payload.id() == user.id());
|
||||
REQUIRE(payload.id() == trader_user.id());
|
||||
REQUIRE(payload.wheel() == false);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("empty token") {
|
||||
api::user::TokenPayload payload;
|
||||
@@ -28,11 +43,11 @@ TEST_CASE("AuthToken", "[security]") {
|
||||
}
|
||||
|
||||
SECTION("bts is the big wheel") {
|
||||
user.set_name("bts");
|
||||
auto token = api::auth::generate_token(user);
|
||||
admin_user.set_name("bts");
|
||||
auto token = api::auth::generate_token(admin_user);
|
||||
api::user::TokenPayload payload;
|
||||
REQUIRE(api::auth::decode_token(token, &payload));
|
||||
REQUIRE(payload.id() == user.id());
|
||||
REQUIRE(payload.id() == admin_user.id());
|
||||
REQUIRE(payload.wheel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
set(sources algo/AlgoDefinitionServiceServer.cpp;cmds/AdminReport.cpp;cmds/AutoCert.cpp;cmds/Benchmark.cpp;cmds/CmdRegistry.cpp;cmds/ConfigCheck.cpp;cmds/Help.cpp;cmds/KeyCode.cpp;cmds/Md.cpp;cmds/PrintHedgeOrders.cpp;cmds/QuickTest.cpp;cmds/Run.cpp;cmds/RunSim.cpp;cmds/Span.cpp;cmds/Test.cpp;cmds/TestCeloxica.cpp;cmds/Web.cpp;dspark/mirage/quick_queue/quick_queue.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;main.cpp;md/ChannelVolume.cpp;md/MarketDataAuxiliaryServiceServer.cpp;md/VolumeAtPriceState.cpp;md/platforms/IMDPlatform.cpp;md/platforms/celoxica/CXListener.cpp;md/platforms/celoxica/CXMain.cpp;md/platforms/celoxica/tests/cx_tests.cpp;md/platforms/ice_onixs/ICEListener.cpp;md/platforms/ice_onixs/ICEMain.cpp;md/platforms/ice_onixs/tests/ice_tests.cpp;md/platforms/mdp3_onixs/MDP3Listener.cpp;md/platforms/mdp3_onixs/MDP3Main.cpp;md/platforms/mdp3_onixs/tests/mdp3_tests.cpp;md/run_md.cpp;position/IMarginObserver.cpp;position/IPositionObserver.cpp;position/MarginServiceCore.cpp;position/PositionServiceCore.cpp;position/PositionServiceServer.cpp;position/tests/PositionTests.cpp;support/ErrorReportService.cpp;support/ShellLatencyServer.cpp;user/AdminServiceServer.cpp;user/AuthServiceServer.cpp;user/tests/admin_service_tests.cpp)
|
||||
set(sources algo/AlgoDefinitionServiceServer.cpp;cmds/AdminReport.cpp;cmds/AutoCert.cpp;cmds/Benchmark.cpp;cmds/CmdRegistry.cpp;cmds/ConfigCheck.cpp;cmds/Help.cpp;cmds/KeyCode.cpp;cmds/Md.cpp;cmds/PrintHedgeOrders.cpp;cmds/QuickTest.cpp;cmds/Run.cpp;cmds/RunSim.cpp;cmds/Span.cpp;cmds/Test.cpp;cmds/TestCeloxica.cpp;cmds/Web.cpp;dspark/mirage/quick_queue/quick_queue.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;main.cpp;md/ChannelVolume.cpp;md/MarketDataAuxiliaryServiceServer.cpp;md/VolumeAtPriceState.cpp;md/platforms/IMDPlatform.cpp;md/platforms/celoxica/CXListener.cpp;md/platforms/celoxica/CXMain.cpp;md/platforms/celoxica/tests/cx_tests.cpp;md/platforms/ice_onixs/ICEListener.cpp;md/platforms/ice_onixs/ICEMain.cpp;md/platforms/ice_onixs/tests/ice_tests.cpp;md/platforms/mdp3_onixs/MDP3Listener.cpp;md/platforms/mdp3_onixs/MDP3Main.cpp;md/platforms/mdp3_onixs/tests/mdp3_tests.cpp;md/run_md.cpp;position/IPositionObserver.cpp;position/MarginServiceCore.cpp;position/MarginServiceServer.cpp;position/PositionAggregator.cpp;position/PositionServicePNLHelper.cpp;position/PositionServiceServer.cpp;position/tests/PositionTests.cpp;support/ErrorReportService.cpp;support/ShellLatencyServer.cpp;user/AdminServiceServer.cpp;user/AuthServiceServer.cpp;user/tests/admin_service_tests.cpp)
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "server/sparkd/md/MarketDataAuxiliaryServiceServer.h"
|
||||
#include "server/sparkd/md/run_md.h"
|
||||
#include "server/sparkd/position/PositionServiceServer.h"
|
||||
#include "server/sparkd/position/MarginServiceServer.h"
|
||||
#include "server/sparkd/support/ErrorReportService.h"
|
||||
#include "server/sparkd/support/ShellLatencyServer.h"
|
||||
#include "server/sparkd/user/AdminServiceServer.h"
|
||||
@@ -84,11 +85,10 @@ int Run::execute(std::vector<std::string> const &) {
|
||||
md::auxiliary::MarketDataAuxiliaryServiceServer md_aux_server(sc);
|
||||
//orderman::AlgoServiceServer algo_server(sc);
|
||||
bts::api::orderman::algo::AlgoDefinitionServiceServer algo_definition_server(sc);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
position::PositionServiceServer position_service(sc);
|
||||
position::PositionServiceServer position_service(sc,mdclient);
|
||||
position::MarginServiceServer margin_service(sc, mdclient);
|
||||
metrics::ShellLatencyServer shell_latency_service(
|
||||
sc, stats, utils::Dispatcher::background());
|
||||
|
||||
|
||||
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user